gstmemory.c 21.1 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 70 71 72
#endif

/* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
 * with newer GLib versions (>= 2.31.0) */
#define GLIB_DISABLE_DEPRECATION_WARNINGS
73 74 75
#include "gst_private.h"
#include "gstmemory.h"

76 77
G_DEFINE_BOXED_TYPE (GstMemory, gst_memory, (GBoxedCopyFunc) gst_memory_ref,
    (GBoxedFreeFunc) gst_memory_unref);
Wim Taymans's avatar
Wim Taymans committed
78

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

97
struct _GstAllocator
98 99 100 101 102 103
{
  GQuark name;

  GstMemoryInfo info;
};

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 113 114 115
  guint8 *data;
  GFreeFunc free_func;
  gsize maxsize;
  gsize offset;
  gsize size;
} GstMemoryDefault;

116
/* the default allocator */
117
static const GstAllocator *_default_allocator;
118 119

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

Wim Taymans's avatar
Wim Taymans committed
122
/* initialize the fields */
Wim Taymans's avatar
Wim Taymans committed
123
static void
124 125 126
_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
127
{
128
  mem->mem.allocator = _default_mem_impl;
129
  mem->mem.flags = flags;
Wim Taymans's avatar
Wim Taymans committed
130
  mem->mem.refcount = 1;
Wim Taymans's avatar
Wim Taymans committed
131
  mem->mem.parent = parent ? gst_memory_ref (parent) : NULL;
132
  mem->mem.state = 0;
Wim Taymans's avatar
Wim Taymans committed
133 134 135 136 137 138 139 140
  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
141
/* create a new memory block that manages the given memory */
Wim Taymans's avatar
Wim Taymans committed
142
static GstMemoryDefault *
143
_default_mem_new (GstMemoryFlags flags, GstMemory * parent, gpointer data,
Wim Taymans's avatar
Wim Taymans committed
144 145 146
    GFreeFunc free_func, gsize maxsize, gsize offset, gsize size)
{
  GstMemoryDefault *mem;
Wim Taymans's avatar
Wim Taymans committed
147
  gsize slice_size;
Wim Taymans's avatar
Wim Taymans committed
148

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

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

  return mem;
}
157

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

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

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

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

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

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

  return mem;
}

188
static GstMemory *
189
_default_mem_alloc (const GstAllocator * allocator, gsize maxsize, gsize align)
190 191 192 193
{
  return (GstMemory *) _default_mem_new_block (maxsize, align, 0, maxsize);
}

194
static gsize
195
_default_mem_get_sizes (GstMemoryDefault * mem, gsize * offset, gsize * maxsize)
196
{
197 198
  if (offset)
    *offset = mem->offset;
Wim Taymans's avatar
Wim Taymans committed
199 200
  if (maxsize)
    *maxsize = mem->maxsize;
201

Wim Taymans's avatar
Wim Taymans committed
202 203 204 205
  return mem->size;
}

static void
206
_default_mem_resize (GstMemoryDefault * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
207
{
208
  g_return_if_fail (offset >= 0 || mem->offset >= -offset);
Wim Taymans's avatar
Wim Taymans committed
209
  g_return_if_fail (size + mem->offset + offset <= mem->maxsize);
Wim Taymans's avatar
Wim Taymans committed
210 211 212 213 214 215 216 217 218 219 220

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

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

Wim Taymans's avatar
Wim Taymans committed
224
  return mem->data + mem->offset;
225 226 227
}

static gboolean
Wim Taymans's avatar
Wim Taymans committed
228
_default_mem_unmap (GstMemoryDefault * mem, gpointer data, gsize size)
229
{
Matej's avatar
Matej committed
230 231 232 233
  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);
234

Wim Taymans's avatar
Wim Taymans committed
235 236 237
  g_return_val_if_fail ((guint8 *) data >= mem->data
      && (guint8 *) data < mem->data + mem->maxsize, FALSE);

Wim Taymans's avatar
Wim Taymans committed
238 239 240 241 242 243
  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
244

245
  if (size != -1) {
Wim Taymans's avatar
Wim Taymans committed
246
    g_return_val_if_fail (mem->offset + size <= mem->maxsize, FALSE);
Wim Taymans's avatar
Wim Taymans committed
247
    mem->size = size;
248
  }
Wim Taymans's avatar
Wim Taymans committed
249

Wim Taymans's avatar
Wim Taymans committed
250 251
  return TRUE;
}
252 253

static void
Wim Taymans's avatar
Wim Taymans committed
254
_default_mem_free (GstMemoryDefault * mem)
255
{
Wim Taymans's avatar
Wim Taymans committed
256 257 258 259 260
  if (mem->mem.parent)
    gst_memory_unref (mem->mem.parent);

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

Wim Taymans's avatar
Wim Taymans committed
262
  g_slice_free1 (mem->slice_size, mem);
263 264
}

