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

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

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

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

73 74 75 76 77 78
#ifndef GST_DISABLE_TRACE
#include "gsttrace.h"
static GstAllocTrace *_gst_memory_trace;
static GstAllocTrace *_gst_allocator_trace;
#endif

79 80
G_DEFINE_BOXED_TYPE (GstMemory, gst_memory, (GBoxedCopyFunc) gst_memory_ref,
    (GBoxedFreeFunc) gst_memory_unref);
Wim Taymans's avatar
Wim Taymans committed
81

82 83 84
G_DEFINE_BOXED_TYPE (GstAllocator, gst_allocator,
    (GBoxedCopyFunc) gst_allocator_ref, (GBoxedFreeFunc) gst_allocator_unref);

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

103
struct _GstAllocator
104
{
105
  gint refcount;
106 107

  GstMemoryInfo info;
108 109 110

  gpointer user_data;
  GDestroyNotify notify;
111 112
};

Wim Taymans's avatar
Wim Taymans committed
113
/* default memory implementation */
114 115 116
typedef struct
{
  GstMemory mem;
Wim Taymans's avatar
Wim Taymans committed
117
  gsize slice_size;
118 119 120 121
  guint8 *data;
  GFreeFunc free_func;
} GstMemoryDefault;

122
/* the default allocator */
123
static GstAllocator *_default_allocator;
124 125

/* our predefined allocators */
126
static GstAllocator *_default_mem_impl;
Wim Taymans's avatar
Wim Taymans committed
127

Wim Taymans's avatar
Wim Taymans committed
128
/* initialize the fields */
Wim Taymans's avatar
Wim Taymans committed
129
static void
130 131 132
_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
133
{
134
  mem->mem.allocator = _default_mem_impl;
135
  mem->mem.flags = flags;
Wim Taymans's avatar
Wim Taymans committed
136
  mem->mem.refcount = 1;
Wim Taymans's avatar
Wim Taymans committed
137
  mem->mem.parent = parent ? gst_memory_ref (parent) : NULL;
138
  mem->mem.state = (flags & GST_MEMORY_FLAG_READONLY ? 0x1 : 0);
Wim Taymans's avatar
Wim Taymans committed
139 140 141
  mem->mem.maxsize = maxsize;
  mem->mem.offset = offset;
  mem->mem.size = size;
Wim Taymans's avatar
Wim Taymans committed
142 143 144
  mem->slice_size = slice_size;
  mem->data = data;
  mem->free_func = free_func;
Wim Taymans's avatar
Wim Taymans committed
145

146
  GST_CAT_DEBUG (GST_CAT_MEMORY, "new memory %p", mem);
Wim Taymans's avatar
Wim Taymans committed
147 148
}

Wim Taymans's avatar
Wim Taymans committed
149
/* create a new memory block that manages the given memory */
Wim Taymans's avatar
Wim Taymans committed
150
static GstMemoryDefault *
151
_default_mem_new (GstMemoryFlags flags, GstMemory * parent, gpointer data,
Wim Taymans's avatar
Wim Taymans committed
152 153 154
    GFreeFunc free_func, gsize maxsize, gsize offset, gsize size)
{
  GstMemoryDefault *mem;
Wim Taymans's avatar
Wim Taymans committed
155
  gsize slice_size;
Wim Taymans's avatar
Wim Taymans committed
156

Wim Taymans's avatar
Wim Taymans committed
157 158 159
  slice_size = sizeof (GstMemoryDefault);

  mem = g_slice_alloc (slice_size);
160
  _default_mem_init (mem, flags, parent, slice_size,
Wim Taymans's avatar
Wim Taymans committed
161 162 163 164
      data, free_func, maxsize, offset, size);

  return mem;
}
165

