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

76 77 78
G_DEFINE_BOXED_TYPE (GstAllocator, gst_allocator,
    (GBoxedCopyFunc) gst_allocator_ref, (GBoxedFreeFunc) gst_allocator_unref);

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
  gint refcount;
100 101

  GstMemoryInfo info;
102 103 104

  gpointer user_data;
  GDestroyNotify notify;
105 106
};

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

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

/* our predefined allocators */
120
static 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;
Wim Taymans's avatar
Wim Taymans committed
132 133 134 135
  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
136 137 138
  mem->slice_size = slice_size;
  mem->data = data;
  mem->free_func = free_func;
Wim Taymans's avatar
Wim Taymans committed
139 140

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

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

Wim Taymans's avatar
Wim Taymans committed
151 152 153
  slice_size = sizeof (GstMemoryDefault);

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

  return mem;
}
159

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

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

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

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

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

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

  return mem;
}

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

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

static gboolean
Wim Taymans's avatar
Wim Taymans committed
204
_default_mem_unmap (GstMemoryDefault * mem)
205
{
Wim Taymans's avatar
Wim Taymans committed
206 207
  return TRUE;
}
208 209

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

Wim Taymans's avatar
Wim Taymans committed
214 215 216 217 218
  if (mem->mem.parent)
    gst_memory_unref (mem->mem.parent);

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

Wim Taymans's avatar
Wim Taymans committed
220
  g_slice_free1 (mem->slice_size, mem);
221 222
}

Wim Taymans's avatar
Wim Taymans committed
223
static GstMemoryDefault *
224
_default_mem_copy (GstMemoryDefault * mem, gssize offset, gsize size)
225 226 227
{
  GstMemoryDefault *copy;

Wim Taymans's avatar
Wim Taymans committed
228
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
229
    size = mem->mem.size > offset ? mem->mem.size - offset : 0;
Wim Taymans's avatar
Wim Taymans committed
230

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

Wim Taymans's avatar
Wim Taymans committed
236
  return copy;
237 238
}

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

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

249
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
250
    size = mem->mem.size - offset;
251

Wim Taymans's avatar
Wim Taymans committed
252 253 254
  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
255 256

  return sub;
257 258
}

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

  if (offset) {
    GstMemoryDefault *parent;

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

Wim Taymans's avatar
Wim Taymans committed
269
    *offset = mem1->mem.offset - parent->mem.offset;
Wim Taymans's avatar
Wim Taymans committed
270
  }
271

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

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

Wim Taymans's avatar
Wim Taymans committed
283
  if (!gst_memory_map (mem, &sinfo, GST_MAP_READ))
Wim Taymans's avatar
Wim Taymans committed
284
    return NULL;
Wim Taymans's avatar
Wim Taymans committed
285

Wim Taymans's avatar
Wim Taymans committed
286
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
287 288
    size = sinfo.size > offset ? sinfo.size - offset : 0;

Wim Taymans's avatar
Wim Taymans committed
289 290
  /* 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
291 292 293 294 295 296
  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;
  }

297
  memcpy (dinfo.data, sinfo.data + offset, size);
Wim Taymans's avatar
Wim Taymans committed
298 299
  gst_memory_unmap (copy, &dinfo);
  gst_memory_unmap (mem, &sinfo);
300

Wim Taymans's avatar
Wim Taymans committed
301
  return copy;
302 303 304
}

static gboolean
305
_fallback_mem_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
306 307 308 309
{
  return FALSE;
}

Wim Taymans's avatar
Wim Taymans committed
310
static GRWLock lock;
311
static GHashTable *allocators;
312

313 314 315 316 317 318
static void
_priv_sysmem_notify (gpointer user_data)
{
  g_warning ("The default memory allocator was freed!");
}

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

Wim Taymans's avatar
Wim Taymans committed
332
  g_rw_lock_init (&lock);
333
  allocators = g_hash_table_new (g_str_hash, g_str_equal);
334

335 336
#ifdef HAVE_GETPAGESIZE
#ifdef MEMORY_ALIGNMENT_PAGESIZE
Wim Taymans's avatar
Wim Taymans committed
337
  gst_memory_alignment = getpagesize () - 1;
338 339 340
#endif
#endif

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

343
  _default_mem_impl = gst_allocator_new (&_mem_info, NULL, _priv_sysmem_notify);
344

345 346 347
  _default_allocator = gst_allocator_ref (_default_mem_impl);
  gst_allocator_register (GST_ALLOCATOR_SYSMEM,
      gst_allocator_ref (_default_mem_impl));
348 349
}

Wim Taymans's avatar
Wim Taymans committed
350 351 352 353 354 355 356 357 358 359 360 361 362
/**
 * 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
363 364 365
GstMemory *
gst_memory_new_wrapped (GstMemoryFlags flags, gpointer data,
    GFreeFunc free_func, gsize maxsize, gsize offset, gsize size)
366
{
Wim Taymans's avatar
Wim Taymans committed
367
  GstMemoryDefault *mem;
Wim Taymans's avatar
Wim Taymans committed
368

Wim Taymans's avatar
Wim Taymans committed
369 370 371
  g_return_val_if_fail (data != NULL, NULL);
  g_return_val_if_fail (offset + size <= maxsize, NULL);

Wim Taymans's avatar
Wim Taymans committed
372 373 374 375 376 377 378 379 380
  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
381 382
 * Increases the refcount of @mem.
 *
Wim Taymans's avatar
Wim Taymans committed
383 384
 * Returns: @mem with increased refcount
 */