Wim Taymans's avatar
Wim Taymans committed
265
static GstMemoryDefault *
266
_default_mem_copy (GstMemoryDefault * mem, gssize offset, gsize size)
267 268 269
{
  GstMemoryDefault *copy;

Wim Taymans's avatar
Wim Taymans committed
270 271
  if (size == -1)
    size = mem->size > offset ? mem->size - offset : 0;
Wim Taymans's avatar
Wim Taymans committed
272

273 274
  copy = _default_mem_new_block (mem->maxsize, 0, mem->offset + offset, size);
  memcpy (copy->data, mem->data, mem->maxsize);
275

Wim Taymans's avatar
Wim Taymans committed
276
  return copy;
277 278
}

Wim Taymans's avatar
Wim Taymans committed
279
static GstMemoryDefault *
280
_default_mem_share (GstMemoryDefault * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
281 282 283 284 285 286 287 288
{
  GstMemoryDefault *sub;
  GstMemory *parent;

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

289 290 291
  if (size == -1)
    size = mem->size - offset;

292
  sub = _default_mem_new (parent->flags, parent, mem->data, NULL, mem->maxsize,
Wim Taymans's avatar
Wim Taymans committed
293 294 295
      mem->offset + offset, size);

  return sub;
296 297
}

Wim Taymans's avatar
Wim Taymans committed
298
static gboolean
Wim Taymans's avatar
Wim Taymans committed
299 300
_default_mem_is_span (GstMemoryDefault * mem1, GstMemoryDefault * mem2,
    gsize * offset)
301
{
Wim Taymans's avatar
Wim Taymans committed
302 303 304 305 306 307 308 309

  if (offset) {
    GstMemoryDefault *parent;

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

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

Wim Taymans's avatar
Wim Taymans committed
311
  /* and memory is contiguous */
Wim Taymans's avatar
Wim Taymans committed
312
  return mem1->data + mem1->offset + mem1->size == mem2->data + mem2->offset;
313 314 315
}

static GstMemory *
316
_fallback_copy (GstMemory * mem, gssize offset, gssize size)
317
{
318 319
  GstMemory *copy;
  guint8 *data, *dest;
320
  gsize msize;
321

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

331
  gst_memory_unmap (mem, data, msize);
332

Wim Taymans's avatar
Wim Taymans committed
333
  return (GstMemory *) copy;
334 335 336
}

static gboolean
Wim Taymans's avatar
Wim Taymans committed
337
_fallback_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
338 339 340 341
{
  return FALSE;
}

342
static GStaticRWLock lock = G_STATIC_RW_LOCK_INIT;
343
static GHashTable *allocators;
344

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

361
  allocators = g_hash_table_new (g_str_hash, g_str_equal);
362

363 364
#ifdef HAVE_GETPAGESIZE
#ifdef MEMORY_ALIGNMENT_PAGESIZE
Wim Taymans's avatar
Wim Taymans committed
365
  gst_memory_alignment = getpagesize () - 1;
366 367 368
#endif
#endif

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

371
  _default_mem_impl = gst_allocator_register (GST_ALLOCATOR_SYSMEM, &_mem_info);
372

373
  _default_allocator = _default_mem_impl;
374 375
}

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

Wim Taymans's avatar
Wim Taymans committed
395 396 397
  g_return_val_if_fail (data != NULL, NULL);
  g_return_val_if_fail (offset + size <= maxsize, NULL);

Wim Taymans's avatar
Wim Taymans committed
398 399 400 401 402 403 404 405 406
  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
407 408
 * Increases the refcount of @mem.
 *
Wim Taymans's avatar
Wim Taymans committed
409 410
 * Returns: @mem with increased refcount
 */
411 412 413 414 415 416 417 418 419 420
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
421 422 423
/**
 * gst_memory_unref:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
424 425 426
 *
 * 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
427
 */
428 429 430 431
void
gst_memory_unref (GstMemory * mem)
{
  g_return_if_fail (mem != NULL);
432
  g_return_if_fail (mem->allocator != NULL);
433 434

  if (g_atomic_int_dec_and_test (&mem->refcount))
435
    mem->allocator->info.free (mem);
436 437
}

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

453
  return mem->allocator->info.get_sizes (mem, offset, maxsize);
454 455
}

Wim Taymans's avatar
Wim Taymans committed
456 457 458 459 460 461 462 463 464 465
/**
 * 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
466
gst_memory_resize (GstMemory * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
467 468
{
  g_return_if_fail (mem != NULL);
469
  g_return_if_fail (gst_memory_is_writable (mem));
Wim Taymans's avatar
Wim Taymans committed
470

471
  mem->allocator->info.resize (mem, offset, size);
Wim Taymans's avatar
Wim Taymans committed
472 473
}

474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
/**
 * 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
492 493 494
/**
 * gst_memory_map:
 * @mem: a #GstMemory
495 496
 * @size: (out) (allow-none): pointer for size
 * @maxsize: (out) (allow-none): pointer for maxsize
Wim Taymans's avatar
Wim Taymans committed
497 498 499 500 501 502 503
 * @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.
 *
504
 * Returns: (transfer none): a pointer to the memory of @mem.
Wim Taymans's avatar
Wim Taymans committed
505
 */
