gstmemory.c 20.9 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 97 98 99 100
{
  GQuark name;

  GstMemoryInfo info;
};

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

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

/* our predefined allocators */
117
static const 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;
129
  mem->mem.state = 0;
Wim Taymans's avatar
Wim Taymans committed
130 131 132 133 134 135 136 137
  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
138
/* create a new memory block that manages the given memory */
Wim Taymans's avatar
Wim Taymans committed
139
static GstMemoryDefault *
140
_default_mem_new (GstMemoryFlags flags, GstMemory * parent, gpointer data,
Wim Taymans's avatar
Wim Taymans committed
141 142 143
    GFreeFunc free_func, gsize maxsize, gsize offset, gsize size)
{
  GstMemoryDefault *mem;
Wim Taymans's avatar
Wim Taymans committed
144
  gsize slice_size;
Wim Taymans's avatar
Wim Taymans committed
145

Wim Taymans's avatar
Wim Taymans committed
146 147 148
  slice_size = sizeof (GstMemoryDefault);

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

  return mem;
}
154

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

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

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

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

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

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

  return mem;
}

185
static GstMemory *
186
_default_mem_alloc (const GstAllocator * allocator, gsize maxsize, gsize align)
187 188 189 190
{
  return (GstMemory *) _default_mem_new_block (maxsize, align, 0, maxsize);
}

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

Wim Taymans's avatar
Wim Taymans committed
199 200 201 202
  return mem->size;
}

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

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

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

Wim Taymans's avatar
Wim Taymans committed
221
  return mem->data + mem->offset;
222 223 224
}

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

Wim Taymans's avatar
Wim Taymans committed
232 233 234
  g_return_val_if_fail ((guint8 *) data >= mem->data
      && (guint8 *) data < mem->data + mem->maxsize, FALSE);

Wim Taymans's avatar
Wim Taymans committed
235 236 237 238 239 240
  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
241

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

Wim Taymans's avatar
Wim Taymans committed
247 248
  return TRUE;
}
249 250

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

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

Wim Taymans's avatar
Wim Taymans committed
259
  g_slice_free1 (mem->slice_size, mem);
260 261
}

Wim Taymans's avatar
Wim Taymans committed
262
static GstMemoryDefault *
263
_default_mem_copy (GstMemoryDefault * mem, gssize offset, gsize size)
264 265 266
{
  GstMemoryDefault *copy;

Wim Taymans's avatar
Wim Taymans committed
267 268
  if (size == -1)
    size = mem->size > offset ? mem->size - offset : 0;
Wim Taymans's avatar
Wim Taymans committed
269

270 271
  copy = _default_mem_new_block (mem->maxsize, 0, mem->offset + offset, size);
  memcpy (copy->data, mem->data, mem->maxsize);
272

Wim Taymans's avatar
Wim Taymans committed
273
  return copy;
274 275
}

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

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

286 287 288
  if (size == -1)
    size = mem->size - offset;

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

  return sub;
293 294
}

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

  if (offset) {
    GstMemoryDefault *parent;

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

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

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

static GstMemory *
313
_fallback_copy (GstMemory * mem, gssize offset, gssize size)
314
{
315 316
  GstMemory *copy;
  guint8 *data, *dest;
317
  gsize msize;
318

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

328
  gst_memory_unmap (mem, data, msize);
329

Wim Taymans's avatar
Wim Taymans committed
330
  return (GstMemory *) copy;
331 332 333
}

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

Wim Taymans's avatar
Wim Taymans committed
339
static GRWLock lock;
340
static GHashTable *allocators;
341

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

Wim Taymans's avatar
Wim Taymans committed
358
  g_rw_lock_init (&lock);
359
  allocators = g_hash_table_new (g_str_hash, g_str_equal);
360

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

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

369
  _default_mem_impl = gst_allocator_register (GST_ALLOCATOR_SYSMEM, &_mem_info);
370

371
  _default_allocator = _default_mem_impl;
372 373
}

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

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

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

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

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

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

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

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

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

511 512 513
  g_return_val_if_fail (mem != NULL, NULL);
  access_mode = flags & 3;
  g_return_val_if_fail (!(access_mode & GST_MAP_WRITE)
514
      || gst_memory_is_writable (mem), NULL);
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 541 542 543 544

  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;
545 546
}

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

568 569
  g_return_val_if_fail (mem != NULL, FALSE);

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

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

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

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

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

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

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

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

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

  return TRUE;
Wim Taymans's avatar
Wim Taymans committed
667
}
668 669

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

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

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

Wim Taymans's avatar
Wim Taymans committed
712
  g_rw_lock_writer_lock (&lock);
713
  g_hash_table_insert (allocators, (gpointer) name, (gpointer) allocator);
Wim Taymans's avatar
Wim Taymans committed
714
  g_rw_lock_writer_unlock (&lock);
715 716 717 718 719

  return allocator;
}

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

Wim Taymans's avatar
Wim Taymans committed
734
  g_rw_lock_reader_lock (&lock);
735
  if (name) {
736
    allocator = g_hash_table_lookup (allocators, (gconstpointer) name);
737 738 739
  } else {
    allocator = _default_allocator;
  }
Wim Taymans's avatar
Wim Taymans committed
740
  g_rw_lock_reader_unlock (&lock);
741 742 743 744 745

  return allocator;
}

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

Wim Taymans's avatar
Wim Taymans committed
756
  g_rw_lock_writer_lock (&lock);
757
  _default_allocator = allocator;
Wim Taymans's avatar
Wim Taymans committed
758
  g_rw_lock_writer_unlock (&lock);
759 760 761
}

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

  if (allocator == NULL)
    allocator = _default_allocator;

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