gstmemory.c 25.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
 * After the memory access is completed, gst_memory_unmap() should be called.
 *
David Schleef's avatar
David Schleef committed
56
 * Memory can be copied with gst_memory_copy(), which will return a writable
Wim Taymans's avatar
Wim Taymans committed
57 58 59 60
 * 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
  guint8 *data;
119 120
  gpointer user_data;
  GDestroyNotify notify;
121 122
} GstMemoryDefault;

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

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

Wim Taymans's avatar
Wim Taymans committed
129
/* initialize the fields */
Wim Taymans's avatar
Wim Taymans committed
130
static void
131 132
_default_mem_init (GstMemoryDefault * mem, GstMemoryFlags flags,
    GstMemory * parent, gsize slice_size, gpointer data,
133 134
    gsize maxsize, gsize offset, gsize size, gpointer user_data,
    GDestroyNotify notify)
Wim Taymans's avatar
Wim Taymans committed
135
{
136
  mem->mem.allocator = _default_mem_impl;
137
  mem->mem.flags = flags;
Wim Taymans's avatar
Wim Taymans committed
138
  mem->mem.refcount = 1;
Wim Taymans's avatar
Wim Taymans committed
139
  mem->mem.parent = parent ? gst_memory_ref (parent) : NULL;
140
  mem->mem.state = (flags & GST_MEMORY_FLAG_READONLY ? 0x1 : 0);
Wim Taymans's avatar
Wim Taymans committed
141 142 143
  mem->mem.maxsize = maxsize;
  mem->mem.offset = offset;
  mem->mem.size = size;
Wim Taymans's avatar
Wim Taymans committed
144 145
  mem->slice_size = slice_size;
  mem->data = data;
146 147
  mem->user_data = user_data;
  mem->notify = notify;
Wim Taymans's avatar
Wim Taymans committed
148

149
  GST_CAT_DEBUG (GST_CAT_MEMORY, "new memory %p", mem);
Wim Taymans's avatar
Wim Taymans committed
150 151
}

Wim Taymans's avatar
Wim Taymans committed
152
/* create a new memory block that manages the given memory */
Wim Taymans's avatar
Wim Taymans committed
153
static GstMemoryDefault *
154
_default_mem_new (GstMemoryFlags flags, GstMemory * parent, gpointer data,
155 156
    gsize maxsize, gsize offset, gsize size, gpointer user_data,
    GDestroyNotify notify)
Wim Taymans's avatar
Wim Taymans committed
157 158
{
  GstMemoryDefault *mem;
Wim Taymans's avatar
Wim Taymans committed
159
  gsize slice_size;
Wim Taymans's avatar
Wim Taymans committed
160

Wim Taymans's avatar
Wim Taymans committed
161 162 163
  slice_size = sizeof (GstMemoryDefault);

  mem = g_slice_alloc (slice_size);
164
  _default_mem_init (mem, flags, parent, slice_size,
165
      data, maxsize, offset, size, user_data, notify);
Wim Taymans's avatar
Wim Taymans committed
166 167 168

  return mem;
}
169

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

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

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

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

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

194 195
  _default_mem_init (mem, 0, NULL, slice_size, data, maxsize,
      aoffset + offset, size, NULL, NULL);
Wim Taymans's avatar
Wim Taymans committed
196 197 198 199

  return mem;
}

200
static GstMemory *
201 202
_default_alloc_alloc (GstAllocator * allocator, gsize maxsize, gsize align,
    gpointer user_data)
203 204 205 206
{
  return (GstMemory *) _default_mem_new_block (maxsize, align, 0, maxsize);
}

Wim Taymans's avatar
Wim Taymans committed
207
static gpointer
Wim Taymans's avatar
Wim Taymans committed
208
_default_mem_map (GstMemoryDefault * mem, GstMapFlags flags)
Wim Taymans's avatar
Wim Taymans committed
209
{
Wim Taymans's avatar
Wim Taymans committed
210
  return mem->data;
211 212 213
}