506 507 508 509
gpointer
gst_memory_map (GstMemory * mem, gsize * size, gsize * maxsize,
    GstMapFlags flags)
{
510 511
  gpointer res;
  gint access_mode, state, newstate;
512

513 514 515
  g_return_val_if_fail (mem != NULL, NULL);
  access_mode = flags & 3;
  g_return_val_if_fail (!(access_mode & GST_MAP_WRITE)
516
      || gst_memory_is_writable (mem), NULL);
517 518 519 520 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

  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;
547 548
}

Wim Taymans's avatar
Wim Taymans committed
549 550 551 552
/**
 * gst_memory_unmap:
 * @mem: a #GstMemory
 * @data: data to unmap
553
 * @size: new size of @mem, or -1
Wim Taymans's avatar
Wim Taymans committed
554 555 556 557 558
 *
 * 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.
 *
559 560 561
 * 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
562 563
 * Returns: TRUE when the memory was release successfully.
 */
564
gboolean
565
gst_memory_unmap (GstMemory * mem, gpointer data, gssize size)
566
{
567 568 569
  gboolean need_unmap = TRUE;
  gint state, newstate;

570 571
  g_return_val_if_fail (mem != NULL, FALSE);

572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591
  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;
592 593
}

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

611
  return mem->allocator->info.copy (mem, offset, size);
612 613
}

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

632
  return mem->allocator->info.share (mem, offset, size);
633 634
}

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

656 657
  /* need to have the same allocators */
  if (mem1->allocator != mem2->allocator)
Wim Taymans's avatar
Wim Taymans committed
658 659
    return FALSE;

660 661 662
  /* need to have the same parent */
  if (mem1->parent == NULL || mem1->parent != mem2->parent)
    return FALSE;
Wim Taymans's avatar
Wim Taymans committed
663

664
  /* and memory is contiguous */
665
  if (!mem1->allocator->info.is_span (mem1, mem2, offset))
666 667 668
    return FALSE;

  return TRUE;
Wim Taymans's avatar
Wim Taymans committed
669
}
670 671

/**
672
 * gst_allocator_register:
673 674 675 676 677 678
 * @name: the name of the allocator
 * @info: #GstMemoryInfo
 *
 * Registers the memory allocator with @name and implementation functions
 * @info.
 *
679
 * All functions in @info are mandatory exept the copy and is_span
680 681 682 683 684
 * 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.
 *
685
 * Returns: a new #GstAllocator.
686
 */
687 688
const GstAllocator *
gst_allocator_register (const gchar * name, const GstMemoryInfo * info)
689
{
690
  GstAllocator *allocator;
691 692 693 694 695 696

#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);
697
  g_return_val_if_fail (info->alloc != NULL, NULL);
698 699 700 701 702
  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);
703
  g_return_val_if_fail (info->share != NULL, NULL);
704

705
  allocator = g_slice_new (GstAllocator);
706 707 708 709 710 711
  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
712
  GST_DEBUG ("registering allocator \"%s\"", name);
713 714

  g_static_rw_lock_writer_lock (&lock);
715
  g_hash_table_insert (allocators, (gpointer) name, (gpointer) allocator);
716 717 718 719 720 721
  g_static_rw_lock_writer_unlock (&lock);

  return allocator;
}

/**
722
 * gst_allocator_find:
723 724
 * @name: the name of the allocator
 *
725 726
 * Find a previously registered allocator with @name. When @name is NULL, the
 * default allocator will be returned.
727
 *
728
 * Returns: a #GstAllocator or NULL when the allocator with @name was not
729 730
 * registered.
 */
731 732
const GstAllocator *
gst_allocator_find (const gchar * name)
733
{
734
  const GstAllocator *allocator;
735

736 737
  g_static_rw_lock_reader_lock (&lock);
  if (name) {
738
    allocator = g_hash_table_lookup (allocators, (gconstpointer) name);
739 740 741 742
  } else {
    allocator = _default_allocator;
  }
  g_static_rw_lock_reader_unlock (&lock);
743 744 745 746 747

  return allocator;
}

/**
748 749
 * gst_allocator_set_default:
 * @allocator: a #GstAllocator
750 751 752 753
 *
 * Set the default allocator.
 */
void
754
gst_allocator_set_default (const GstAllocator * allocator)
755 756 757
{
  g_return_if_fail (allocator != NULL);

758
  g_static_rw_lock_writer_lock (&lock);
759
  _default_allocator = allocator;
760
  g_static_rw_lock_writer_unlock (&lock);
761 762 763
}

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

  if (allocator == NULL)
    allocator = _default_allocator;

787 788
  return allocator->info.alloc (allocator, maxsize, align,
      allocator->info.user_data);
789
}