Wim Taymans's avatar
Wim Taymans committed
166
/* allocate the memory and structure in one block */
Wim Taymans's avatar
Wim Taymans committed
167 168 169 170 171 172 173
static GstMemoryDefault *
_default_mem_new_block (gsize maxsize, gsize align, gsize offset, gsize size)
{
  GstMemoryDefault *mem;
  gsize aoffset, slice_size;
  guint8 *data;

174
  /* ensure configured alignment */
Wim Taymans's avatar
Wim Taymans committed
175
  align |= gst_memory_alignment;
Wim Taymans's avatar
Wim Taymans committed
176 177
  /* allocate more to compensate for alignment */
  maxsize += align;
Wim Taymans's avatar
Wim Taymans committed
178
  /* alloc header and data in one block */
Wim Taymans's avatar
Wim Taymans committed
179
  slice_size = sizeof (GstMemoryDefault) + maxsize;
Wim Taymans's avatar
Wim Taymans committed
180 181 182 183 184 185 186 187

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

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

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

Wim Taymans's avatar
Wim Taymans committed
190
  _default_mem_init (mem, 0, NULL, slice_size, data, NULL, maxsize,
Wim Taymans's avatar
Wim Taymans committed
191 192 193 194 195
      aoffset + offset, size);

  return mem;
}

196
static GstMemory *
197 198
_default_alloc_alloc (GstAllocator * allocator, gsize maxsize, gsize align,
    gpointer user_data)
199 200 201 202
{
  return (GstMemory *) _default_mem_new_block (maxsize, align, 0, maxsize);
}

Wim Taymans's avatar
Wim Taymans committed
203
static gpointer
Wim Taymans's avatar
Wim Taymans committed
204
_default_mem_map (GstMemoryDefault * mem, GstMapFlags flags)
Wim Taymans's avatar
Wim Taymans committed
205
{
Wim Taymans's avatar
Wim Taymans committed
206
  return mem->data;
207 208 209
}

static gboolean
Wim Taymans's avatar
Wim Taymans committed
210
_default_mem_unmap (GstMemoryDefault * mem)
211
{
Wim Taymans's avatar
Wim Taymans committed
212 213
  return TRUE;
}
214 215

static void
Wim Taymans's avatar
Wim Taymans committed
216
_default_mem_free (GstMemoryDefault * mem)
217
{
218
  GST_CAT_DEBUG (GST_CAT_MEMORY, "free memory %p", mem);
Wim Taymans's avatar
Wim Taymans committed
219

Wim Taymans's avatar
Wim Taymans committed
220 221 222 223 224
  if (mem->mem.parent)
    gst_memory_unref (mem->mem.parent);

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

Wim Taymans's avatar
Wim Taymans committed
226
  g_slice_free1 (mem->slice_size, mem);
227 228
}

Wim Taymans's avatar
Wim Taymans committed
229
static GstMemoryDefault *
230
_default_mem_copy (GstMemoryDefault * mem, gssize offset, gsize size)
231 232 233
{
  GstMemoryDefault *copy;

Wim Taymans's avatar
Wim Taymans committed
234
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
235
    size = mem->mem.size > offset ? mem->mem.size - offset : 0;
Wim Taymans's avatar
Wim Taymans committed
236

Wim Taymans's avatar
Wim Taymans committed
237 238 239 240
  copy =
      _default_mem_new_block (mem->mem.maxsize, 0, mem->mem.offset + offset,
      size);
  memcpy (copy->data, mem->data, mem->mem.maxsize);
241
  GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "copy memory %p -> %p", mem, copy);
242

Wim Taymans's avatar
Wim Taymans committed
243
  return copy;
244 245
}

