gstmemory.c 21 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

Wim Taymans's avatar
Wim Taymans committed
66
#include "config.h"
67 68 69
#include "gst_private.h"
#include "gstmemory.h"

70 71
G_DEFINE_BOXED_TYPE (GstMemory, gst_memory, (GBoxedCopyFunc) gst_memory_ref,
    (GBoxedFreeFunc) gst_memory_unref);
Wim Taymans's avatar
Wim Taymans committed
72

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

91
struct _GstAllocator
92 93 94 95 96 97
{
  GQuark name;

  GstMemoryInfo info;
};

Wim Taymans's avatar
Wim Taymans committed
98
/* default memory implementation */
99 100 101
typedef struct
{
  GstMemory mem;
Wim Taymans's avatar
Wim Taymans committed
102
  gsize slice_size;
103 104 105 106 107 108 109
  guint8 *data;
  GFreeFunc free_func;
  gsize maxsize;
  gsize offset;
  gsize size;
} GstMemoryDefault;

110
/* the default allocator */
111
static const GstAllocator *_default_allocator;
112 113

/* our predefined allocators */
114
static const GstAllocator *_default_mem_impl;
Wim Taymans's avatar
Wim Taymans committed
115

Wim Taymans's avatar
Wim Taymans committed
116
/* initialize the fields */
Wim Taymans's avatar
Wim Taymans committed
117
static void
118 119 120
_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
121
{
122
  mem->mem.allocator = _default_mem_impl;
123
  mem->mem.flags = flags;
Wim Taymans's avatar
Wim Taymans committed
124
  mem->mem.refcount = 1;
Wim Taymans's avatar
Wim Taymans committed
125
  mem->mem.parent = parent ? gst_memory_ref (parent) : NULL;
126
  mem->mem.state = 0;
Wim Taymans's avatar
Wim Taymans committed
127 128 129 130 131 132 133 134
  mem->slice_size = slice_size;
  mem->data = data;
  mem->free_func = free_func;
  mem->maxsize = maxsize;
  mem->offset = offset;
  mem->size = size;
}

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

Wim Taymans's avatar
Wim Taymans committed
143 144 145
  slice_size = sizeof (GstMemoryDefault);

  mem = g_slice_alloc (slice_size);
146
  _default_mem_init (mem, flags, parent, slice_size,
Wim Taymans's avatar
Wim Taymans committed
147 148 149 150
      data, free_func, maxsize, offset, size);

  return mem;
}
151

Wim Taymans's avatar
Wim Taymans committed
152
/* allocate the memory and structure in one block */
Wim Taymans's avatar
Wim Taymans committed
153 154 155 156 157 158 159
static GstMemoryDefault *
_default_mem_new_block (gsize maxsize, gsize align, gsize offset, gsize size)
{
  GstMemoryDefault *mem;
  gsize aoffset, slice_size;
  guint8 *data;

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

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

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

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

Wim Taymans's avatar
Wim Taymans committed
176
  _default_mem_init (mem, 0, NULL, slice_size, data, NULL, maxsize,
Wim Taymans's avatar
Wim Taymans committed
177 178 179 180 181
      aoffset + offset, size);

  return mem;
}

182
static GstMemory *
183
_default_mem_alloc (const GstAllocator * allocator, gsize maxsize, gsize align)
184 185 186 187
{
  return (GstMemory *) _default_mem_new_block (maxsize, align, 0, maxsize);
}

188
static gsize
189
_default_mem_get_sizes (GstMemoryDefault * mem, gsize * offset, gsize * maxsize)
190
{
191 192
  if (offset)
    *offset = mem->offset;
Wim Taymans's avatar
Wim Taymans committed
193 194
  if (maxsize)
    *maxsize = mem->maxsize;
195

Wim Taymans's avatar
Wim Taymans committed
196 197 198 199
  return mem->size;
}