static gboolean
Wim Taymans's avatar
Wim Taymans committed
214
_default_mem_unmap (GstMemoryDefault * mem)
215
{
Wim Taymans's avatar
Wim Taymans committed
216 217
  return TRUE;
}
218 219

static void
Wim Taymans's avatar
Wim Taymans committed
220
_default_mem_free (GstMemoryDefault * mem)
221
{
222
  GST_CAT_DEBUG (GST_CAT_MEMORY, "free memory %p", mem);
Wim Taymans's avatar
Wim Taymans committed
223

Wim Taymans's avatar
Wim Taymans committed
224 225 226
  if (mem->mem.parent)
    gst_memory_unref (mem->mem.parent);

227 228
  if (mem->notify)
    mem->notify (mem->user_data);
229

Wim Taymans's avatar
Wim Taymans committed
230
  g_slice_free1 (mem->slice_size, mem);
231 232
}

Wim Taymans's avatar
Wim Taymans committed
233
static GstMemoryDefault *
234
_default_mem_copy (GstMemoryDefault * mem, gssize offset, gsize size)
235 236 237
{
  GstMemoryDefault *copy;

Wim Taymans's avatar
Wim Taymans committed
238
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
239
    size = mem->mem.size > offset ? mem->mem.size - offset : 0;
Wim Taymans's avatar
Wim Taymans committed
240

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

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

Wim Taymans's avatar
Wim Taymans committed
250
static GstMemoryDefault *
251
_default_mem_share (GstMemoryDefault * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
252 253 254 255 256 257 258 259
{
  GstMemoryDefault *sub;
  GstMemory *parent;

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

260
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
261
    size = mem->mem.size - offset;
262

Wim Taymans's avatar
Wim Taymans committed
263
  sub =
264 265
      _default_mem_new (parent->flags, parent, mem->data,
      mem->mem.maxsize, mem->mem.offset + offset, size, NULL, NULL);
Wim Taymans's avatar
Wim Taymans committed
266 267

  return sub;
268 269
}

Wim Taymans's avatar
Wim Taymans committed
270
static gboolean
Wim Taymans's avatar
Wim Taymans committed
271 272
_default_mem_is_span (GstMemoryDefault * mem1, GstMemoryDefault * mem2,
    gsize * offset)
273
{
Wim Taymans's avatar
Wim Taymans committed
274 275 276 277 278 279

  if (offset) {
    GstMemoryDefault *parent;

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

Wim Taymans's avatar
Wim Taymans committed
280
    *offset = mem1->mem.offset - parent->mem.offset;
Wim Taymans's avatar
Wim Taymans committed
281
  }
282

Wim Taymans's avatar
Wim Taymans committed
283
  /* and memory is contiguous */
Wim Taymans's avatar
Wim Taymans committed
284 285
  return mem1->data + mem1->mem.offset + mem1->mem.size ==
      mem2->data + mem2->mem.offset;
286 287 288
}

static GstMemory *
289
_fallback_mem_copy (GstMemory * mem, gssize offset, gssize size)
290
{
291
  GstMemory *copy;
Wim Taymans's avatar
Wim Taymans committed
292
  GstMapInfo sinfo, dinfo;
293

Wim Taymans's avatar
Wim Taymans committed
294
  if (!gst_memory_map (mem, &sinfo, GST_MAP_READ))
Wim Taymans's avatar
Wim Taymans committed
295
    return NULL;
Wim Taymans's avatar
Wim Taymans committed
296

Wim Taymans's avatar
Wim Taymans committed
297
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
298 299
    size = sinfo.size > offset ? sinfo.size - offset : 0;

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

308
  memcpy (dinfo.data, sinfo.data + offset, size);
309
  GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "copy memory %p -> %p", mem, copy);
Wim Taymans's avatar
Wim Taymans committed
310 311
  gst_memory_unmap (copy, &dinfo);
  gst_memory_unmap (mem, &sinfo);
312

Wim Taymans's avatar
Wim Taymans committed
313
  return copy;
314 315 316
}

static gboolean
317
_fallback_mem_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
318 319 320 321
{
  return FALSE;
}

Wim Taymans's avatar
Wim Taymans committed
322
static GRWLock lock;
323
static GHashTable *allocators;
324

325 326 327 328 329 330
static void
_priv_sysmem_notify (gpointer user_data)
{
  g_warning ("The default memory allocator was freed!");
}

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

345 346 347 348 349
#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
350
  g_rw_lock_init (&lock);
351
  allocators = g_hash_table_new (g_str_hash, g_str_equal);
352

353 354
#ifdef HAVE_GETPAGESIZE
#ifdef MEMORY_ALIGNMENT_PAGESIZE
Wim Taymans's avatar
Wim Taymans committed
355
  gst_memory_alignment = getpagesize () - 1;
356 357 358
#endif
#endif

359 360
  GST_CAT_DEBUG (GST_CAT_MEMORY, "memory alignment: %" G_GSIZE_FORMAT,
      gst_memory_alignment);
Wim Taymans's avatar
Wim Taymans committed
361

362
  _default_mem_impl = gst_allocator_new (&_mem_info, NULL, _priv_sysmem_notify);
363

364 365 366
  _default_allocator = gst_allocator_ref (_default_mem_impl);
  gst_allocator_register (GST_ALLOCATOR_SYSMEM,
      gst_allocator_ref (_default_mem_impl));
367 368
}