Wim Taymans's avatar
Wim Taymans committed
246
static GstMemoryDefault *
247
_default_mem_share (GstMemoryDefault * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
248 249 250 251 252 253 254 255
{
  GstMemoryDefault *sub;
  GstMemory *parent;

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

256
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
257
    size = mem->mem.size - offset;
258

Wim Taymans's avatar
Wim Taymans committed
259 260 261
  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
262 263

  return sub;
264 265
}

Wim Taymans's avatar
Wim Taymans committed
266
static gboolean
Wim Taymans's avatar
Wim Taymans committed
267 268
_default_mem_is_span (GstMemoryDefault * mem1, GstMemoryDefault * mem2,
    gsize * offset)
269
{
Wim Taymans's avatar
Wim Taymans committed
270 271 272 273 274 275

  if (offset) {
    GstMemoryDefault *parent;

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

Wim Taymans's avatar
Wim Taymans committed
276
    *offset = mem1->mem.offset - parent->mem.offset;
Wim Taymans's avatar
Wim Taymans committed
277
  }
278

Wim Taymans's avatar
Wim Taymans committed
279
  /* and memory is contiguous */
Wim Taymans's avatar
Wim Taymans committed
280 281
  return mem1->data + mem1->mem.offset + mem1->mem.size ==
      mem2->data + mem2->mem.offset;
282 283 284
}

static GstMemory *
285
_fallback_mem_copy (GstMemory * mem, gssize offset, gssize size)
286
{
287
  GstMemory *copy;
Wim Taymans's avatar
Wim Taymans committed
288
  GstMapInfo sinfo, dinfo;
289

Wim Taymans's avatar
Wim Taymans committed
290
  if (!gst_memory_map (mem, &sinfo, GST_MAP_READ))
Wim Taymans's avatar
Wim Taymans committed
291
    return NULL;
Wim Taymans's avatar
Wim Taymans committed
292

Wim Taymans's avatar
Wim Taymans committed
293
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
294 295
    size = sinfo.size > offset ? sinfo.size - offset : 0;

Wim Taymans's avatar
Wim Taymans committed
296 297
  /* 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
298
  if (!gst_memory_map (copy, &dinfo, GST_MAP_WRITE)) {
299
    GST_CAT_WARNING (GST_CAT_MEMORY, "could not write map memory %p", copy);
Wim Taymans's avatar
Wim Taymans committed
300 301 302 303
    gst_memory_unmap (mem, &sinfo);
    return NULL;
  }

304
  memcpy (dinfo.data, sinfo.data + offset, size);
305
  GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "copy memory %p -> %p", mem, copy);
Wim Taymans's avatar
Wim Taymans committed
306 307
  gst_memory_unmap (copy, &dinfo);
  gst_memory_unmap (mem, &sinfo);
308

Wim Taymans's avatar
Wim Taymans committed
309
  return copy;
310 311 312
}

static gboolean
313
_fallback_mem_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
314 315 316 317
{
  return FALSE;
}

Wim Taymans's avatar
Wim Taymans committed
318
static GRWLock lock;
319
static GHashTable *allocators;
320

321 322 323 324 325 326
static void
_priv_sysmem_notify (gpointer user_data)
{
  g_warning ("The default memory allocator was freed!");
}

Wim Taymans's avatar
Wim Taymans committed
327
void
Wim Taymans's avatar
Wim Taymans committed
328
_priv_gst_memory_initialize (void)
Wim Taymans's avatar
Wim Taymans committed
329 330
{
  static const GstMemoryInfo _mem_info = {
Wim Taymans's avatar
Wim Taymans committed
331
    GST_ALLOCATOR_SYSMEM,
332
    (GstAllocatorAllocFunction) _default_alloc_alloc,
Wim Taymans's avatar
Wim Taymans committed
333 334 335 336
    (GstMemoryMapFunction) _default_mem_map,
    (GstMemoryUnmapFunction) _default_mem_unmap,
    (GstMemoryFreeFunction) _default_mem_free,
    (GstMemoryCopyFunction) _default_mem_copy,
Wim Taymans's avatar
Wim Taymans committed
337
    (GstMemoryShareFunction) _default_mem_share,
338
    (GstMemoryIsSpanFunction) _default_mem_is_span,
Wim Taymans's avatar
Wim Taymans committed
339 340
  };

341 342 343 344 345
#ifndef GST_DISABLE_TRACE
  _gst_memory_trace = _gst_alloc_trace_register ("GstMemory", -1);
  _gst_allocator_trace = _gst_alloc_trace_register ("GstAllocator", -1);
#endif

Wim Taymans's avatar
Wim Taymans committed
346
  g_rw_lock_init (&lock);
347
  allocators = g_hash_table_new (g_str_hash, g_str_equal);
348

349 350
#ifdef HAVE_GETPAGESIZE
#ifdef MEMORY_ALIGNMENT_PAGESIZE
Wim Taymans's avatar
Wim Taymans committed
351
  gst_memory_alignment = getpagesize () - 1;
352 353 354
#endif
#endif

355 356
  GST_CAT_DEBUG (GST_CAT_MEMORY, "memory alignment: %" G_GSIZE_FORMAT,
      gst_memory_alignment);
Wim Taymans's avatar
Wim Taymans committed
357

358
  _default_mem_impl = gst_allocator_new (&_mem_info, NULL, _priv_sysmem_notify);
359

360 361 362
  _default_allocator = gst_allocator_ref (_default_mem_impl);
  gst_allocator_register (GST_ALLOCATOR_SYSMEM,
      gst_allocator_ref (_default_mem_impl));
363 364
}

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

Wim Taymans's avatar
Wim Taymans committed
384 385 386
  g_return_val_if_fail (data != NULL, NULL);
  g_return_val_if_fail (offset + size <= maxsize, NULL);

Wim Taymans's avatar
Wim Taymans committed
387 388
  mem = _default_mem_new (flags, NULL, data, free_func, maxsize, offset, size);

389 390 391 392
#ifndef GST_DISABLE_TRACE
  _gst_alloc_trace_new (_gst_memory_trace, mem);
#endif

Wim Taymans's avatar
Wim Taymans committed
393 394 395 396 397 398 399
  return (GstMemory *) mem;
}

/**
 * gst_memory_ref:
 * @mem: a #GstMemory
 *
Wim Taymans's avatar
Wim Taymans committed
400 401
 * Increases the refcount of @mem.
 *
Wim Taymans's avatar
Wim Taymans committed
402 403
 * Returns: @mem with increased refcount
 */
404 405 406 407 408
GstMemory *
gst_memory_ref (GstMemory * mem)
{
  g_return_val_if_fail (mem != NULL, NULL);

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

412 413 414 415 416
  g_atomic_int_inc (&mem->refcount);

  return mem;
}

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

430 431
  GST_CAT_TRACE (GST_CAT_MEMORY, "memory %p, %d->%d", mem, mem->refcount,
      mem->refcount - 1);
Wim Taymans's avatar
Wim Taymans committed
432

433
  if (g_atomic_int_dec_and_test (&mem->refcount)) {
434
    g_return_if_fail (g_atomic_int_get (&mem->state) < 4);
435 436 437
#ifndef GST_DISABLE_TRACE
    _gst_alloc_trace_free (_gst_memory_trace, mem);
#endif
438
    mem->allocator->info.mem_free (mem);
439
  }
440 441
}

442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
/**
 * gst_memory_is_exclusive:
 * @mem: a #GstMemory
 *
 * Check if the current ref to @mem is exclusive, this means that no other
 * references exist other than @mem.
 */
gboolean
gst_memory_is_exclusive (GstMemory * mem)
{
  g_return_val_if_fail (mem != NULL, FALSE);

  return (g_atomic_int_get (&mem->refcount) == 1);
}

Wim Taymans's avatar
Wim Taymans committed
457 458 459
/**
 * gst_memory_get_sizes:
 * @mem: a #GstMemory
460
 * @offset: pointer to offset
Wim Taymans's avatar
Wim Taymans committed
461 462
 * @maxsize: pointer to maxsize
 *
463
 * Get the current @size, @offset and @maxsize of @mem.
Wim Taymans's avatar
Wim Taymans committed
464 465
 *
 * Returns: the current sizes of @mem
Wim Taymans's avatar
Wim Taymans committed
466
 */
467
gsize
468
gst_memory_get_sizes (GstMemory * mem, gsize * offset, gsize * maxsize)
469 470 471
{
  g_return_val_if_fail (mem != NULL, 0);

Wim Taymans's avatar
Wim Taymans committed
472 473 474 475 476 477
  if (offset)
    *offset = mem->offset;
  if (maxsize)
    *maxsize = mem->maxsize;

  return mem->size;
478 479
}

Wim Taymans's avatar
Wim Taymans committed
480 481 482 483 484 485 486 487 488 489
/**
 * 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
490
gst_memory_resize (GstMemory * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
491 492
{
  g_return_if_fail (mem != NULL);
Wim Taymans's avatar
Wim Taymans committed
493 494
  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
495

Wim Taymans's avatar
Wim Taymans committed
496 497
  mem->offset += offset;
  mem->size = size;
Wim Taymans's avatar
Wim Taymans committed
498 499
}

Wim Taymans's avatar
Wim Taymans committed
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
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:
  {
525 526
    GST_CAT_DEBUG (GST_CAT_MEMORY, "lock failed %p: state %d, access_mode %d",
        mem, state, access_mode);
Wim Taymans's avatar
Wim Taymans committed
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
    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));
}

546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571

/**
 * 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
572 573
    gst_memory_unref (mem);

574 575 576 577 578 579 580 581 582 583 584
    if (result == NULL)
      goto cannot_copy;

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

  /* ERRORS */
cannot_copy:
  {
585
    GST_CAT_DEBUG (GST_CAT_MEMORY, "cannot copy memory %p", mem);
586 587 588 589
    return NULL;
  }
cannot_map:
  {
590 591
    GST_CAT_DEBUG (GST_CAT_MEMORY, "cannot map memory %p with flags %d", mem,
        flags);
Wim Taymans's avatar
Wim Taymans committed
592
    gst_memory_unref (result);
593 594 595 596
    return NULL;
  }
}

