gstmemory.c 23.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/* GStreamer
 * Copyright (C) 2011 Wim Taymans <wim.taymans@gmail.be>
 *
 * gstmemory.c: memory block handling
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

Wim Taymans's avatar
Wim Taymans committed
22 23 24 25 26 27 28 29
/**
 * SECTION:gstmemory
 * @short_description: refcounted wrapper for memory blocks
 * @see_also: #GstBuffer
 *
 * GstMemory is a lightweight refcounted object that wraps a region of memory.
 * They are typically used to manage the data of a #GstBuffer.
 *
Wim Taymans's avatar
Wim Taymans committed
30 31 32 33 34
 * A GstMemory object has an allocated region of memory of maxsize. The maximum
 * size does not change during the lifetime of the memory object. The memory
 * also has an offset and size property that specifies the valid range of memory
 * in the allocated region.
 *
35
 * Memory is usually created by allocators with a gst_allocator_alloc()
36 37 38
 * method call. When NULL is used as the allocator, the default allocator will
 * be used.
 *
39
 * New allocators can be registered with gst_allocator_register().
40
 * Allocators are identified by name and can be retrieved with
41
 * gst_allocator_find().
42
 *
Wim Taymans's avatar
Wim Taymans committed
43
 * New memory can be created with gst_memory_new_wrapped() that wraps the memory
44
 * allocated elsewhere.
Wim Taymans's avatar
Wim Taymans committed
45 46 47 48 49 50 51 52
 *
 * Refcounting of the memory block is performed with gst_memory_ref() and
 * gst_memory_unref().
 *
 * The size of the memory can be retrieved and changed with
 * gst_memory_get_sizes() and gst_memory_resize() respectively.
 *
 * Getting access to the data of the memory is performed with gst_memory_map().
Wim Taymans's avatar
Wim Taymans committed
53
 * The call will return a pointer to offset bytes into the region of memory.
Wim Taymans's avatar
Wim Taymans committed
54 55 56 57 58 59 60
 * After the memory access is completed, gst_memory_unmap() should be called.
 *
 * Memory can be copied with gst_memory_copy(), which will returnn a writable
 * copy. gst_memory_share() will create a new memory block that shares the
 * memory with an existing memory block at a custom offset and with a custom
 * size.
 *
Wim Taymans's avatar
Wim Taymans committed
61
 * Memory can be efficiently merged when gst_memory_is_span() returns TRUE.
Wim Taymans's avatar
Wim Taymans committed
62
 *
63
 * Last reviewed on 2011-06-08 (0.11.0)
Wim Taymans's avatar
Wim Taymans committed
64
 */
65

66
#ifdef HAVE_CONFIG_H
Wim Taymans's avatar
Wim Taymans committed
67
#include "config.h"
68 69
#endif

70 71 72
#include "gst_private.h"
#include "gstmemory.h"

73 74
G_DEFINE_BOXED_TYPE (GstMemory, gst_memory, (GBoxedCopyFunc) gst_memory_ref,
    (GBoxedFreeFunc) gst_memory_unref);
Wim Taymans's avatar
Wim Taymans committed
75

Wim Taymans's avatar
Wim Taymans committed
76 77 78 79 80
/**
 * gst_memory_alignment:
 *
 * The default memory alignment in bytes - 1
 * an alignment of 7 would be the same as what malloc() guarantees.
81 82
 */
#if defined(MEMORY_ALIGNMENT_MALLOC)
Wim Taymans's avatar
Wim Taymans committed
83
size_t gst_memory_alignment = 7;
84
#elif defined(MEMORY_ALIGNMENT_PAGESIZE)
Wim Taymans's avatar
Wim Taymans committed
85
/* we fill this in in the _init method */
Wim Taymans's avatar
Wim Taymans committed
86
size_t gst_memory_alignment = 0;
87
#elif defined(MEMORY_ALIGNMENT)
Wim Taymans's avatar
Wim Taymans committed
88
size_t gst_memory_alignment = MEMORY_ALIGNMENT - 1;
89 90
#else
#error "No memory alignment configured"
Wim Taymans's avatar
Wim Taymans committed
91
size_t gst_memory_alignment = 0;
92 93
#endif

94
struct _GstAllocator
95
{
96
  gint refcount;
97 98

  GstMemoryInfo info;
99 100 101

  gpointer user_data;
  GDestroyNotify notify;
102 103
};

Wim Taymans's avatar
Wim Taymans committed
104
/* default memory implementation */
105 106 107
typedef struct
{
  GstMemory mem;
Wim Taymans's avatar
Wim Taymans committed
108
  gsize slice_size;
109 110 111 112
  guint8 *data;
  GFreeFunc free_func;
} GstMemoryDefault;

113
/* the default allocator */
114
static GstAllocator *_default_allocator;
115 116

/* our predefined allocators */
117
static GstAllocator *_default_mem_impl;
Wim Taymans's avatar
Wim Taymans committed
118