385 386 387 388 389
GstMemory *
gst_memory_ref (GstMemory * mem)
{
  g_return_val_if_fail (mem != NULL, NULL);

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

392 393 394 395 396
  g_atomic_int_inc (&mem->refcount);

  return mem;
}

Wim Taymans's avatar
Wim Taymans committed
397 398 399
/**
 * gst_memory_unref:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
400 401 402
 *
 * 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
403
 */
404 405 406 407
void
gst_memory_unref (GstMemory * mem)
{
  g_return_if_fail (mem != NULL);
408
  g_return_if_fail (mem->allocator != NULL);
409

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

412
  if (g_atomic_int_dec_and_test (&mem->refcount))
413
    mem->allocator->info.mem_free (mem);
414 415
}

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

Wim Taymans's avatar
Wim Taymans committed
431 432 433 434 435 436
  if (offset)
    *offset = mem->offset;
  if (maxsize)
    *maxsize = mem->maxsize;

  return mem->size;
437 438
}

Wim Taymans's avatar
Wim Taymans committed
439 440 441 442 443 444 445 446 447 448
/**
 * 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
449
gst_memory_resize (GstMemory * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
450 451
{
  g_return_if_fail (mem != NULL);
452
  g_return_if_fail (gst_memory_is_writable (mem));
Wim Taymans's avatar
Wim Taymans committed
453 454
  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
455

Wim Taymans's avatar
Wim Taymans committed
456 457
  mem->offset += offset;
  mem->size = size;
Wim Taymans's avatar
Wim Taymans committed
458 459
}

460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
/**
 * 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
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 521 522 523
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));
}

524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549

/**
 * 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
550 551
    gst_memory_unref (mem);

552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568
    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
569
    gst_memory_unref (result);
570 571 572 573
    return NULL;
  }
}

Wim Taymans's avatar
Wim Taymans committed
574 575 576
/**
 * gst_memory_map:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
577
 * @info: (out): pointer for info
Wim Taymans's avatar
Wim Taymans committed
578 579
 * @flags: mapping flags
 *
Wim Taymans's avatar
Wim Taymans committed
580 581
 * 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
582
 *
Wim Taymans's avatar
Wim Taymans committed
583
 * This function can return %FALSE for various reasons:
Wim Taymans's avatar
Wim Taymans committed
584 585 586
 * - 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
587
 * @info and its contents remains valid for as long as @mem is alive and until
Wim Taymans's avatar
Wim Taymans committed
588 589 590 591 592
 * 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
593
 * Returns: %TRUE if the map operation was successful.
Wim Taymans's avatar
Wim Taymans committed
594
 */
Wim Taymans's avatar
Wim Taymans committed
595 596
gboolean
gst_memory_map (GstMemory * mem, GstMapInfo * info, GstMapFlags flags)
597
{
Wim Taymans's avatar
Wim Taymans committed
598 599
  g_return_val_if_fail (mem != NULL, FALSE);
  g_return_val_if_fail (info != NULL, FALSE);
600

Wim Taymans's avatar
Wim Taymans committed
601 602 603
  if (!gst_memory_lock (mem, flags))
    goto lock_failed;

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

Wim Taymans's avatar
Wim Taymans committed
606
  if (G_UNLIKELY (info->data == NULL))
Wim Taymans's avatar
Wim Taymans committed
607 608
    goto error;

Wim Taymans's avatar
Wim Taymans committed
609
  info->memory = mem;
Wim Taymans's avatar
Wim Taymans committed
610
  info->flags = flags;
Wim Taymans's avatar
Wim Taymans committed
611 612
  info->size = mem->size;
  info->maxsize = mem->maxsize - mem->offset;
613
  info->data = info->data + mem->offset;
614

Wim Taymans's avatar
Wim Taymans committed
615
  return TRUE;
616

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

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

648
  mem->allocator->info.mem_unmap (mem);
Wim Taymans's avatar
Wim Taymans committed
649
  gst_memory_unlock (mem);
650 651
}