Wim Taymans's avatar
Wim Taymans committed
597 598 599
/**
 * gst_memory_map:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
600
 * @info: (out): pointer for info
Wim Taymans's avatar
Wim Taymans committed
601 602
 * @flags: mapping flags
 *
Wim Taymans's avatar
Wim Taymans committed
603 604
 * 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
605
 *
Wim Taymans's avatar
Wim Taymans committed
606
 * This function can return %FALSE for various reasons:
Wim Taymans's avatar
Wim Taymans committed
607 608 609
 * - the memory backed by @mem is not accessible with the given @flags.
 * - the memory was already mapped with a different mapping.
 *
610 611
 * @info and its contents remain valid for as long as @mem is valid and
 * until gst_memory_unmap() is called.
Wim Taymans's avatar
Wim Taymans committed
612 613 614 615
 *
 * For each gst_memory_map() call, a corresponding gst_memory_unmap() call
 * should be done.
 *
Wim Taymans's avatar
Wim Taymans committed
616
 * Returns: %TRUE if the map operation was successful.
Wim Taymans's avatar
Wim Taymans committed
617
 */
Wim Taymans's avatar
Wim Taymans committed
618 619
gboolean
gst_memory_map (GstMemory * mem, GstMapInfo * info, GstMapFlags flags)
620
{
Wim Taymans's avatar
Wim Taymans committed
621 622
  g_return_val_if_fail (mem != NULL, FALSE);
  g_return_val_if_fail (info != NULL, FALSE);
623

Wim Taymans's avatar
Wim Taymans committed
624 625 626
  if (!gst_memory_lock (mem, flags))
    goto lock_failed;

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

Wim Taymans's avatar
Wim Taymans committed
629
  if (G_UNLIKELY (info->data == NULL))
Wim Taymans's avatar
Wim Taymans committed
630 631
    goto error;

Wim Taymans's avatar
Wim Taymans committed
632
  info->memory = mem;
Wim Taymans's avatar
Wim Taymans committed
633
  info->flags = flags;
Wim Taymans's avatar
Wim Taymans committed
634 635
  info->size = mem->size;
  info->maxsize = mem->maxsize - mem->offset;
636
  info->data = info->data + mem->offset;
637

Wim Taymans's avatar
Wim Taymans committed
638
  return TRUE;
639

Wim Taymans's avatar
Wim Taymans committed
640 641 642
  /* ERRORS */
lock_failed:
  {
643
    GST_CAT_DEBUG (GST_CAT_MEMORY, "mem %p: lock %d failed", mem, flags);
Wim Taymans's avatar
Wim Taymans committed
644
    return FALSE;
Wim Taymans's avatar
Wim Taymans committed
645 646 647
  }
error:
  {
648
    /* something went wrong, restore the orginal state again */
649
    GST_CAT_ERROR (GST_CAT_MEMORY, "mem %p: map failed", mem);
Wim Taymans's avatar
Wim Taymans committed
650
    gst_memory_unlock (mem);
Wim Taymans's avatar
Wim Taymans committed
651
    return FALSE;
652
  }
653 654
}