Wim Taymans's avatar
Wim Taymans committed
119
/* initialize the fields */
Wim Taymans's avatar
Wim Taymans committed
120
static void
121 122 123
_default_mem_init (GstMemoryDefault * mem, GstMemoryFlags flags,
    GstMemory * parent, gsize slice_size, gpointer data,
    GFreeFunc free_func, gsize maxsize, gsize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
124
{
125
  mem->mem.allocator = _default_mem_impl;
126
  mem->mem.flags = flags;
Wim Taymans's avatar
Wim Taymans committed
127
  mem->mem.refcount = 1;
Wim Taymans's avatar
Wim Taymans committed
128
  mem->mem.parent = parent ? gst_memory_ref (parent) : NULL;
Wim Taymans's avatar
Wim Taymans committed
129 130 131 132
  mem->mem.state = (flags & GST_MEMORY_FLAG_READONLY ? 0x5 : 0);
  mem->mem.maxsize = maxsize;
  mem->mem.offset = offset;
  mem->mem.size = size;
Wim Taymans's avatar
Wim Taymans committed
133 134 135
  mem->slice_size = slice_size;
  mem->data = data;
  mem->free_func = free_func;
Wim Taymans's avatar
Wim Taymans committed
136 137

  GST_DEBUG ("new memory %p", mem);
Wim Taymans's avatar
Wim Taymans committed
138 139
}

Wim Taymans's avatar
Wim Taymans committed
140
/* create a new memory block that manages the given memory */
Wim Taymans's avatar
Wim Taymans committed
141
static GstMemoryDefault *
142
_default_mem_new (GstMemoryFlags flags, GstMemory * parent, gpointer data,
Wim Taymans's avatar
Wim Taymans committed
143 144 145
    GFreeFunc free_func, gsize maxsize, gsize offset, gsize size)
{
  GstMemoryDefault *mem;
Wim Taymans's avatar
Wim Taymans committed
146
  gsize slice_size;
Wim Taymans's avatar
Wim Taymans committed
147

Wim Taymans's avatar
Wim Taymans committed
148 149 150
  slice_size = sizeof (GstMemoryDefault);

  mem = g_slice_alloc (slice_size);
151
  _default_mem_init (mem, flags, parent, slice_size,
Wim Taymans's avatar
Wim Taymans committed
152 153 154 155
      data, free_func, maxsize, offset, size);

  return mem;
}
156

Wim Taymans's avatar
Wim Taymans committed
157
/* allocate the memory and structure in one block */
Wim Taymans's avatar
Wim Taymans committed
158 159 160 161 162 163 164
static GstMemoryDefault *
_default_mem_new_block (gsize maxsize, gsize align, gsize offset, gsize size)
{
  GstMemoryDefault *mem;
  gsize aoffset, slice_size;
  guint8 *data;

165
  /* ensure configured alignment */
Wim Taymans's avatar
Wim Taymans committed
166
  align |= gst_memory_alignment;
Wim Taymans's avatar
Wim Taymans committed
167 168
  /* allocate more to compensate for alignment */
  maxsize += align;
Wim Taymans's avatar
Wim Taymans committed
169
  /* alloc header and data in one block */
Wim Taymans's avatar
Wim Taymans committed
170
  slice_size = sizeof (GstMemoryDefault) + maxsize;
Wim Taymans's avatar
Wim Taymans committed
171 172 173 174 175 176 177 178

  mem = g_slice_alloc (slice_size);
  if (mem == NULL)
    return NULL;

  data = (guint8 *) mem + sizeof (GstMemoryDefault);

  if ((aoffset = ((guintptr) data & align)))
179
    aoffset = (align + 1) - aoffset;
Wim Taymans's avatar
Wim Taymans committed
180

Wim Taymans's avatar
Wim Taymans committed
181
  _default_mem_init (mem, 0, NULL, slice_size, data, NULL, maxsize,
Wim Taymans's avatar
Wim Taymans committed
182 183 184 185 186
      aoffset + offset, size);

  return mem;
}

187
static GstMemory *
188 189
_default_alloc_alloc (GstAllocator * allocator, gsize maxsize, gsize align,
    gpointer user_data)
190 191 192 193
{
  return (GstMemory *) _default_mem_new_block (maxsize, align, 0, maxsize);
}

Wim Taymans's avatar
Wim Taymans committed
194
static gpointer
Wim Taymans's avatar
Wim Taymans committed
195
_default_mem_map (GstMemoryDefault * mem, GstMapFlags flags)
Wim Taymans's avatar
Wim Taymans committed
196
{
Wim Taymans's avatar
Wim Taymans committed
197
  return mem->data;
198 199 200
}

static gboolean
Wim Taymans's avatar
Wim Taymans committed
201
_default_mem_unmap (GstMemoryDefault * mem)
202
{
Wim Taymans's avatar
Wim Taymans committed
203 204
  return TRUE;
}
205 206

static void
Wim Taymans's avatar
Wim Taymans committed
207
_default_mem_free (GstMemoryDefault * mem)
208
{
Wim Taymans's avatar
Wim Taymans committed
209 210
  GST_DEBUG ("free memory %p", mem);

Wim Taymans's avatar
Wim Taymans committed
211 212 213 214 215
  if (mem->mem.parent)
    gst_memory_unref (mem->mem.parent);

  if (mem->free_func)
    mem->free_func (mem->data);
216

Wim Taymans's avatar
Wim Taymans committed
217
  g_slice_free1 (mem->slice_size, mem);
218 219
}

Wim Taymans's avatar
Wim Taymans committed
220
static GstMemoryDefault *
221
_default_mem_copy (GstMemoryDefault * mem, gssize offset, gsize size)
222 223 224
{
  GstMemoryDefault *copy;

Wim Taymans's avatar
Wim Taymans committed
225
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
226
    size = mem->mem.size > offset ? mem->mem.size - offset : 0;
Wim Taymans's avatar
Wim Taymans committed
227

Wim Taymans's avatar
Wim Taymans committed
228 229 230 231
  copy =
      _default_mem_new_block (mem->mem.maxsize, 0, mem->mem.offset + offset,
      size);
  memcpy (copy->data, mem->data, mem->mem.maxsize);
232

Wim Taymans's avatar
Wim Taymans committed
233
  return copy;
234 235
}

Wim Taymans's avatar
Wim Taymans committed
236
static GstMemoryDefault *
237
_default_mem_share (GstMemoryDefault * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
238 239 240 241 242 243 244 245
{
  GstMemoryDefault *sub;
  GstMemory *parent;

  /* find the real parent */
  if ((parent = mem->mem.parent) == NULL)
    parent = (GstMemory *) mem;

246
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
247
    size = mem->mem.size - offset;
248

Wim Taymans's avatar
Wim Taymans committed
249 250 251
  sub =
      _default_mem_new (parent->flags, parent, mem->data, NULL,
      mem->mem.maxsize, mem->mem.offset + offset, size);
Wim Taymans's avatar
Wim Taymans committed
252 253

  return sub;
254 255
}

Wim Taymans's avatar
Wim Taymans committed
256
static gboolean
Wim Taymans's avatar
Wim Taymans committed
257 258
_default_mem_is_span (GstMemoryDefault * mem1, GstMemoryDefault * mem2,
    gsize * offset)
259
{
Wim Taymans's avatar
Wim Taymans committed
260 261 262 263 264 265

  if (offset) {
    GstMemoryDefault *parent;

    parent = (GstMemoryDefault *) mem1->mem.parent;

Wim Taymans's avatar
Wim Taymans committed
266
    *offset = mem1->mem.offset - parent->mem.offset;
Wim Taymans's avatar
Wim Taymans committed
267
  }
268

Wim Taymans's avatar
Wim Taymans committed
269
  /* and memory is contiguous */
Wim Taymans's avatar
Wim Taymans committed
270 271
  return mem1->data + mem1->mem.offset + mem1->mem.size ==
      mem2->data + mem2->mem.offset;
272 273 274
}

static GstMemory *
275
_fallback_mem_copy (GstMemory * mem, gssize offset, gssize size)
276
{
277
  GstMemory *copy;
Wim Taymans's avatar
Wim Taymans committed
278
  GstMapInfo sinfo, dinfo;
279

Wim Taymans's avatar
Wim Taymans committed
280
  if (!gst_memory_map (mem, &sinfo, GST_MAP_READ))
Wim Taymans's avatar
Wim Taymans committed
281
    return NULL;
Wim Taymans's avatar
Wim Taymans committed
282

Wim Taymans's avatar
Wim Taymans committed
283
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
284 285
    size = sinfo.size > offset ? sinfo.size - offset : 0;

Wim Taymans's avatar
Wim Taymans committed
286 287
  /* use the same allocator as the memory we copy  */
  copy = gst_allocator_alloc (mem->allocator, size, mem->align);
Wim Taymans's avatar
Wim Taymans committed
288 289 290 291 292 293
  if (!gst_memory_map (copy, &dinfo, GST_MAP_WRITE)) {
    GST_WARNING ("could not write map memory %p", copy);
    gst_memory_unmap (mem, &sinfo);
    return NULL;
  }

294
  memcpy (dinfo.data, sinfo.data + offset, size);
Wim Taymans's avatar
Wim Taymans committed
295 296
  gst_memory_unmap (copy, &dinfo);
  gst_memory_unmap (mem, &sinfo);
297

Wim Taymans's avatar
Wim Taymans committed
298
  return copy;
299 300 301
}

static gboolean
302
_fallback_mem_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
303 304 305 306
{
  return FALSE;
}

Wim Taymans's avatar
Wim Taymans committed
307
static GRWLock lock;
308
static GHashTable *allocators;
309

310 311 312 313 314 315
static void
_priv_sysmem_notify (gpointer user_data)
{
  g_warning ("The default memory allocator was freed!");
}

Wim Taymans's avatar
Wim Taymans committed
316
void
Wim Taymans's avatar
Wim Taymans committed
317
_priv_gst_memory_initialize (void)
Wim Taymans's avatar
Wim Taymans committed
318 319
{
  static const GstMemoryInfo _mem_info = {
320
    (GstAllocatorAllocFunction) _default_alloc_alloc,
Wim Taymans's avatar
Wim Taymans committed
321 322 323 324
    (GstMemoryMapFunction) _default_mem_map,
    (GstMemoryUnmapFunction) _default_mem_unmap,
    (GstMemoryFreeFunction) _default_mem_free,
    (GstMemoryCopyFunction) _default_mem_copy,
Wim Taymans's avatar
Wim Taymans committed
325
    (GstMemoryShareFunction) _default_mem_share,
326
    (GstMemoryIsSpanFunction) _default_mem_is_span,
Wim Taymans's avatar
Wim Taymans committed
327 328
  };

Wim Taymans's avatar
Wim Taymans committed
329
  g_rw_lock_init (&lock);
330
  allocators = g_hash_table_new (g_str_hash, g_str_equal);
331

332 333
#ifdef HAVE_GETPAGESIZE
#ifdef MEMORY_ALIGNMENT_PAGESIZE
Wim Taymans's avatar
Wim Taymans committed
334
  gst_memory_alignment = getpagesize () - 1;
335 336 337
#endif
#endif

Wim Taymans's avatar
Wim Taymans committed
338 339
  GST_DEBUG ("memory alignment: %" G_GSIZE_FORMAT, gst_memory_alignment);

340
  _default_mem_impl = gst_allocator_new (&_mem_info, NULL, _priv_sysmem_notify);
341

342 343 344
  _default_allocator = gst_allocator_ref (_default_mem_impl);
  gst_allocator_register (GST_ALLOCATOR_SYSMEM,
      gst_allocator_ref (_default_mem_impl));
345 346
}

Wim Taymans's avatar
Wim Taymans committed
347 348 349 350 351 352 353 354 355 356 357 358 359
/**
 * gst_memory_new_wrapped:
 * @flags: #GstMemoryFlags
 * @data: data to wrap
 * @free_func: function to free @data
 * @maxsize: allocated size of @data
 * @offset: offset in @data
 * @size: size of valid data
 *
 * Allocate a new memory block that wraps the given @data.
 *
 * Returns: a new #GstMemory.
 */
Wim Taymans's avatar
Wim Taymans committed
360 361 362
GstMemory *
gst_memory_new_wrapped (GstMemoryFlags flags, gpointer data,
    GFreeFunc free_func, gsize maxsize, gsize offset, gsize size)
363
{
Wim Taymans's avatar
Wim Taymans committed
364
  GstMemoryDefault *mem;
Wim Taymans's avatar
Wim Taymans committed
365

Wim Taymans's avatar
Wim Taymans committed
366 367 368
  g_return_val_if_fail (data != NULL, NULL);
  g_return_val_if_fail (offset + size <= maxsize, NULL);

Wim Taymans's avatar
Wim Taymans committed
369 370 371 372 373 374 375 376 377
  mem = _default_mem_new (flags, NULL, data, free_func, maxsize, offset, size);

  return (GstMemory *) mem;
}

/**
 * gst_memory_ref:
 * @mem: a #GstMemory
 *
Wim Taymans's avatar
Wim Taymans committed
378 379
 * Increases the refcount of @mem.
 *
Wim Taymans's avatar
Wim Taymans committed
380 381
 * Returns: @mem with increased refcount
 */
382 383 384 385 386
GstMemory *
gst_memory_ref (GstMemory * mem)
{
  g_return_val_if_fail (mem != NULL, NULL);

Wim Taymans's avatar
Wim Taymans committed
387 388
  GST_DEBUG ("memory %p, %d->%d", mem, mem->refcount, mem->refcount + 1);

389 390 391 392 393
  g_atomic_int_inc (&mem->refcount);

  return mem;
}

Wim Taymans's avatar
Wim Taymans committed
394 395 396
/**
 * gst_memory_unref:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
397 398 399
 *
 * Decreases the refcount of @mem. When the refcount reaches 0, the free
 * function of @mem will be called.
Wim Taymans's avatar
Wim Taymans committed
400
 */
401 402 403 404
void
gst_memory_unref (GstMemory * mem)
{
  g_return_if_fail (mem != NULL);
405
  g_return_if_fail (mem->allocator != NULL);
406

Wim Taymans's avatar
Wim Taymans committed
407 408
  GST_DEBUG ("memory %p, %d->%d", mem, mem->refcount, mem->refcount - 1);

409
  if (g_atomic_int_dec_and_test (&mem->refcount))
410
    mem->allocator->info.mem_free (mem);
411 412
}

Wim Taymans's avatar
Wim Taymans committed
413 414 415
/**
 * gst_memory_get_sizes:
 * @mem: a #GstMemory
416
 * @offset: pointer to offset
Wim Taymans's avatar
Wim Taymans committed
417 418
 * @maxsize: pointer to maxsize
 *
419
 * Get the current @size, @offset and @maxsize of @mem.
Wim Taymans's avatar
Wim Taymans committed
420 421
 *
 * Returns: the current sizes of @mem
Wim Taymans's avatar
Wim Taymans committed
422
 */
423
gsize
424
gst_memory_get_sizes (GstMemory * mem, gsize * offset, gsize * maxsize)
425 426 427
{
  g_return_val_if_fail (mem != NULL, 0);

Wim Taymans's avatar
Wim Taymans committed
428 429 430 431 432 433
  if (offset)
    *offset = mem->offset;
  if (maxsize)
    *maxsize = mem->maxsize;

  return mem->size;
434 435
}

Wim Taymans's avatar
Wim Taymans committed
436 437 438 439 440 441 442 443 444 445
/**
 * gst_memory_resize:
 * @mem: a #GstMemory
 * @offset: a new offset
 * @size: a new size
 *
 * Resize the memory region. @mem should be writable and offset + size should be
 * less than the maxsize of @mem.
 */
void
446
gst_memory_resize (GstMemory * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
447 448
{
  g_return_if_fail (mem != NULL);
449
  g_return_if_fail (gst_memory_is_writable (mem));
Wim Taymans's avatar
Wim Taymans committed
450 451
  g_return_if_fail (offset >= 0 || mem->offset >= -offset);
  g_return_if_fail (size + mem->offset + offset <= mem->maxsize);
Wim Taymans's avatar
Wim Taymans committed
452

Wim Taymans's avatar
Wim Taymans committed
453 454
  mem->offset += offset;
  mem->size = size;
Wim Taymans's avatar
Wim Taymans committed
455 456
}

457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
/**
 * gst_memory_is_writable:
 * @mem: a #GstMemory
 *
 * Check if @mem is writable.
 *
 * Returns: %TRUE is @mem is writable.
 */
gboolean
gst_memory_is_writable (GstMemory * mem)
{
  g_return_val_if_fail (mem != NULL, FALSE);

  return (mem->refcount == 1) &&
      ((mem->parent == NULL) || (mem->parent->refcount == 1)) &&
      ((mem->flags & GST_MEMORY_FLAG_READONLY) == 0);
}

Wim Taymans's avatar
Wim Taymans committed
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
static gboolean
gst_memory_lock (GstMemory * mem, GstMapFlags flags)
{
  gint access_mode, state, newstate;

  access_mode = flags & 3;

  do {
    state = g_atomic_int_get (&mem->state);
    if (state == 0) {
      /* nothing mapped, set access_mode and refcount */
      newstate = 4 | access_mode;
    } else {
      /* access_mode must match */
      if ((state & access_mode) != access_mode)
        goto lock_failed;
      /* increase refcount */
      newstate = state + 4;
    }
  } while (!g_atomic_int_compare_and_exchange (&mem->state, state, newstate));

  return TRUE;

lock_failed:
  {
    GST_DEBUG ("lock failed %p: state %d, access_mode %d", mem, state,
        access_mode);
    return FALSE;
  }
}

static void
gst_memory_unlock (GstMemory * mem)
{
  gint state, newstate;

  do {
    state = g_atomic_int_get (&mem->state);
    /* decrease the refcount */
    newstate = state - 4;
    /* last refcount, unset access_mode */
    if (newstate < 4)
      newstate = 0;
  } while (!g_atomic_int_compare_and_exchange (&mem->state, state, newstate));
}

521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546

/**
 * gst_memory_make_mapped:
 * @mem: (transfer full): a #GstMemory
 * @info: (out): pointer for info
 * @flags: mapping flags
 *
 * Create a #GstMemory object that is mapped with @flags. If @mem is mappable
 * with @flags, this function returns the mapped @mem directly. Otherwise a
 * mapped copy of @mem is returned.
 *
 * This function takes ownership of old @mem and returns a reference to a new
 * #GstMemory.
 *
 * Returns: (transfer full): a #GstMemory object mapped with @flags or NULL when
 * a mapping is not possible.
 */
GstMemory *
gst_memory_make_mapped (GstMemory * mem, GstMapInfo * info, GstMapFlags flags)
{
  GstMemory *result;

  if (gst_memory_map (mem, info, flags)) {
    result = mem;
  } else {
    result = gst_memory_copy (mem, 0, -1);
Wim Taymans's avatar
Wim Taymans committed
547 548
    gst_memory_unref (mem);

549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
    if (result == NULL)
      goto cannot_copy;

    if (!gst_memory_map (result, info, flags))
      goto cannot_map;
  }
  return result;

  /* ERRORS */
cannot_copy:
  {
    GST_DEBUG ("cannot copy memory %p", mem);
    return NULL;
  }
cannot_map:
  {
    GST_DEBUG ("cannot map memory %p with flags %d", mem, flags);
Wim Taymans's avatar
Wim Taymans committed
566
    gst_memory_unref (result);
567 568 569 570
    return NULL;
  }
}

Wim Taymans's avatar
Wim Taymans committed
571 572 573
/**
 * gst_memory_map:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
574
 * @info: (out): pointer for info
Wim Taymans's avatar
Wim Taymans committed
575 576
 * @flags: mapping flags
 *
Wim Taymans's avatar
Wim Taymans committed
577 578
 * Fill @info with the pointer and sizes of the memory in @mem that can be
 * accessed according to @flags.
Wim Taymans's avatar
Wim Taymans committed
579
 *
Wim Taymans's avatar
Wim Taymans committed
580
 * This function can return %FALSE for various reasons:
Wim Taymans's avatar
Wim Taymans committed
581 582 583
 * - the memory backed by @mem is not accessible with the given @flags.
 * - the memory was already mapped with a different mapping.
 *
Wim Taymans's avatar
Wim Taymans committed
584
 * @info and its contents remains valid for as long as @mem is alive and until
Wim Taymans's avatar
Wim Taymans committed
585 586 587 588 589
 * gst_memory_unmap() is called.
 *
 * For each gst_memory_map() call, a corresponding gst_memory_unmap() call
 * should be done.
 *
Wim Taymans's avatar
Wim Taymans committed
590
 * Returns: %TRUE if the map operation was successful.
Wim Taymans's avatar
Wim Taymans committed
591
 */
Wim Taymans's avatar
Wim Taymans committed
592 593
gboolean
gst_memory_map (GstMemory * mem, GstMapInfo * info, GstMapFlags flags)
594
{
Wim Taymans's avatar
Wim Taymans committed
595 596
  g_return_val_if_fail (mem != NULL, FALSE);
  g_return_val_if_fail (info != NULL, FALSE);
597

Wim Taymans's avatar
Wim Taymans committed
598 599 600
  if (!gst_memory_lock (mem, flags))
    goto lock_failed;

601
  info->data = mem->allocator->info.mem_map (mem, mem->maxsize, flags);
Wim Taymans's avatar
Wim Taymans committed
602

Wim Taymans's avatar
Wim Taymans committed
603
  if (G_UNLIKELY (info->data == NULL))
Wim Taymans's avatar
Wim Taymans committed
604 605
    goto error;

Wim Taymans's avatar
Wim Taymans committed
606
  info->memory = mem;
Wim Taymans's avatar
Wim Taymans committed
607
  info->flags = flags;
Wim Taymans's avatar
Wim Taymans committed
608 609
  info->size = mem->size;
  info->maxsize = mem->maxsize - mem->offset;
610
  info->data = info->data + mem->offset;
611

Wim Taymans's avatar
Wim Taymans committed
612
  return TRUE;
613

Wim Taymans's avatar
Wim Taymans committed
614 615 616
  /* ERRORS */
lock_failed:
  {
617
    GST_DEBUG ("mem %p: lock %d failed", mem, flags);
Wim Taymans's avatar
Wim Taymans committed
618
    return FALSE;
Wim Taymans's avatar
Wim Taymans committed
619 620 621
  }
error:
  {
622
    /* something went wrong, restore the orginal state again */
Wim Taymans's avatar
Wim Taymans committed
623 624
    GST_ERROR ("mem %p: map failed", mem);
    gst_memory_unlock (mem);
Wim Taymans's avatar
Wim Taymans committed
625
    return FALSE;
626
  }
627 628
}

Wim Taymans's avatar
Wim Taymans committed
629 630 631
/**
 * gst_memory_unmap:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
632
 * @info: a #GstMapInfo
Wim Taymans's avatar
Wim Taymans committed
633
 *
Wim Taymans's avatar
Wim Taymans committed
634
 * Release the memory obtained with gst_memory_map()
Wim Taymans's avatar
Wim Taymans committed
635
 */
Wim Taymans's avatar
Wim Taymans committed
636
void
Wim Taymans's avatar
Wim Taymans committed
637
gst_memory_unmap (GstMemory * mem, GstMapInfo * info)
638
{
Wim Taymans's avatar
Wim Taymans committed
639
  g_return_if_fail (mem != NULL);
Wim Taymans's avatar
Wim Taymans committed
640 641
  g_return_if_fail (info != NULL);
  g_return_if_fail (info->memory == mem);
Wim Taymans's avatar
Wim Taymans committed
642 643
  /* there must be a ref */
  g_return_if_fail (g_atomic_int_get (&mem->state) >= 4);
644

645
  mem->allocator->info.mem_unmap (mem);
Wim Taymans's avatar
Wim Taymans committed
646
  gst_memory_unlock (mem);
647 648
}

Wim Taymans's avatar
Wim Taymans committed
649 650 651 652
/**
 * gst_memory_copy:
 * @mem: a #GstMemory
 * @offset: an offset to copy
653
 * @size: size to copy or -1 to copy all bytes from offset
Wim Taymans's avatar
Wim Taymans committed
654 655 656 657 658 659 660
 *
 * Return a copy of @size bytes from @mem starting from @offset. This copy is
 * guaranteed to be writable. @size can be set to -1 to return a copy all bytes
 * from @offset.
 *
 * Returns: a new #GstMemory.
 */
661
GstMemory *
662
gst_memory_copy (GstMemory * mem, gssize offset, gssize size)
663
{
Wim Taymans's avatar
Wim Taymans committed
664 665
  GstMemory *copy;

666
  g_return_val_if_fail (mem != NULL, NULL);
Wim Taymans's avatar
Wim Taymans committed
667 668
  g_return_val_if_fail (gst_memory_lock (mem, GST_MAP_READ), NULL);

669
  copy = mem->allocator->info.mem_copy (mem, offset, size);
670

Wim Taymans's avatar
Wim Taymans committed
671 672 673
  gst_memory_unlock (mem);

  return copy;
674 675
}

Wim Taymans's avatar
Wim Taymans committed
676 677 678 679
/**
 * gst_memory_share:
 * @mem: a #GstMemory
 * @offset: an offset to share
680
 * @size: size to share or -1 to share bytes from offset
Wim Taymans's avatar
Wim Taymans committed
681
 *
682 683 684 685
 * Return a shared copy of @size bytes from @mem starting from @offset. No
 * memory copy is performed and the memory region is simply shared. The result
 * is guaranteed to be not-writable. @size can be set to -1 to return a share
 * all bytes from @offset.
Wim Taymans's avatar
Wim Taymans committed
686 687 688
 *
 * Returns: a new #GstMemory.
 */
689
GstMemory *
690
gst_memory_share (GstMemory * mem, gssize offset, gssize size)
691 692 693
{
  g_return_val_if_fail (mem != NULL, NULL);

694
  return mem->allocator->info.mem_share (mem, offset, size);
695 696
}

Wim Taymans's avatar
Wim Taymans committed
697 698 699 700 701 702 703 704 705 706 707 708 709 710 711
/**
 * gst_memory_is_span:
 * @mem1: a #GstMemory
 * @mem2: a #GstMemory
 * @offset: a pointer to a result offset
 *
 * Check if @mem1 and mem2 share the memory with a common parent memory object
 * and that the memory is contiguous.
 *
 * If this is the case, the memory of @mem1 and @mem2 can be merged
 * efficiently by performing gst_memory_share() on the parent object from
 * the returned @offset.
 *
 * Returns: %TRUE if the memory is contiguous and of a common parent.
 */
Wim Taymans's avatar
Wim Taymans committed
712
gboolean
713
gst_memory_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
Wim Taymans's avatar
Wim Taymans committed
714 715 716 717
{
  g_return_val_if_fail (mem1 != NULL, FALSE);
  g_return_val_if_fail (mem2 != NULL, FALSE);

718 719
  /* need to have the same allocators */
  if (mem1->allocator != mem2->allocator)
Wim Taymans's avatar
Wim Taymans committed
720 721
    return FALSE;

722 723 724
  /* need to have the same parent */
  if (mem1->parent == NULL || mem1->parent != mem2->parent)
    return FALSE;
Wim Taymans's avatar
Wim Taymans committed
725

726
  /* and memory is contiguous */
727
  if (!mem1->allocator->info.mem_is_span (mem1, mem2, offset))
728 729 730
    return FALSE;

  return TRUE;
Wim Taymans's avatar
Wim Taymans committed
731
}
732 733

/**
734
 * gst_allocator_register:
735 736 737
 * @info: a #GstMemoryInfo
 * @user_data: user data
 * @notify: a #GDestroyNotify for @user_data
738
 *
739
 * Create a new memory allocator with @info and @user_data.
740
 *
741
 * All functions in @info are mandatory exept the copy and is_span
742 743
 * functions, which will have a default implementation when left NULL.
 *
744 745
 * The @user_data will be passed to all calls of the alloc function and the
 * @notify function.
746
 *
747
 * Returns: a new #GstAllocator.
748
 */
749 750 751
GstAllocator *
gst_allocator_new (const GstMemoryInfo * info, gpointer user_data,
    GDestroyNotify notify)
752
{
753
  GstAllocator *allocator;
754 755 756 757 758

#define INSTALL_FALLBACK(_t) \
  if (allocator->info._t == NULL) allocator->info._t = _fallback_ ##_t;

  g_return_val_if_fail (info != NULL, NULL);
759
  g_return_val_if_fail (info->alloc != NULL, NULL);
760 761 762 763
  g_return_val_if_fail (info->mem_map != NULL, NULL);
  g_return_val_if_fail (info->mem_unmap != NULL, NULL);
  g_return_val_if_fail (info->mem_free != NULL, NULL);
  g_return_val_if_fail (info->mem_share != NULL, NULL);
764

765
  allocator = g_slice_new (GstAllocator);
766
  allocator->refcount = 1;
767
  allocator->info = *info;
768 769 770 771
  allocator->user_data = user_data;
  allocator->notify = notify;
  INSTALL_FALLBACK (mem_copy);
  INSTALL_FALLBACK (mem_is_span);
772 773
#undef INSTALL_FALLBACK

774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836
  GST_DEBUG ("new allocator %p", allocator);

  return allocator;
}

/**
 * gst_alocator_ref:
 * @allocator: a #GstAllocator
 *
 * Increases the refcount of @allocator.
 *
 * Returns: @allocator with increased refcount
 */
GstAllocator *
gst_allocator_ref (GstAllocator * allocator)
{
  g_return_val_if_fail (allocator != NULL, NULL);

  GST_DEBUG ("alocator %p, %d->%d", allocator, allocator->refcount,
      allocator->refcount + 1);

  g_atomic_int_inc (&allocator->refcount);

  return allocator;
}

/**
 * gst_allocator_unref:
 * @allocator: a #GstAllocator
 *
 * Decreases the refcount of @allocator. When the refcount reaches 0, the free
 * function of @allocator will be called.
 */
void
gst_allocator_unref (GstAllocator * allocator)
{
  g_return_if_fail (allocator != NULL);

  GST_DEBUG ("allocator %p, %d->%d", allocator, allocator->refcount,
      allocator->refcount - 1);

  if (g_atomic_int_dec_and_test (&allocator->refcount)) {
    if (allocator->notify)
      allocator->notify (allocator->user_data);
    g_slice_free1 (sizeof (GstAllocator), allocator);
  }
}

/**
 * gst_allocator_register:
 * @name: the name of the allocator
 * @allocator: (transfer full): #GstAllocator
 *
 * Registers the memory @allocator with @name. This function takes ownership of
 * @allocator.
 */
void
gst_allocator_register (const gchar * name, GstAllocator * allocator)
{
  g_return_if_fail (name != NULL);
  g_return_if_fail (allocator != NULL);

  GST_DEBUG ("registering allocator %p with name \"%s\"", allocator, name);
837

Wim Taymans's avatar
Wim Taymans committed
838
  g_rw_lock_writer_lock (&lock);
839
  g_hash_table_insert (allocators, (gpointer) name, (gpointer) allocator);
Wim Taymans's avatar
Wim Taymans committed
840
  g_rw_lock_writer_unlock (&lock);
841 842 843
}

/**
844
 * gst_allocator_find:
845 846
 * @name: the name of the allocator
 *
847 848
 * Find a previously registered allocator with @name. When @name is NULL, the
 * default allocator will be returned.
849
 *
850 851
 * Returns: (transfer full): a #GstAllocator or NULL when the allocator with @name was not
 * registered. Use gst_allocator_unref() to release the allocator after usage.
852
 */
853
GstAllocator *
854
gst_allocator_find (const gchar * name)
855
{
856
  GstAllocator *allocator;
857

Wim Taymans's avatar
Wim Taymans committed
858
  g_rw_lock_reader_lock (&lock);
859
  if (name) {
860
    allocator = g_hash_table_lookup (allocators, (gconstpointer) name);
861 862 863
  } else {
    allocator = _default_allocator;
  }
864 865
  if (allocator)
    gst_allocator_ref (allocator);
Wim Taymans's avatar
Wim Taymans committed
866
  g_rw_lock_reader_unlock (&lock);
867 868 869 870 871

  return allocator;
}

/**
872
 * gst_allocator_set_default:
873
 * @allocator: (transfer full): a #GstAllocator
874
 *
875
 * Set the default allocator. This function takes ownership of @allocator.
876 877
 */
void
878
gst_allocator_set_default (GstAllocator * allocator)
879
{
880
  GstAllocator *old;
881 882
  g_return_if_fail (allocator != NULL);

Wim Taymans's avatar
Wim Taymans committed
883
  g_rw_lock_writer_lock (&lock);
884
  old = _default_allocator;
885
  _default_allocator = allocator;
Wim Taymans's avatar
Wim Taymans committed
886
  g_rw_lock_writer_unlock (&lock);
887 888 889

  if (old)
    gst_allocator_unref (old);
890 891 892
}

/**
893
 * gst_allocator_alloc:
894
 * @allocator: (transfer none) (allow-none): a #GstAllocator to use
895 896 897 898 899 900
 * @maxsize: allocated size of @data
 * @align: alignment for the data
 *
 * Use @allocator to allocate a new memory block with memory that is at least
 * @maxsize big and has the given alignment.
 *
901 902
 * When @allocator is NULL, the default allocator will be used.
 *
903 904 905
 * @align is given as a bitmask so that @align + 1 equals the amount of bytes to
 * align to. For example, to align to 8 bytes, use an alignment of 7.
 *
906
 * Returns: (transfer full): a new #GstMemory.
907 908
 */
GstMemory *
909
gst_allocator_alloc (GstAllocator * allocator, gsize maxsize, gsize align)
910 911 912 913 914 915
{
  g_return_val_if_fail (((align + 1) & align) == 0, NULL);

  if (allocator == NULL)
    allocator = _default_allocator;

916
  return allocator->info.alloc (allocator, maxsize, align,
917
      allocator->user_data);
918
}