Wim Taymans's avatar
Wim Taymans committed
369 370 371 372 373 374 375
/**
 * gst_memory_new_wrapped:
 * @flags: #GstMemoryFlags
 * @data: data to wrap
 * @maxsize: allocated size of @data
 * @offset: offset in @data
 * @size: size of valid data
376 377
 * @user_data: user_data
 * @notify: called with @user_data when the memory is freed
Wim Taymans's avatar
Wim Taymans committed
378 379 380 381 382
 *
 * Allocate a new memory block that wraps the given @data.
 *
 * Returns: a new #GstMemory.
 */
Wim Taymans's avatar
Wim Taymans committed
383 384
GstMemory *
gst_memory_new_wrapped (GstMemoryFlags flags, gpointer data,
385 386
    gsize maxsize, gsize offset, gsize size, gpointer user_data,
    GDestroyNotify notify)
387
{
Wim Taymans's avatar
Wim Taymans committed
388
  GstMemoryDefault *mem;
Wim Taymans's avatar
Wim Taymans committed
389

Wim Taymans's avatar
Wim Taymans committed
390 391 392
  g_return_val_if_fail (data != NULL, NULL);
  g_return_val_if_fail (offset + size <= maxsize, NULL);

393 394 395
  mem =
      _default_mem_new (flags, NULL, data, maxsize, offset, size, user_data,
      notify);
Wim Taymans's avatar
Wim Taymans committed
396

397 398 399 400
#ifndef GST_DISABLE_TRACE
  _gst_alloc_trace_new (_gst_memory_trace, mem);
#endif

Wim Taymans's avatar
Wim Taymans committed
401 402 403 404 405 406 407
  return (GstMemory *) mem;
}

/**
 * gst_memory_ref:
 * @mem: a #GstMemory
 *
Wim Taymans's avatar
Wim Taymans committed
408 409
 * Increases the refcount of @mem.
 *
Wim Taymans's avatar
Wim Taymans committed
410 411
 * Returns: @mem with increased refcount
 */
412 413 414 415 416
GstMemory *
gst_memory_ref (GstMemory * mem)
{
  g_return_val_if_fail (mem != NULL, NULL);

417 418
  GST_CAT_TRACE (GST_CAT_MEMORY, "memory %p, %d->%d", mem, mem->refcount,
      mem->refcount + 1);
Wim Taymans's avatar
Wim Taymans committed
419

420 421 422 423 424
  g_atomic_int_inc (&mem->refcount);

  return mem;
}

Wim Taymans's avatar
Wim Taymans committed
425 426 427
/**
 * gst_memory_unref:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
428 429 430
 *
 * 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
431
 */