Wim Taymans's avatar
Wim Taymans committed
655 656 657
/**
 * gst_memory_unmap:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
658
 * @info: a #GstMapInfo
Wim Taymans's avatar
Wim Taymans committed
659
 *
Wim Taymans's avatar
Wim Taymans committed
660
 * Release the memory obtained with gst_memory_map()
Wim Taymans's avatar
Wim Taymans committed
661
 */
Wim Taymans's avatar
Wim Taymans committed
662
void
Wim Taymans's avatar
Wim Taymans committed
663
gst_memory_unmap (GstMemory * mem, GstMapInfo * info)
664
{
Wim Taymans's avatar
Wim Taymans committed
665
  g_return_if_fail (mem != NULL);
Wim Taymans's avatar
Wim Taymans committed
666 667
  g_return_if_fail (info != NULL);
  g_return_if_fail (info->memory == mem);
Wim Taymans's avatar
Wim Taymans committed
668 669
  /* there must be a ref */
  g_return_if_fail (g_atomic_int_get (&mem->state) >= 4);
670

671
  mem->allocator->info.mem_unmap (mem);
Wim Taymans's avatar
Wim Taymans committed
672
  gst_memory_unlock (mem);
673 674
}

Wim Taymans's avatar
Wim Taymans committed
675 676 677 678
/**
 * gst_memory_copy:
 * @mem: a #GstMemory
 * @offset: an offset to copy
679
 * @size: size to copy or -1 to copy all bytes from offset
Wim Taymans's avatar
Wim Taymans committed
680 681 682 683 684 685 686
 *
 * 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.
 */
