gstmemory.c 25.2 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;
Wim Taymans's avatar
Wim Taymans committed
138 139 140 141
  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
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 434 435 436
  if (g_atomic_int_dec_and_test (&mem->refcount)) {
#ifndef GST_DISABLE_TRACE
    _gst_alloc_trace_free (_gst_memory_trace, mem);
#endif
437
    mem->allocator->info.mem_free (mem);
438
  }
439 440
}

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

Wim Taymans's avatar
Wim Taymans committed
456 457 458 459 460 461
  if (offset)
    *offset = mem->offset;
  if (maxsize)
    *maxsize = mem->maxsize;

  return mem->size;
462 463
}

Wim Taymans's avatar
Wim Taymans committed
464 465 466 467 468 469 470 471 472 473
/**
 * 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
474
gst_memory_resize (GstMemory * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
475 476
{
  g_return_if_fail (mem != NULL);
477
  g_return_if_fail (gst_memory_is_writable (mem));
Wim Taymans's avatar
Wim Taymans committed
478 479
  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
480

Wim Taymans's avatar
Wim Taymans committed
481 482
  mem->offset += offset;
  mem->size = size;
Wim Taymans's avatar
Wim Taymans committed
483 484
}

485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
/**
 * 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
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527
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:
  {
528 529
    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
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
    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));
}

549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574

/**
 * 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
575 576
    gst_memory_unref (mem);

577 578 579 580 581 582 583 584 585 586 587
    if (result == NULL)
      goto cannot_copy;

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

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

Wim Taymans's avatar
Wim Taymans committed
600 601 602
/**
 * gst_memory_map:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
603
 * @info: (out): pointer for info
Wim Taymans's avatar
Wim Taymans committed
604 605
 * @flags: mapping flags
 *
Wim Taymans's avatar
Wim Taymans committed
606 607
 * 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
608
 *
Wim Taymans's avatar
Wim Taymans committed
609
 * This function can return %FALSE for various reasons:
Wim Taymans's avatar
Wim Taymans committed
610 611 612
 * - 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
613
 * @info and its contents remains valid for as long as @mem is alive and until
Wim Taymans's avatar
Wim Taymans committed
614 615 616 617 618
 * 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
619
 * Returns: %TRUE if the map operation was successful.
Wim Taymans's avatar
Wim Taymans committed
620
 */
Wim Taymans's avatar
Wim Taymans committed
621 622
gboolean
gst_memory_map (GstMemory * mem, GstMapInfo * info, GstMapFlags flags)
623
{
Wim Taymans's avatar
Wim Taymans committed
624 625
  g_return_val_if_fail (mem != NULL, FALSE);
  g_return_val_if_fail (info != NULL, FALSE);
626

Wim Taymans's avatar
Wim Taymans committed
627 628 629
  if (!gst_memory_lock (mem, flags))
    goto lock_failed;

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

Wim Taymans's avatar
Wim Taymans committed
632
  if (G_UNLIKELY (info->data == NULL))
Wim Taymans's avatar
Wim Taymans committed
633 634
    goto error;

Wim Taymans's avatar
Wim Taymans committed
635
  info->memory = mem;
Wim Taymans's avatar
Wim Taymans committed
636
  info->flags = flags;
Wim Taymans's avatar
Wim Taymans committed
637 638
  info->size = mem->size;
  info->maxsize = mem->maxsize - mem->offset;
639
  info->data = info->data + mem->offset;
640

Wim Taymans's avatar
Wim Taymans committed
641
  return TRUE;
642

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

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

674
  mem->allocator->info.mem_unmap (mem);
Wim Taymans's avatar
Wim Taymans committed
675
  gst_memory_unlock (mem);
676 677
}

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

695
  g_return_val_if_fail (mem != NULL, NULL);
Wim Taymans's avatar
Wim Taymans committed
696

697
  copy = mem->allocator->info.mem_copy (mem, offset, size);
698

699 700 701
#ifndef GST_DISABLE_TRACE
  _gst_alloc_trace_new (_gst_memory_trace, copy);
#endif
Wim Taymans's avatar
Wim Taymans committed
702 703

  return copy;
704 705
}

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

724 725
  g_return_val_if_fail (mem != NULL, NULL);

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

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

  return shared;
733 734
}

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

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

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

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

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

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

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

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

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

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

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

  return allocator;
}

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

  return allocator->info.mem_type;
}

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

850 851
  GST_CAT_TRACE (GST_CAT_MEMORY, "alocator %p, %d->%d", allocator,
      allocator->refcount, allocator->refcount + 1);
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869

  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);

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

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

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

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

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

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

  return allocator;
}

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

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

  if (old)
    gst_allocator_unref (old);
952 953 954
}

/**
955
 * gst_allocator_alloc:
956
 * @allocator: (transfer none) (allow-none): a #GstAllocator to use
957 958 959 960 961 962
 * @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.
 *
963 964
 * When @allocator is NULL, the default allocator will be used.
 *