static void
200
_default_mem_resize (GstMemoryDefault * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
201
{
202
  g_return_if_fail (offset >= 0 || mem->offset >= -offset);
Wim Taymans's avatar
Wim Taymans committed
203
  g_return_if_fail (size + mem->offset + offset <= mem->maxsize);
Wim Taymans's avatar
Wim Taymans committed
204 205 206 207 208 209 210 211 212 213 214

  mem->offset += offset;
  mem->size = size;
}

static gpointer
_default_mem_map (GstMemoryDefault * mem, gsize * size, gsize * maxsize,
    GstMapFlags flags)
{
  if (size)
    *size = mem->size;
215
  if (maxsize)
Wim Taymans's avatar
Wim Taymans committed
216
    *maxsize = mem->maxsize - mem->offset;
217

Wim Taymans's avatar
Wim Taymans committed
218
  return mem->data + mem->offset;
219 220 221
}

static gboolean
Wim Taymans's avatar
Wim Taymans committed
222
_default_mem_unmap (GstMemoryDefault * mem, gpointer data, gsize size)
223
{
Matej's avatar
Matej committed
224 225 226 227
  GST_DEBUG ("mem: %p, data %p, size %" G_GSIZE_FORMAT, mem, data, size);
  GST_DEBUG ("mem: %p, data %p,  offset %" G_GSIZE_FORMAT ", size %"
      G_GSIZE_FORMAT ", maxsize %" G_GSIZE_FORMAT, mem, mem->data, mem->offset,
      mem->size, mem->maxsize);
228

Wim Taymans's avatar
Wim Taymans committed
229 230 231
  g_return_val_if_fail ((guint8 *) data >= mem->data
      && (guint8 *) data < mem->data + mem->maxsize, FALSE);

Wim Taymans's avatar
Wim Taymans committed
232 233 234 235 236 237
  if (mem->data + mem->offset != data) {
    gsize newoffset = (guint8 *) data - mem->data;
    if (size == -1)
      size = mem->offset + mem->size - newoffset;
    mem->offset = newoffset;
  }
Wim Taymans's avatar
Wim Taymans committed
238

239
  if (size != -1) {
Wim Taymans's avatar
Wim Taymans committed
240
    g_return_val_if_fail (mem->offset + size <= mem->maxsize, FALSE);
Wim Taymans's avatar
Wim Taymans committed
241
    mem->size = size;
242
  }
Wim Taymans's avatar
Wim Taymans committed
243

Wim Taymans's avatar
Wim Taymans committed
244 245
  return TRUE;
}
246 247

static void
Wim Taymans's avatar
Wim Taymans committed
248
_default_mem_free (GstMemoryDefault * mem)
249
{
Wim Taymans's avatar
Wim Taymans committed
250 251 252 253 254
  if (mem->mem.parent)
    gst_memory_unref (mem->mem.parent);

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

Wim Taymans's avatar
Wim Taymans committed
256
  g_slice_free1 (mem->slice_size, mem);
257 258
}

Wim Taymans's avatar
Wim Taymans committed
259
static GstMemoryDefault *
260
_default_mem_copy (GstMemoryDefault * mem, gssize offset, gsize size)
261 262 263
{
  GstMemoryDefault *copy;

Wim Taymans's avatar
Wim Taymans committed
264 265
  if (size == -1)
    size = mem->size > offset ? mem->size - offset : 0;
Wim Taymans's avatar
Wim Taymans committed
266

267 268
  copy = _default_mem_new_block (mem->maxsize, 0, mem->offset + offset, size);
  memcpy (copy->data, mem->data, mem->maxsize);
269

Wim Taymans's avatar
Wim Taymans committed
270
  return copy;
271 272
}

Wim Taymans's avatar
Wim Taymans committed
273
static GstMemoryDefault *
274
_default_mem_share (GstMemoryDefault * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
275 276 277 278 279 280 281 282
{
  GstMemoryDefault *sub;
  GstMemory *parent;

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

283 284 285
  if (size == -1)
    size = mem->size - offset;

286
  sub = _default_mem_new (parent->flags, parent, mem->data, NULL, mem->maxsize,
Wim Taymans's avatar
Wim Taymans committed
287 288 289
      mem->offset + offset, size);

  return sub;
290 291
}

Wim Taymans's avatar
Wim Taymans committed
292
static gboolean
Wim Taymans's avatar
Wim Taymans committed
293 294
_default_mem_is_span (GstMemoryDefault * mem1, GstMemoryDefault * mem2,
    gsize * offset)
295
{
Wim Taymans's avatar
Wim Taymans committed
296 297 298 299 300 301 302 303

  if (offset) {
    GstMemoryDefault *parent;

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

    *offset = mem1->offset - parent->offset;
  }
304

Wim Taymans's avatar
Wim Taymans committed
305
  /* and memory is contiguous */
Wim Taymans's avatar
Wim Taymans committed
306
  return mem1->data + mem1->offset + mem1->size == mem2->data + mem2->offset;
307 308 309
}

static GstMemory *
310
_fallback_copy (GstMemory * mem, gssize offset, gssize size)
311
{
312 313
  GstMemory *copy;
  guint8 *data, *dest;
314
  gsize msize;
315

316
  data = gst_memory_map (mem, &msize, NULL, GST_MAP_READ);
Wim Taymans's avatar
Wim Taymans committed
317 318
  if (size == -1)
    size = msize > offset ? msize - offset : 0;
319
  /* use the same allocator as the memory we copy, FIXME, alignment?  */
Wim Taymans's avatar
Wim Taymans committed
320
  copy = gst_allocator_alloc (mem->allocator, size, 0);
321 322 323 324
  dest = gst_memory_map (copy, NULL, NULL, GST_MAP_WRITE);
  memcpy (dest, data + offset, size);
  gst_memory_unmap (copy, dest, size);

325
  gst_memory_unmap (mem, data, msize);
326

Wim Taymans's avatar
Wim Taymans committed
327
  return (GstMemory *) copy;
328 329 330
}

static gboolean
Wim Taymans's avatar
Wim Taymans committed
331
_fallback_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
332 333 334 335
{
  return FALSE;
}

336
static GStaticRWLock lock = G_STATIC_RW_LOCK_INIT;
337
static GHashTable *allocators;
338

Wim Taymans's avatar
Wim Taymans committed
339
void
Wim Taymans's avatar
Wim Taymans committed
340
_priv_gst_memory_initialize (void)
Wim Taymans's avatar
Wim Taymans committed
341 342
{
  static const GstMemoryInfo _mem_info = {
343
    (GstMemoryAllocFunction) _default_mem_alloc,
Wim Taymans's avatar
Wim Taymans committed
344
    (GstMemoryGetSizesFunction) _default_mem_get_sizes,
Wim Taymans's avatar
Wim Taymans committed
345
    (GstMemoryResizeFunction) _default_mem_resize,
Wim Taymans's avatar
Wim Taymans committed
346 347 348 349
    (GstMemoryMapFunction) _default_mem_map,
    (GstMemoryUnmapFunction) _default_mem_unmap,
    (GstMemoryFreeFunction) _default_mem_free,
    (GstMemoryCopyFunction) _default_mem_copy,
Wim Taymans's avatar
Wim Taymans committed
350
    (GstMemoryShareFunction) _default_mem_share,
351 352
    (GstMemoryIsSpanFunction) _default_mem_is_span,
    NULL
Wim Taymans's avatar
Wim Taymans committed
353 354
  };

355
  allocators = g_hash_table_new (g_str_hash, g_str_equal);
356

357 358
#ifdef HAVE_GETPAGESIZE
#ifdef MEMORY_ALIGNMENT_PAGESIZE
Wim Taymans's avatar
Wim Taymans committed
359
  gst_memory_alignment = getpagesize () - 1;
360 361 362
#endif
#endif

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

365
  _default_mem_impl = gst_allocator_register (GST_ALLOCATOR_SYSMEM, &_mem_info);
366

367
  _default_allocator = _default_mem_impl;
368 369
}

Wim Taymans's avatar
Wim Taymans committed
370 371 372 373 374 375 376 377 378 379 380 381 382
/**
 * 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
383 384 385
GstMemory *
gst_memory_new_wrapped (GstMemoryFlags flags, gpointer data,
    GFreeFunc free_func, gsize maxsize, gsize offset, gsize size)
386
{
Wim Taymans's avatar
Wim Taymans committed
387
  GstMemoryDefault *mem;
Wim Taymans's avatar
Wim Taymans committed
388

Wim Taymans's avatar
Wim Taymans committed
389 390 391
  g_return_val_if_fail (data != NULL, NULL);
  g_return_val_if_fail (offset + size <= maxsize, NULL);

Wim Taymans's avatar
Wim Taymans committed
392 393 394 395 396 397 398 399 400
  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
401 402
 * Increases the refcount of @mem.
 *
Wim Taymans's avatar
Wim Taymans committed
403 404
 * Returns: @mem with increased refcount
 */
405 406 407 408 409 410 411 412 413 414
GstMemory *
gst_memory_ref (GstMemory * mem)
{
  g_return_val_if_fail (mem != NULL, NULL);

  g_atomic_int_inc (&mem->refcount);

  return mem;
}

Wim Taymans's avatar
Wim Taymans committed
415 416 417
/**
 * gst_memory_unref:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
418 419 420
 *
 * 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
421
 */
422 423 424 425
void
gst_memory_unref (GstMemory * mem)
{
  g_return_if_fail (mem != NULL);
426
  g_return_if_fail (mem->allocator != NULL);
427 428

  if (g_atomic_int_dec_and_test (&mem->refcount))
429
    mem->allocator->info.free (mem);
430 431
}

Wim Taymans's avatar
Wim Taymans committed
432 433 434
/**
 * gst_memory_get_sizes:
 * @mem: a #GstMemory
435
 * @offset: pointer to offset
Wim Taymans's avatar
Wim Taymans committed
436 437
 * @maxsize: pointer to maxsize
 *
438
 * Get the current @size, @offset and @maxsize of @mem.
Wim Taymans's avatar
Wim Taymans committed
439 440
 *
 * Returns: the current sizes of @mem
Wim Taymans's avatar
Wim Taymans committed
441
 */
442
gsize
443
gst_memory_get_sizes (GstMemory * mem, gsize * offset, gsize * maxsize)
444 445 446
{
  g_return_val_if_fail (mem != NULL, 0);

447
  return mem->allocator->info.get_sizes (mem, offset, maxsize);
448 449
}

Wim Taymans's avatar
Wim Taymans committed
450 451 452 453 454 455 456 457 458 459
/**
 * 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
460
gst_memory_resize (GstMemory * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
461 462
{
  g_return_if_fail (mem != NULL);
463
  g_return_if_fail (gst_memory_is_writable (mem));
Wim Taymans's avatar
Wim Taymans committed
464

465
  mem->allocator->info.resize (mem, offset, size);
Wim Taymans's avatar
Wim Taymans committed
466 467
}

468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
/**
 * 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
486 487 488
/**
 * gst_memory_map:
 * @mem: a #GstMemory
489 490
 * @size: (out) (allow-none): pointer for size
 * @maxsize: (out) (allow-none): pointer for maxsize
Wim Taymans's avatar
Wim Taymans committed
491 492 493 494 495 496 497
 * @flags: mapping flags
 *
 * Get a pointer to the memory of @mem that can be accessed according to @flags.
 *
 * @size and @maxsize will contain the size of the memory and the maximum
 * allocated memory of @mem respectively. They can be set to NULL.
 *
498
 * Returns: (transfer none): a pointer to the memory of @mem.
Wim Taymans's avatar
Wim Taymans committed
499
 */
500 501 502 503
gpointer
gst_memory_map (GstMemory * mem, gsize * size, gsize * maxsize,
    GstMapFlags flags)
{
504 505
  gpointer res;
  gint access_mode, state, newstate;
506

507 508 509
  g_return_val_if_fail (mem != NULL, NULL);
  access_mode = flags & 3;
  g_return_val_if_fail (!(access_mode & GST_MAP_WRITE)
510
      || gst_memory_is_writable (mem), NULL);
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540

  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 */
      g_return_val_if_fail ((state & access_mode) == access_mode, NULL);
      /* increase refcount */
      newstate = state + 4;
    }
  } while (!g_atomic_int_compare_and_exchange (&mem->state, state, newstate));

  res = mem->allocator->info.map (mem, size, maxsize, flags);

  if (G_UNLIKELY (res == NULL)) {
    /* something went wrong, restore the orginal state again */
    do {
      state = g_atomic_int_get (&mem->state);
      /* there must be a ref */
      g_return_val_if_fail (state >= 4, NULL);
      /* 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));
  }
  return res;
541 542
}

Wim Taymans's avatar
Wim Taymans committed
543 544 545 546
/**
 * gst_memory_unmap:
 * @mem: a #GstMemory
 * @data: data to unmap
547
 * @size: new size of @mem, or -1
Wim Taymans's avatar
Wim Taymans committed
548 549 550 551 552
 *
 * Release the memory pointer obtained with gst_memory_map() and set the size of
 * the memory to @size. @size can be set to -1 when the size should not be
 * updated.
 *
553 554 555
 * It is possible to pass a different @data than that obtained from
 * gst_memory_map() in which case the offset of @mem will be updated.
 *
Wim Taymans's avatar
Wim Taymans committed
556 557
 * Returns: TRUE when the memory was release successfully.
 */
558
gboolean
559
gst_memory_unmap (GstMemory * mem, gpointer data, gssize size)
560
{
561 562 563
  gboolean need_unmap = TRUE;
  gint state, newstate;

564 565
  g_return_val_if_fail (mem != NULL, FALSE);

566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
  do {
    state = g_atomic_int_get (&mem->state);

    /* there must be a ref */
    g_return_val_if_fail (state >= 4, FALSE);

    if (need_unmap) {
      /* try to unmap, only do this once */
      if (!mem->allocator->info.unmap (mem, data, size))
        return FALSE;
      need_unmap = FALSE;
    }
    /* success, try to 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));

  return TRUE;
586 587
}

Wim Taymans's avatar
Wim Taymans committed
588 589 590 591
/**
 * gst_memory_copy:
 * @mem: a #GstMemory
 * @offset: an offset to copy
592
 * @size: size to copy or -1 to copy all bytes from offset
Wim Taymans's avatar
Wim Taymans committed
593 594 595 596 597 598 599
 *
 * 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.
 */
600
GstMemory *
601
gst_memory_copy (GstMemory * mem, gssize offset, gssize size)
602 603 604
{
  g_return_val_if_fail (mem != NULL, NULL);

605
  return mem->allocator->info.copy (mem, offset, size);
606 607
}

Wim Taymans's avatar
Wim Taymans committed
608 609 610 611
/**
 * gst_memory_share:
 * @mem: a #GstMemory
 * @offset: an offset to share
612
 * @size: size to share or -1 to share bytes from offset
Wim Taymans's avatar
Wim Taymans committed
613
 *
614 615 616 617
 * 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
618 619 620
 *
 * Returns: a new #GstMemory.
 */
621
GstMemory *
622
gst_memory_share (GstMemory * mem, gssize offset, gssize size)
623 624 625
{
  g_return_val_if_fail (mem != NULL, NULL);

626
  return mem->allocator->info.share (mem, offset, size);
627 628
}

Wim Taymans's avatar
Wim Taymans committed
629 630 631 632 633 634 635 636 637 638 639 640 641 642 643
/**
 * 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
644
gboolean
645
gst_memory_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
Wim Taymans's avatar
Wim Taymans committed
646 647 648 649
{
  g_return_val_if_fail (mem1 != NULL, FALSE);
  g_return_val_if_fail (mem2 != NULL, FALSE);

650 651
  /* need to have the same allocators */
  if (mem1->allocator != mem2->allocator)
Wim Taymans's avatar
Wim Taymans committed
652 653
    return FALSE;

654 655 656
  /* need to have the same parent */
  if (mem1->parent == NULL || mem1->parent != mem2->parent)
    return FALSE;
Wim Taymans's avatar
Wim Taymans committed
657

658
  /* and memory is contiguous */
659
  if (!mem1->allocator->info.is_span (mem1, mem2, offset))
660 661 662
    return FALSE;

  return TRUE;
Wim Taymans's avatar
Wim Taymans committed
663
}
664 665

/**
666
 * gst_allocator_register:
667 668 669 670 671 672
 * @name: the name of the allocator
 * @info: #GstMemoryInfo
 *
 * Registers the memory allocator with @name and implementation functions
 * @info.
 *
673
 * All functions in @info are mandatory exept the copy and is_span
674 675 676 677 678
 * functions, which will have a default implementation when left NULL.
 *
 * The user_data field in @info will be passed to all calls of the alloc
 * function.
 *
679
 * Returns: a new #GstAllocator.
680
 */
681 682
const GstAllocator *
gst_allocator_register (const gchar * name, const GstMemoryInfo * info)
683
{
684
  GstAllocator *allocator;
685 686 687 688 689 690

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

  g_return_val_if_fail (name != NULL, NULL);
  g_return_val_if_fail (info != NULL, NULL);
691
  g_return_val_if_fail (info->alloc != NULL, NULL);
692 693 694 695 696
  g_return_val_if_fail (info->get_sizes != NULL, NULL);
  g_return_val_if_fail (info->resize != NULL, NULL);
  g_return_val_if_fail (info->map != NULL, NULL);
  g_return_val_if_fail (info->unmap != NULL, NULL);
  g_return_val_if_fail (info->free != NULL, NULL);
697
  g_return_val_if_fail (info->share != NULL, NULL);
698

699
  allocator = g_slice_new (GstAllocator);
700 701 702 703 704 705
  allocator->name = g_quark_from_string (name);
  allocator->info = *info;
  INSTALL_FALLBACK (copy);
  INSTALL_FALLBACK (is_span);
#undef INSTALL_FALLBACK

Josep Torra's avatar
Josep Torra committed
706
  GST_DEBUG ("registering allocator \"%s\"", name);
707 708

  g_static_rw_lock_writer_lock (&lock);
709
  g_hash_table_insert (allocators, (gpointer) name, (gpointer) allocator);
710 711 712 713 714 715
  g_static_rw_lock_writer_unlock (&lock);

  return allocator;
}

/**
716
 * gst_allocator_find:
717 718
 * @name: the name of the allocator
 *
719 720
 * Find a previously registered allocator with @name. When @name is NULL, the
 * default allocator will be returned.
721
 *
722
 * Returns: a #GstAllocator or NULL when the allocator with @name was not
723 724
 * registered.
 */
725 726
const GstAllocator *
gst_allocator_find (const gchar * name)
727
{
728
  const GstAllocator *allocator;
729

730 731
  g_static_rw_lock_reader_lock (&lock);
  if (name) {
732
    allocator = g_hash_table_lookup (allocators, (gconstpointer) name);
733 734 735 736
  } else {
    allocator = _default_allocator;
  }
  g_static_rw_lock_reader_unlock (&lock);
737 738 739 740 741

  return allocator;
}

/**
742 743
 * gst_allocator_set_default:
 * @allocator: a #GstAllocator
744 745 746 747
 *
 * Set the default allocator.
 */
void
748
gst_allocator_set_default (const GstAllocator * allocator)
749 750 751
{
  g_return_if_fail (allocator != NULL);

752
  g_static_rw_lock_writer_lock (&lock);
753
  _default_allocator = allocator;
754
  g_static_rw_lock_writer_unlock (&lock);
755 756 757
}

/**
758
 * gst_allocator_alloc:
759
 * @allocator: (transfer none) (allow-none): a #GstAllocator to use
760 761 762 763 764 765
 * @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.
 *
766 767
 * When @allocator is NULL, the default allocator will be used.
 *
768 769 770
 * @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.
 *
771
 * Returns: (transfer full): a new #GstMemory.
772 773
 */
GstMemory *
774
gst_allocator_alloc (const GstAllocator * allocator, gsize maxsize, gsize align)
775 776 777 778 779 780
{
  g_return_val_if_fail (((align + 1) & align) == 0, NULL);

  if (allocator == NULL)
    allocator = _default_allocator;

781 782
  return allocator->info.alloc (allocator, maxsize, align,
      allocator->info.user_data);
783
}