687
GstMemory *
688
gst_memory_copy (GstMemory * mem, gssize offset, gssize size)
689
{
Wim Taymans's avatar
Wim Taymans committed
690 691
  GstMemory *copy;

692
  g_return_val_if_fail (mem != NULL, NULL);
Wim Taymans's avatar
Wim Taymans committed
693

694
  copy = mem->allocator->info.mem_copy (mem, offset, size);
695

696 697 698
#ifndef GST_DISABLE_TRACE
  _gst_alloc_trace_new (_gst_memory_trace, copy);
#endif
Wim Taymans's avatar
Wim Taymans committed
699 700

  return copy;
701 702
}

Wim Taymans's avatar
Wim Taymans committed
703 704 705 706
/**
 * gst_memory_share:
 * @mem: a #GstMemory
 * @offset: an offset to share
707
 * @size: size to share or -1 to share bytes from offset
Wim Taymans's avatar
Wim Taymans committed
708
 *
709 710 711 712
 * 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
713 714 715
 *
 * Returns: a new #GstMemory.
 */
716
GstMemory *
717
gst_memory_share (GstMemory * mem, gssize offset, gssize size)
718
{
719 720
  GstMemory *shared;

721
  g_return_val_if_fail (mem != NULL, NULL);
722 723
  g_return_val_if_fail (!GST_MEMORY_FLAG_IS_SET (mem, GST_MEMORY_FLAG_NO_SHARE),
      NULL);
724

725 726 727 728 729 730 731
  shared = mem->allocator->info.mem_share (mem, offset, size);

#ifndef GST_DISABLE_TRACE
  _gst_alloc_trace_new (_gst_memory_trace, shared);
#endif

  return shared;
732 733
}