432 433 434 435
void
gst_memory_unref (GstMemory * mem)
{
  g_return_if_fail (mem != NULL);
436
  g_return_if_fail (mem->allocator != NULL);
437

438 439
  GST_CAT_TRACE (GST_CAT_MEMORY, "memory %p, %d->%d", mem, mem->refcount,
      mem->refcount - 1);
Wim Taymans's avatar
Wim Taymans committed
440

441
  if (g_atomic_int_dec_and_test (&mem->refcount)) {
Wim Taymans's avatar
Wim Taymans committed
442
    /* there should be no outstanding mappings */
443
    g_return_if_fail (g_atomic_int_get (&mem->state) < 4);
444 445 446
#ifndef GST_DISABLE_TRACE
    _gst_alloc_trace_free (_gst_memory_trace, mem);
#endif
447
    mem->allocator->info.mem_free (mem);
448
  }
449 450
}

451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
/**
 * 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
466 467 468
/**
 * gst_memory_get_sizes:
 * @mem: a #GstMemory
469
 * @offset: pointer to offset
Wim Taymans's avatar
Wim Taymans committed
470 471
 * @maxsize: pointer to maxsize
 *
472
 * Get the current @size, @offset and @maxsize of @mem.
Wim Taymans's avatar
Wim Taymans committed
473 474
 *
 * Returns: the current sizes of @mem
Wim Taymans's avatar
Wim Taymans committed
475
 */
476
gsize
477
gst_memory_get_sizes (GstMemory * mem, gsize * offset, gsize * maxsize)
478 479 480
{
  g_return_val_if_fail (mem != NULL, 0);

Wim Taymans's avatar
Wim Taymans committed
481 482 483 484 485 486
  if (offset)
    *offset = mem->offset;
  if (maxsize)
    *maxsize = mem->maxsize;

  return mem->size;
487 488
}

Wim Taymans's avatar
Wim Taymans committed
489 490 491 492 493 494 495 496 497 498
/**
 * 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
499
gst_memory_resize (GstMemory * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
500 501
{
  g_return_if_fail (mem != NULL);
Wim Taymans's avatar
Wim Taymans committed
502 503
  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
504

Wim Taymans's avatar
Wim Taymans committed
505 506
  mem->offset += offset;
  mem->size = size;
Wim Taymans's avatar
Wim Taymans committed
507 508
}

Wim Taymans's avatar
Wim Taymans committed
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
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:
  {
534 535
    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
536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
    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));
}

555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580

/**
 * 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
581 582
    gst_memory_unref (mem);

583 584 585 586 587 588 589 590 591 592 593
    if (result == NULL)
      goto cannot_copy;

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

  /* ERRORS */
cannot_copy:
  {
594
    GST_CAT_DEBUG (GST_CAT_MEMORY, "cannot copy memory %p", mem);
595 596 597 598
    return NULL;
  }
cannot_map:
  {
599 600
    GST_CAT_DEBUG (GST_CAT_MEMORY, "cannot map memory %p with flags %d", mem,
        flags);
Wim Taymans's avatar
Wim Taymans committed
601
    gst_memory_unref (result);
602 603 604 605
    return NULL;
  }
}

Wim Taymans's avatar
Wim Taymans committed
606 607 608
/**
 * gst_memory_map:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
609
 * @info: (out): pointer for info
Wim Taymans's avatar
Wim Taymans committed
610 611
 * @flags: mapping flags
 *
Wim Taymans's avatar
Wim Taymans committed
612 613
 * 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
614
 *
Wim Taymans's avatar
Wim Taymans committed
615
 * This function can return %FALSE for various reasons:
Wim Taymans's avatar
Wim Taymans committed
616 617 618
 * - the memory backed by @mem is not accessible with the given @flags.
 * - the memory was already mapped with a different mapping.
 *
619 620
 * @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
621 622 623 624
 *
 * For each gst_memory_map() call, a corresponding gst_memory_unmap() call
 * should be done.
 *
Wim Taymans's avatar
Wim Taymans committed
625
 * Returns: %TRUE if the map operation was successful.
Wim Taymans's avatar
Wim Taymans committed
626
 */