Wim Taymans's avatar
Wim Taymans committed
652 653 654 655
/**
 * gst_memory_copy:
 * @mem: a #GstMemory
 * @offset: an offset to copy
656
 * @size: size to copy or -1 to copy all bytes from offset
Wim Taymans's avatar
Wim Taymans committed
657 658 659 660 661 662 663
 *
 * 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.
 */
664
GstMemory *
665
gst_memory_copy (GstMemory * mem, gssize offset, gssize size)
666
{
Wim Taymans's avatar
Wim Taymans committed
667 668
  GstMemory *copy;

669
  g_return_val_if_fail (mem != NULL, NULL);
Wim Taymans's avatar
Wim Taymans committed
670 671
  g_return_val_if_fail (gst_memory_lock (mem, GST_MAP_READ), NULL);

672
  copy = mem->allocator->info.mem_copy (mem, offset, size);
673

Wim Taymans's avatar
Wim Taymans committed
674 675 676
  gst_memory_unlock (mem);

  return copy;
677 678
}

Wim Taymans's avatar
Wim Taymans committed
679 680 681 682
/**
 * gst_memory_share:
 * @mem: a #GstMemory
 * @offset: an offset to share
683
 * @size: size to share or -1 to share bytes from offset
Wim Taymans's avatar
Wim Taymans committed
684
 *
685 686 687 688
 * 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
689 690 691
 *
 * Returns: a new #GstMemory.
 */
692
GstMemory *
693
gst_memory_share (GstMemory * mem, gssize offset, gssize size)
694 695 696
{
  g_return_val_if_fail (mem != NULL, NULL);

697
  return mem->allocator->info.mem_share (mem, offset, size);
698 699
}

Wim Taymans's avatar
Wim Taymans committed
700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
/**
 * 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
715
gboolean
716
gst_memory_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
Wim Taymans's avatar
Wim Taymans committed
717 718 719 720
{
  g_return_val_if_fail (mem1 != NULL, FALSE);
  g_return_val_if_fail (mem2 != NULL, FALSE);

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

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

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

  return TRUE;
Wim Taymans's avatar
Wim Taymans committed
734
}
735 736

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

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

  g_return_val_if_fail (info != NULL, NULL);
762
  g_return_val_if_fail (info->alloc != NULL, NULL);
763 764 765 766
  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);
767

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

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 837 838 839
  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);
840

Wim Taymans's avatar
Wim Taymans committed
841
  g_rw_lock_writer_lock (&lock);
842
  g_hash_table_insert (allocators, (gpointer) name, (gpointer) allocator);
Wim Taymans's avatar
Wim Taymans committed
843
  g_rw_lock_writer_unlock (&lock);
844 845 846
}

/**
847
 * gst_allocator_find:
848 849
 * @name: the name of the allocator
 *
850 851
 * Find a previously registered allocator with @name. When @name is NULL, the
 * default allocator will be returned.
852
 *
853 854
 * 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.
855
 */
856
GstAllocator *
857
gst_allocator_find (const gchar * name)
858
{
859
  GstAllocator *allocator;
860

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

  return allocator;
}

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

Wim Taymans's avatar
Wim Taymans committed
886
  g_rw_lock_writer_lock (&lock);
887
  old = _default_allocator;
888
  _default_allocator = allocator;
Wim Taymans's avatar
Wim Taymans committed
889
  g_rw_lock_writer_unlock (&lock);
890 891 892

  if (old)
    gst_allocator_unref (old);
893 894 895
}

/**
896
 * gst_allocator_alloc:
897
 * @allocator: (transfer none) (allow-none): a #GstAllocator to use
898 899 900 901 902 903
 * @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.
 *
904 905
 * When @allocator is NULL, the default allocator will be used.
 *
906 907 908
 * @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.
 *
909
 * Returns: (transfer full): a new #GstMemory.
910 911
 */
GstMemory *
912
gst_allocator_alloc (GstAllocator * allocator, gsize maxsize, gsize align)
913 914 915 916 917 918
{
  g_return_val_if_fail (((align + 1) & align) == 0, NULL);

  if (allocator == NULL)
    allocator = _default_allocator;

919
  return allocator->info.alloc (allocator, maxsize, align,
920
      allocator->user_data);
921
}