Wim Taymans's avatar
Wim Taymans committed
734 735 736 737 738 739 740 741 742 743 744 745 746 747 748
/**
 * 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
749
gboolean
750
gst_memory_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
Wim Taymans's avatar
Wim Taymans committed
751 752 753 754
{
  g_return_val_if_fail (mem1 != NULL, FALSE);
  g_return_val_if_fail (mem2 != NULL, FALSE);

755 756
  /* need to have the same allocators */
  if (mem1->allocator != mem2->allocator)
Wim Taymans's avatar
Wim Taymans committed
757 758
    return FALSE;

759 760 761
  /* need to have the same parent */
  if (mem1->parent == NULL || mem1->parent != mem2->parent)
    return FALSE;
Wim Taymans's avatar
Wim Taymans committed
762

763
  /* and memory is contiguous */
764
  if (!mem1->allocator->info.mem_is_span (mem1, mem2, offset))
765 766 767
    return FALSE;

  return TRUE;
Wim Taymans's avatar
Wim Taymans committed
768
}
769 770

/**
771
 * gst_allocator_register:
772 773 774
 * @info: a #GstMemoryInfo
 * @user_data: user data
 * @notify: a #GDestroyNotify for @user_data
775
 *
776
 * Create a new memory allocator with @info and @user_data.
777
 *
778
 * All functions in @info are mandatory exept the copy and is_span
779 780
 * functions, which will have a default implementation when left NULL.
 *
Wim Taymans's avatar
Wim Taymans committed
781 782
 * The @user_data will be passed to all calls of the alloc function. @notify
 * will be called with @user_data when the allocator is freed.
783
 *
784
 * Returns: a new #GstAllocator.
785
 */
786 787 788
GstAllocator *
gst_allocator_new (const GstMemoryInfo * info, gpointer user_data,
    GDestroyNotify notify)
789
{
790
  GstAllocator *allocator;
791 792 793 794 795

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

  g_return_val_if_fail (info != NULL, NULL);
796
  g_return_val_if_fail (info->alloc != NULL, NULL);
797 798 799 800
  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);
801

802
  allocator = g_slice_new (GstAllocator);
803
  allocator->refcount = 1;
804
  allocator->info = *info;
805 806 807 808
  allocator->user_data = user_data;
  allocator->notify = notify;
  INSTALL_FALLBACK (mem_copy);
  INSTALL_FALLBACK (mem_is_span);
809 810
#undef INSTALL_FALLBACK

811 812 813 814 815
  GST_CAT_DEBUG (GST_CAT_MEMORY, "new allocator %p", allocator);

#ifndef GST_DISABLE_TRACE
  _gst_alloc_trace_new (_gst_allocator_trace, allocator);
#endif
816 817 818 819

  return allocator;
}

Wim Taymans's avatar
Wim Taymans committed
820 821 822 823 824 825
/**
 * gst_alocator_get_memory_type:
 * @allocator: a #GstAllocator
 *
 * Get the memory type allocated by this allocator
 *
Wim Taymans's avatar
Wim Taymans committed
826
 * Returns: the memory type provided by @allocator
Wim Taymans's avatar
Wim Taymans committed
827 828 829 830 831 832 833 834 835
 */