Wim Taymans's avatar
Wim Taymans committed
627 628
gboolean
gst_memory_map (GstMemory * mem, GstMapInfo * info, GstMapFlags flags)
629
{
Wim Taymans's avatar
Wim Taymans committed
630 631
  g_return_val_if_fail (mem != NULL, FALSE);
  g_return_val_if_fail (info != NULL, FALSE);
632

Wim Taymans's avatar
Wim Taymans committed
633 634 635
  if (!gst_memory_lock (mem, flags))
    goto lock_failed;

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

Wim Taymans's avatar
Wim Taymans committed
638
  if (G_UNLIKELY (info->data == NULL))
Wim Taymans's avatar
Wim Taymans committed
639 640
    goto error;

Wim Taymans's avatar
Wim Taymans committed
641
  info->memory = mem;
Wim Taymans's avatar
Wim Taymans committed
642
  info->flags = flags;
Wim Taymans's avatar
Wim Taymans committed
643 644
  info->size = mem->size;
  info->maxsize = mem->maxsize - mem->offset;
645
  info->data = info->data + mem->offset;
646

Wim Taymans's avatar
Wim Taymans committed
647
  return TRUE;
648

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

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

680
  mem->allocator->info.mem_unmap (mem);
Wim Taymans's avatar
Wim Taymans committed
681
  gst_memory_unlock (mem);
682 683
}

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

701
  g_return_val_if_fail (mem != NULL, NULL);
Wim Taymans's avatar
Wim Taymans committed
702

703
  copy = mem->allocator->info.mem_copy (mem, offset, size);
704

705 706 707
#ifndef GST_DISABLE_TRACE
  _gst_alloc_trace_new (_gst_memory_trace, copy);
#endif
Wim Taymans's avatar
Wim Taymans committed
708 709

  return copy;
710 711
}

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

730
  g_return_val_if_fail (mem != NULL, NULL);
731 732
  g_return_val_if_fail (!GST_MEMORY_FLAG_IS_SET (mem, GST_MEMORY_FLAG_NO_SHARE),
      NULL);
733

734 735 736 737 738 739 740
  shared = mem->allocator->info.mem_share (mem, offset, size);

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

  return shared;
741 742
}

Wim Taymans's avatar
Wim Taymans committed
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757
/**
 * 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
758
gboolean
759
gst_memory_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
Wim Taymans's avatar
Wim Taymans committed
760 761 762 763
{
  g_return_val_if_fail (mem1 != NULL, FALSE);
  g_return_val_if_fail (mem2 != NULL, FALSE);

764 765
  /* need to have the same allocators */
  if (mem1->allocator != mem2->allocator)
Wim Taymans's avatar
Wim Taymans committed
766 767
    return FALSE;

768 769 770
  /* need to have the same parent */
  if (mem1->parent == NULL || mem1->parent != mem2->parent)
    return FALSE;
Wim Taymans's avatar
Wim Taymans committed
771

772
  /* and memory is contiguous */
773
  if (!mem1->allocator->info.mem_is_span (mem1, mem2, offset))
774 775 776
    return FALSE;

  return TRUE;
Wim Taymans's avatar
Wim Taymans committed
777
}
778 779

/**
780
 * gst_allocator_register:
781 782 783
 * @info: a #GstMemoryInfo
 * @user_data: user data
 * @notify: a #GDestroyNotify for @user_data
784
 *
785
 * Create a new memory allocator with @info and @user_data.
786
 *
787
 * All functions in @info are mandatory exept the copy and is_span
788 789
 * functions, which will have a default implementation when left NULL.
 *
Wim Taymans's avatar
Wim Taymans committed
790 791
 * 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.
792
 *
793
 * Returns: a new #GstAllocator.
794
 */
795 796 797
GstAllocator *
gst_allocator_new (const GstMemoryInfo * info, gpointer user_data,
    GDestroyNotify notify)
798
{
799
  GstAllocator *allocator;
800 801 802 803 804

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

  g_return_val_if_fail (info != NULL, NULL);
805
  g_return_val_if_fail (info->alloc != NULL, NULL);
806 807 808 809
  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);
810