const gchar *
gst_allocator_get_memory_type (GstAllocator * allocator)
{
  g_return_val_if_fail (allocator != NULL, NULL);

  return allocator->info.mem_type;
}

836 837 838 839 840 841 842 843 844 845 846 847 848
/**
 * 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);

849 850
  GST_CAT_TRACE (GST_CAT_MEMORY, "alocator %p, %d->%d", allocator,
      allocator->refcount, allocator->refcount + 1);
851 852 853 854 855 856 857 858 859 860

  g_atomic_int_inc (&allocator->refcount);

  return allocator;
}

/**
 * gst_allocator_unref:
 * @allocator: a #GstAllocator
 *
Wim Taymans's avatar
Wim Taymans committed
861 862
 * Decreases the refcount of @allocator. When the refcount reaches 0, the notify
 * function of @allocator will be called and the allocator will be freed.
863 864 865 866 867 868
 */
void
gst_allocator_unref (GstAllocator * allocator)
{
  g_return_if_fail (allocator != NULL);

869 870
  GST_CAT_TRACE (GST_CAT_MEMORY, "allocator %p, %d->%d", allocator,
      allocator->refcount, allocator->refcount - 1);
871 872 873 874

  if (g_atomic_int_dec_and_test (&allocator->refcount)) {
    if (allocator->notify)
      allocator->notify (allocator->user_data);
875 876 877
#ifndef GST_DISABLE_TRACE
    _gst_alloc_trace_free (_gst_allocator_trace, allocator);
#endif
878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895
    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);

896 897
  GST_CAT_DEBUG (GST_CAT_MEMORY, "registering allocator %p with name \"%s\"",
      allocator, name);
898

Wim Taymans's avatar
Wim Taymans committed
899
  g_rw_lock_writer_lock (&lock);
900
  g_hash_table_insert (allocators, (gpointer) name, (gpointer) allocator);
Wim Taymans's avatar
Wim Taymans committed
901
  g_rw_lock_writer_unlock (&lock);
902 903 904
}

/**
905
 * gst_allocator_find:
906 907
 * @name: the name of the allocator
 *
908 909
 * Find a previously registered allocator with @name. When @name is NULL, the
 * default allocator will be returned.
910
 *
911 912
 * 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.
913
 */
914
GstAllocator *
915
gst_allocator_find (const gchar * name)
916
{
917
  GstAllocator *allocator;
918

Wim Taymans's avatar
Wim Taymans committed
919
  g_rw_lock_reader_lock (&lock);
920
  if (name) {
921
    allocator = g_hash_table_lookup (allocators, (gconstpointer) name);
922 923 924
  } else {
    allocator = _default_allocator;
  }
925 926
  if (allocator)
    gst_allocator_ref (allocator);
Wim Taymans's avatar
Wim Taymans committed
927
  g_rw_lock_reader_unlock (&lock);
928 929 930 931 932

  return allocator;
}

/**
933
 * gst_allocator_set_default:
934
 * @allocator: (transfer full): a #GstAllocator
935
 *
936
 * Set the default allocator. This function takes ownership of @allocator.
937 938
 */
void
939
gst_allocator_set_default (GstAllocator * allocator)
940
{
941
  GstAllocator *old;
942 943
  g_return_if_fail (allocator != NULL);

Wim Taymans's avatar
Wim Taymans committed
944
  g_rw_lock_writer_lock (&lock);
945
  old = _default_allocator;
946
  _default_allocator = allocator;
Wim Taymans's avatar
Wim Taymans committed
947
  g_rw_lock_writer_unlock (&lock);
948 949 950

  if (old)
    gst_allocator_unref (old);
Wim Taymans's avatar