811
  allocator = g_slice_new (GstAllocator);
812
  allocator->refcount = 1;
813
  allocator->info = *info;
814 815 816 817
  allocator->user_data = user_data;
  allocator->notify = notify;
  INSTALL_FALLBACK (mem_copy);
  INSTALL_FALLBACK (mem_is_span);
818 819
#undef INSTALL_FALLBACK

820 821 822 823 824
  GST_CAT_DEBUG (GST_CAT_MEMORY, "new allocator %p", allocator);

#ifndef GST_DISABLE_TRACE
  _gst_alloc_trace_new (_gst_allocator_trace, allocator);
#endif
825 826 827 828

  return allocator;
}

Wim Taymans's avatar
Wim Taymans committed
829 830 831 832 833 834
/**
 * gst_alocator_get_memory_type:
 * @allocator: a #GstAllocator
 *
 * Get the memory type allocated by this allocator
 *
Wim Taymans's avatar
Wim Taymans committed
835
 * Returns: the memory type provided by @allocator
Wim Taymans's avatar
Wim Taymans committed
836 837 838 839 840 841 842 843 844
 */
const gchar *
gst_allocator_get_memory_type (GstAllocator * allocator)
{
  g_return_val_if_fail (allocator != NULL, NULL);

  return allocator->info.mem_type;
}

845 846 847 848 849 850 851 852 853 854 855 856 857
/**
 * 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);

858 859
  GST_CAT_TRACE (GST_CAT_MEMORY, "alocator %p, %d->%d", allocator,
      allocator->refcount, allocator->refcount + 1);
860 861 862 863 864 865 866 867 868 869

  g_atomic_int_inc (&allocator->refcount);

  return allocator;
}

/**
 * gst_allocator_unref:
 * @allocator: a #GstAllocator
 *
Wim Taymans's avatar
Wim Taymans committed
870 871
 * Decreases the refcount of @allocator. When the refcount reaches 0, the notify
 * function of @allocator will be called and the allocator will be freed.
872 873 874 875 876 877
 */
void
gst_allocator_unref (GstAllocator * allocator)
{
  g_return_if_fail (allocator != NULL);

878 879
  GST_CAT_TRACE (GST_CAT_MEMORY, "allocator %p, %d->%d", allocator,
      allocator->refcount, allocator->refcount - 1);
880 881 882 883

  if (g_atomic_int_dec_and_test (&allocator->refcount)) {
    if (allocator->notify)
      allocator->notify (allocator->user_data);
884 885 886
#ifndef GST_DISABLE_TRACE
    _gst_alloc_trace_free (_gst_allocator_trace, allocator);
#endif
887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904
    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);

905 906
  GST_CAT_DEBUG (GST_CAT_MEMORY, "registering allocator %p with name \"%s\"",
      allocator, name);
907

Wim Taymans's avatar
Wim Taymans committed
908
  g_rw_lock_writer_lock (&lock);
909
  g_hash_table_insert (allocators, (gpointer) name, (gpointer) allocator);
Wim Taymans's avatar
Wim Taymans committed
910
  g_rw_lock_writer_unlock (&lock);
911 912 913
}

/**
914
 * gst_allocator_find:
915 916
 * @name: the name of the allocator
 *
917 918
 * Find a previously registered allocator with @name. When @name is NULL, the
 * default allocator will be returned.
919
 *
920 921
 * 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.
922
 */
923
GstAllocator *
924
gst_allocator_find (const gchar * name)
925
{
926
  GstAllocator *allocator;
927

Wim Taymans's avatar
Wim Taymans committed
928
  g_rw_lock_reader_lock (&lock);
929
  if (name) {
930
    allocator = g_hash_table_lookup (allocators, (gconstpointer) name);
931 932 933
  } else {
    allocator = _default_allocator;
  }
934 935
  if (allocator)
    gst_allocator_ref (allocator);
Wim Taymans's avatar
Wim Taymans committed
936
  g_rw_lock_reader_unlock (&lock);
937 938 939 940 941

  return allocator;
}

/**
942
 * gst_allocator_set_default: