gstmemory.c 28.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
Wim Taymans's avatar
Wim Taymans committed
41 42
 * gst_allocator_find(). gst_allocator_set_default() can be used to change the
 * default allocator.
43
 *
Wim Taymans's avatar
Wim Taymans committed
44
 * New memory can be created with gst_memory_new_wrapped() that wraps the memory
45
 * allocated elsewhere.
Wim Taymans's avatar
Wim Taymans committed
46 47 48 49 50 51 52 53
 *
 * 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
54
 * The call will return a pointer to offset bytes into the region of memory.
Wim Taymans's avatar
Wim Taymans committed
55 56
 * After the memory access is completed, gst_memory_unmap() should be called.
 *
David Schleef's avatar
David Schleef committed
57
 * Memory can be copied with gst_memory_copy(), which will return a writable
Wim Taymans's avatar
Wim Taymans committed
58 59 60 61
 * 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
62
 * Memory can be efficiently merged when gst_memory_is_span() returns TRUE.
Wim Taymans's avatar
Wim Taymans committed
63
 *
Wim Taymans's avatar
Wim Taymans committed
64
 * Last reviewed on 2012-03-28 (0.11.3)
Wim Taymans's avatar
Wim Taymans committed
65
 */
66

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

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

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

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

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

86 87 88 89
G_DEFINE_BOXED_TYPE (GstAllocationParams, gst_allocation_params,
    (GBoxedCopyFunc) gst_allocation_params_copy,
    (GBoxedFreeFunc) gst_allocation_params_free);

90
#if defined(MEMORY_ALIGNMENT_MALLOC)
Wim Taymans's avatar
Wim Taymans committed
91
size_t gst_memory_alignment = 7;
92
#elif defined(MEMORY_ALIGNMENT_PAGESIZE)
Wim Taymans's avatar
Wim Taymans committed
93
/* we fill this in in the _init method */
Wim Taymans's avatar
Wim Taymans committed
94
size_t gst_memory_alignment = 0;
95
#elif defined(MEMORY_ALIGNMENT)
Wim Taymans's avatar
Wim Taymans committed
96
size_t gst_memory_alignment = MEMORY_ALIGNMENT - 1;
97 98
#else
#error "No memory alignment configured"
Wim Taymans's avatar
Wim Taymans committed
99
size_t gst_memory_alignment = 0;
100 101
#endif

102
struct _GstAllocator
103
{
104
  gint refcount;
105 106

  GstMemoryInfo info;
107 108 109

  gpointer user_data;
  GDestroyNotify notify;
110 111
};

Wim Taymans's avatar
Wim Taymans committed
112
/* default memory implementation */
113 114 115
typedef struct
{
  GstMemory mem;
Wim Taymans's avatar
Wim Taymans committed
116
  gsize slice_size;
117
  guint8 *data;
118 119
  gpointer user_data;
  GDestroyNotify notify;
120 121
} 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
_default_mem_init (GstMemoryDefault * mem, GstMemoryFlags flags,
    GstMemory * parent, gsize slice_size, gpointer data,
132 133
    gsize maxsize, gsize offset, gsize size, gsize align,
    gpointer user_data, GDestroyNotify notify)
Wim Taymans's avatar
Wim Taymans committed
134
{
135
  mem->mem.allocator = _default_mem_impl;
136
  mem->mem.flags = flags;
Wim Taymans's avatar
Wim Taymans committed
137
  mem->mem.refcount = 1;
Wim Taymans's avatar
Wim Taymans committed
138
  mem->mem.parent = parent ? gst_memory_ref (parent) : NULL;
139
  mem->mem.state = (flags & GST_MEMORY_FLAG_READONLY ? 0x1 : 0);
Wim Taymans's avatar
Wim Taymans committed
140
  mem->mem.maxsize = maxsize;
141
  mem->mem.align = align;
Wim Taymans's avatar
Wim Taymans committed
142 143
  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

Wim Taymans's avatar
Wim Taymans committed
149 150 151
  GST_CAT_DEBUG (GST_CAT_MEMORY, "new memory %p, maxsize:%" G_GSIZE_FORMAT
      " offset:%" G_GSIZE_FORMAT " size:%" G_GSIZE_FORMAT, mem, maxsize,
      offset, size);
Wim Taymans's avatar
Wim Taymans committed
152 153
}

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

Wim Taymans's avatar
Wim Taymans committed
163 164 165
  slice_size = sizeof (GstMemoryDefault);

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

  return mem;
}
171

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

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

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

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

194
  /* do alignment */
Wim Taymans's avatar
Wim Taymans committed
195 196 197 198 199
  if ((aoffset = ((guintptr) data & align))) {
    aoffset = (align + 1) - aoffset;
    data += aoffset;
    maxsize -= aoffset;
  }
Wim Taymans's avatar
Wim Taymans committed
200

Wim Taymans's avatar
Wim Taymans committed
201 202 203 204 205 206 207 208
  if (offset && (flags & GST_MEMORY_FLAG_ZERO_PREFIXED))
    memset (data, 0, offset);

  padding = maxsize - (offset + size);
  if (padding && (flags & GST_MEMORY_FLAG_ZERO_PADDED))
    memset (data + offset + size, 0, padding);

  _default_mem_init (mem, flags, NULL, slice_size, data, maxsize,
209
      offset, size, align, NULL, NULL);
Wim Taymans's avatar
Wim Taymans committed
210 211 212 213

  return mem;
}

214
static GstMemory *
215 216
_default_alloc_alloc (GstAllocator * allocator, gsize size,
    GstAllocationParams * params, gpointer user_data)
217
{
218 219 220 221
  gsize maxsize = size + params->prefix + params->padding;

  return (GstMemory *) _default_mem_new_block (params->flags,
      maxsize, params->align, params->prefix, size);
222 223
}

Wim Taymans's avatar
Wim Taymans committed
224
static gpointer
Wim Taymans's avatar
Wim Taymans committed
225
_default_mem_map (GstMemoryDefault * mem, GstMapFlags flags)
Wim Taymans's avatar
Wim Taymans committed
226
{
Wim Taymans's avatar
Wim Taymans committed
227
  return mem->data;
228 229 230
}

static gboolean
Wim Taymans's avatar
Wim Taymans committed
231
_default_mem_unmap (GstMemoryDefault * mem)
232
{
Wim Taymans's avatar
Wim Taymans committed
233 234
  return TRUE;
}
235 236

static void
Wim Taymans's avatar
Wim Taymans committed
237
_default_mem_free (GstMemoryDefault * mem)
238
{
239
  GST_CAT_DEBUG (GST_CAT_MEMORY, "free memory %p", mem);
Wim Taymans's avatar
Wim Taymans committed
240

Wim Taymans's avatar
Wim Taymans committed
241 242 243
  if (mem->mem.parent)
    gst_memory_unref (mem->mem.parent);

244 245
  if (mem->notify)
    mem->notify (mem->user_data);
246

Wim Taymans's avatar
Wim Taymans committed
247
  g_slice_free1 (mem->slice_size, mem);
248 249
}

Wim Taymans's avatar
Wim Taymans committed
250
static GstMemoryDefault *
251
_default_mem_copy (GstMemoryDefault * mem, gssize offset, gsize size)
252 253 254
{
  GstMemoryDefault *copy;

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

Wim Taymans's avatar
Wim Taymans committed
258
  copy =
Wim Taymans's avatar
Wim Taymans committed
259
      _default_mem_new_block (0, mem->mem.maxsize, 0, mem->mem.offset + offset,
Wim Taymans's avatar
Wim Taymans committed
260 261
      size);
  memcpy (copy->data, mem->data, mem->mem.maxsize);
262
  GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "copy memory %p -> %p", mem, copy);
263

Wim Taymans's avatar
Wim Taymans committed
264
  return copy;
265 266
}

Wim Taymans's avatar
Wim Taymans committed
267
static GstMemoryDefault *
268
_default_mem_share (GstMemoryDefault * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
269 270 271 272 273 274 275 276
{
  GstMemoryDefault *sub;
  GstMemory *parent;

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

277
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
278
    size = mem->mem.size - offset;
279

Wim Taymans's avatar
Wim Taymans committed
280
  sub =
281
      _default_mem_new (parent->flags, parent, mem->data,
282 283
      mem->mem.maxsize, mem->mem.offset + offset, size, mem->mem.align, NULL,
      NULL);
Wim Taymans's avatar
Wim Taymans committed
284 285

  return sub;
286 287
}

Wim Taymans's avatar
Wim Taymans committed
288
static gboolean
Wim Taymans's avatar
Wim Taymans committed
289 290
_default_mem_is_span (GstMemoryDefault * mem1, GstMemoryDefault * mem2,
    gsize * offset)
291
{
Wim Taymans's avatar
Wim Taymans committed
292 293 294 295 296 297

  if (offset) {
    GstMemoryDefault *parent;

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

Wim Taymans's avatar
Wim Taymans committed
298
    *offset = mem1->mem.offset - parent->mem.offset;
Wim Taymans's avatar
Wim Taymans committed
299
  }
300

Wim Taymans's avatar
Wim Taymans committed
301
  /* and memory is contiguous */
Wim Taymans's avatar
Wim Taymans committed
302 303
  return mem1->data + mem1->mem.offset + mem1->mem.size ==
      mem2->data + mem2->mem.offset;
304 305 306
}

static GstMemory *
307
_fallback_mem_copy (GstMemory * mem, gssize offset, gssize size)
308
{
309
  GstMemory *copy;
Wim Taymans's avatar
Wim Taymans committed
310
  GstMapInfo sinfo, dinfo;
311
  GstAllocationParams params = { 0, 0, 0, mem->align, };
312

Wim Taymans's avatar
Wim Taymans committed
313
  if (!gst_memory_map (mem, &sinfo, GST_MAP_READ))
Wim Taymans's avatar
Wim Taymans committed
314
    return NULL;
Wim Taymans's avatar
Wim Taymans committed
315

Wim Taymans's avatar
Wim Taymans committed
316
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
317 318
    size = sinfo.size > offset ? sinfo.size - offset : 0;

Wim Taymans's avatar
Wim Taymans committed
319
  /* use the same allocator as the memory we copy  */
320
  copy = gst_allocator_alloc (mem->allocator, size, &params);
Wim Taymans's avatar
Wim Taymans committed
321
  if (!gst_memory_map (copy, &dinfo, GST_MAP_WRITE)) {
322
    GST_CAT_WARNING (GST_CAT_MEMORY, "could not write map memory %p", copy);
Wim Taymans's avatar
Wim Taymans committed
323 324 325 326
    gst_memory_unmap (mem, &sinfo);
    return NULL;
  }

327
  memcpy (dinfo.data, sinfo.data + offset, size);
328
  GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "copy memory %p -> %p", mem, copy);
Wim Taymans's avatar
Wim Taymans committed
329 330
  gst_memory_unmap (copy, &dinfo);
  gst_memory_unmap (mem, &sinfo);
331

Wim Taymans's avatar
Wim Taymans committed
332
  return copy;
333 334 335
}

static gboolean
336
_fallback_mem_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
337 338 339 340
{
  return FALSE;
}

Wim Taymans's avatar
Wim Taymans committed
341
static GRWLock lock;
342
static GHashTable *allocators;
343

344 345 346 347 348 349
static void
_priv_sysmem_notify (gpointer user_data)
{
  g_warning ("The default memory allocator was freed!");
}

Wim Taymans's avatar
Wim Taymans committed
350
void
Wim Taymans's avatar
Wim Taymans committed
351
_priv_gst_memory_initialize (void)
Wim Taymans's avatar
Wim Taymans committed
352 353
{
  static const GstMemoryInfo _mem_info = {
Wim Taymans's avatar
Wim Taymans committed
354
    GST_ALLOCATOR_SYSMEM,
355
    (GstAllocatorAllocFunction) _default_alloc_alloc,
Wim Taymans's avatar
Wim Taymans committed
356 357 358 359
    (GstMemoryMapFunction) _default_mem_map,
    (GstMemoryUnmapFunction) _default_mem_unmap,
    (GstMemoryFreeFunction) _default_mem_free,
    (GstMemoryCopyFunction) _default_mem_copy,
Wim Taymans's avatar
Wim Taymans committed
360
    (GstMemoryShareFunction) _default_mem_share,
361
    (GstMemoryIsSpanFunction) _default_mem_is_span,
Wim Taymans's avatar
Wim Taymans committed
362 363
  };

364 365 366 367 368
#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
369
  g_rw_lock_init (&lock);
370
  allocators = g_hash_table_new (g_str_hash, g_str_equal);
371

372 373
#ifdef HAVE_GETPAGESIZE
#ifdef MEMORY_ALIGNMENT_PAGESIZE
Wim Taymans's avatar
Wim Taymans committed
374
  gst_memory_alignment = getpagesize () - 1;
375 376 377
#endif
#endif

378 379
  GST_CAT_DEBUG (GST_CAT_MEMORY, "memory alignment: %" G_GSIZE_FORMAT,
      gst_memory_alignment);
Wim Taymans's avatar
Wim Taymans committed
380

381
  _default_mem_impl = gst_allocator_new (&_mem_info, NULL, _priv_sysmem_notify);
382

383 384 385
  _default_allocator = gst_allocator_ref (_default_mem_impl);
  gst_allocator_register (GST_ALLOCATOR_SYSMEM,
      gst_allocator_ref (_default_mem_impl));
386 387
}

Wim Taymans's avatar
Wim Taymans committed
388 389 390 391 392 393 394
/**
 * gst_memory_new_wrapped:
 * @flags: #GstMemoryFlags
 * @data: data to wrap
 * @maxsize: allocated size of @data
 * @offset: offset in @data
 * @size: size of valid data
395 396
 * @user_data: user_data
 * @notify: called with @user_data when the memory is freed
Wim Taymans's avatar
Wim Taymans committed
397 398 399
 *
 * Allocate a new memory block that wraps the given @data.
 *
Wim Taymans's avatar
Wim Taymans committed
400 401 402
 * The prefix/padding must be filled with 0 if @flags contains
 * #GST_MEMORY_FLAG_ZERO_PREFIXED and #GST_MEMORY_FLAG_ZERO_PADDED respectively.
 *
Wim Taymans's avatar
Wim Taymans committed
403 404
 * Returns: a new #GstMemory.
 */
Wim Taymans's avatar
Wim Taymans committed
405 406
GstMemory *
gst_memory_new_wrapped (GstMemoryFlags flags, gpointer data,
407 408
    gsize maxsize, gsize offset, gsize size, gpointer user_data,
    GDestroyNotify notify)
409
{
Wim Taymans's avatar
Wim Taymans committed
410
  GstMemoryDefault *mem;
Wim Taymans's avatar
Wim Taymans committed
411

Wim Taymans's avatar
Wim Taymans committed
412 413 414
  g_return_val_if_fail (data != NULL, NULL);
  g_return_val_if_fail (offset + size <= maxsize, NULL);

415
  mem =
416
      _default_mem_new (flags, NULL, data, maxsize, offset, size, 0, user_data,
417
      notify);
Wim Taymans's avatar
Wim Taymans committed
418

419 420 421 422
#ifndef GST_DISABLE_TRACE
  _gst_alloc_trace_new (_gst_memory_trace, mem);
#endif

Wim Taymans's avatar
Wim Taymans committed
423 424 425 426 427 428 429
  return (GstMemory *) mem;
}

/**
 * gst_memory_ref:
 * @mem: a #GstMemory
 *
Wim Taymans's avatar
Wim Taymans committed
430 431
 * Increases the refcount of @mem.
 *
Wim Taymans's avatar
Wim Taymans committed
432 433
 * Returns: @mem with increased refcount
 */
434 435 436 437 438
GstMemory *
gst_memory_ref (GstMemory * mem)
{
  g_return_val_if_fail (mem != NULL, NULL);

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

442 443 444 445 446
  g_atomic_int_inc (&mem->refcount);

  return mem;
}

Wim Taymans's avatar
Wim Taymans committed
447 448 449
/**
 * gst_memory_unref:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
450 451 452
 *
 * 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
453
 */
454 455 456 457
void
gst_memory_unref (GstMemory * mem)
{
  g_return_if_fail (mem != NULL);
458
  g_return_if_fail (mem->allocator != NULL);
459

460 461
  GST_CAT_TRACE (GST_CAT_MEMORY, "memory %p, %d->%d", mem, mem->refcount,
      mem->refcount - 1);
Wim Taymans's avatar
Wim Taymans committed
462

463
  if (g_atomic_int_dec_and_test (&mem->refcount)) {
Wim Taymans's avatar
Wim Taymans committed
464
    /* there should be no outstanding mappings */
465
    g_return_if_fail (g_atomic_int_get (&mem->state) < 4);
466 467 468
#ifndef GST_DISABLE_TRACE
    _gst_alloc_trace_free (_gst_memory_trace, mem);
#endif
469
    mem->allocator->info.mem_free (mem);
470
  }
471 472
}

473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
/**
 * 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
488 489 490
/**
 * gst_memory_get_sizes:
 * @mem: a #GstMemory
491
 * @offset: pointer to offset
Wim Taymans's avatar
Wim Taymans committed
492 493
 * @maxsize: pointer to maxsize
 *
494
 * Get the current @size, @offset and @maxsize of @mem.
Wim Taymans's avatar
Wim Taymans committed
495 496
 *
 * Returns: the current sizes of @mem
Wim Taymans's avatar
Wim Taymans committed
497
 */
498
gsize
499
gst_memory_get_sizes (GstMemory * mem, gsize * offset, gsize * maxsize)
500 501 502
{
  g_return_val_if_fail (mem != NULL, 0);

Wim Taymans's avatar
Wim Taymans committed
503 504 505 506 507 508
  if (offset)
    *offset = mem->offset;
  if (maxsize)
    *maxsize = mem->maxsize;

  return mem->size;
509 510
}

Wim Taymans's avatar
Wim Taymans committed
511 512 513 514 515 516 517 518
/**
 * 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.
Wim Taymans's avatar
Wim Taymans committed
519 520 521
 *
 * #GST_MEMORY_FLAG_ZERO_PREFIXED and #GST_MEMORY_FLAG_ZERO_PADDED will be
 * cleared when offset or padding is increased respectively.
Wim Taymans's avatar
Wim Taymans committed
522 523
 */
void
524
gst_memory_resize (GstMemory * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
525 526
{
  g_return_if_fail (mem != NULL);
Wim Taymans's avatar
Wim Taymans committed
527 528
  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
529

Wim Taymans's avatar
Wim Taymans committed
530 531 532 533 534 535 536 537
  /* if we increase the prefix, we can't guarantee it is still 0 filled */
  if ((offset > 0) && GST_MEMORY_IS_ZERO_PREFIXED (mem))
    GST_MEMORY_FLAG_UNSET (mem, GST_MEMORY_FLAG_ZERO_PREFIXED);

  /* if we increase the padding, we can't guarantee it is still 0 filled */
  if ((offset + size < mem->size) && GST_MEMORY_IS_ZERO_PADDED (mem))
    GST_MEMORY_FLAG_UNSET (mem, GST_MEMORY_FLAG_ZERO_PADDED);

Wim Taymans's avatar
Wim Taymans committed
538 539
  mem->offset += offset;
  mem->size = size;
Wim Taymans's avatar
Wim Taymans committed
540 541
}

Wim Taymans's avatar
Wim Taymans committed
542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
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:
  {
567 568
    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
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
    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));
}

588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613

/**
 * 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
614 615
    gst_memory_unref (mem);

616 617 618 619 620 621 622 623 624 625 626
    if (result == NULL)
      goto cannot_copy;

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

  /* ERRORS */
cannot_copy:
  {
627
    GST_CAT_DEBUG (GST_CAT_MEMORY, "cannot copy memory %p", mem);
628 629 630 631
    return NULL;
  }
cannot_map:
  {
632 633
    GST_CAT_DEBUG (GST_CAT_MEMORY, "cannot map memory %p with flags %d", mem,
        flags);
Wim Taymans's avatar
Wim Taymans committed
634
    gst_memory_unref (result);
635 636 637 638
    return NULL;
  }
}

Wim Taymans's avatar
Wim Taymans committed
639 640 641
/**
 * gst_memory_map:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
642
 * @info: (out): pointer for info
Wim Taymans's avatar
Wim Taymans committed
643 644
 * @flags: mapping flags
 *
Wim Taymans's avatar
Wim Taymans committed
645 646
 * 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
647
 *
Wim Taymans's avatar
Wim Taymans committed
648
 * This function can return %FALSE for various reasons:
Wim Taymans's avatar
Wim Taymans committed
649 650 651
 * - the memory backed by @mem is not accessible with the given @flags.
 * - the memory was already mapped with a different mapping.
 *
652 653
 * @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
654 655 656 657
 *
 * For each gst_memory_map() call, a corresponding gst_memory_unmap() call
 * should be done.
 *
Wim Taymans's avatar
Wim Taymans committed
658
 * Returns: %TRUE if the map operation was successful.
Wim Taymans's avatar
Wim Taymans committed
659
 */
Wim Taymans's avatar
Wim Taymans committed
660 661
gboolean
gst_memory_map (GstMemory * mem, GstMapInfo * info, GstMapFlags flags)
662
{
Wim Taymans's avatar
Wim Taymans committed
663 664
  g_return_val_if_fail (mem != NULL, FALSE);
  g_return_val_if_fail (info != NULL, FALSE);
665

Wim Taymans's avatar
Wim Taymans committed
666 667 668
  if (!gst_memory_lock (mem, flags))
    goto lock_failed;

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

Wim Taymans's avatar
Wim Taymans committed
671
  if (G_UNLIKELY (info->data == NULL))
Wim Taymans's avatar
Wim Taymans committed
672 673
    goto error;

Wim Taymans's avatar
Wim Taymans committed
674
  info->memory = mem;
Wim Taymans's avatar
Wim Taymans committed
675
  info->flags = flags;
Wim Taymans's avatar
Wim Taymans committed
676 677
  info->size = mem->size;
  info->maxsize = mem->maxsize - mem->offset;
678
  info->data = info->data + mem->offset;
679

Wim Taymans's avatar
Wim Taymans committed
680
  return TRUE;
681

Wim Taymans's avatar
Wim Taymans committed
682 683 684
  /* ERRORS */
lock_failed:
  {
685
    GST_CAT_DEBUG (GST_CAT_MEMORY, "mem %p: lock %d failed", mem, flags);
Wim Taymans's avatar
Wim Taymans committed
686
    return FALSE;
Wim Taymans's avatar
Wim Taymans committed
687 688 689
  }
error:
  {
690
    /* something went wrong, restore the orginal state again */
691
    GST_CAT_ERROR (GST_CAT_MEMORY, "mem %p: map failed", mem);
Wim Taymans's avatar
Wim Taymans committed
692
    gst_memory_unlock (mem);
Wim Taymans's avatar
Wim Taymans committed
693
    return FALSE;
694
  }
695 696
}

Wim Taymans's avatar
Wim Taymans committed
697 698 699
/**
 * gst_memory_unmap:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
700
 * @info: a #GstMapInfo
Wim Taymans's avatar
Wim Taymans committed
701
 *
Wim Taymans's avatar
Wim Taymans committed
702
 * Release the memory obtained with gst_memory_map()
Wim Taymans's avatar
Wim Taymans committed
703
 */
Wim Taymans's avatar
Wim Taymans committed
704
void
Wim Taymans's avatar
Wim Taymans committed
705
gst_memory_unmap (GstMemory * mem, GstMapInfo * info)
706
{
Wim Taymans's avatar
Wim Taymans committed
707
  g_return_if_fail (mem != NULL);
Wim Taymans's avatar
Wim Taymans committed
708 709
  g_return_if_fail (info != NULL);
  g_return_if_fail (info->memory == mem);
Wim Taymans's avatar
Wim Taymans committed
710 711
  /* there must be a ref */
  g_return_if_fail (g_atomic_int_get (&mem->state) >= 4);
712

713
  mem->allocator->info.mem_unmap (mem);
Wim Taymans's avatar
Wim Taymans committed
714
  gst_memory_unlock (mem);
715 716
}

Wim Taymans's avatar
Wim Taymans committed
717 718 719 720
/**
 * gst_memory_copy:
 * @mem: a #GstMemory
 * @offset: an offset to copy
721
 * @size: size to copy or -1 to copy all bytes from offset
Wim Taymans's avatar
Wim Taymans committed
722 723 724 725 726 727 728
 *
 * 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.
 */
729
GstMemory *
730
gst_memory_copy (GstMemory * mem, gssize offset, gssize size)
731
{
Wim Taymans's avatar
Wim Taymans committed
732 733
  GstMemory *copy;

734
  g_return_val_if_fail (mem != NULL, NULL);
Wim Taymans's avatar
Wim Taymans committed
735

736
  copy = mem->allocator->info.mem_copy (mem, offset, size);
737

738 739 740
#ifndef GST_DISABLE_TRACE
  _gst_alloc_trace_new (_gst_memory_trace, copy);
#endif
Wim Taymans's avatar
Wim Taymans committed
741 742

  return copy;
743 744
}

Wim Taymans's avatar
Wim Taymans committed
745 746 747 748
/**
 * gst_memory_share:
 * @mem: a #GstMemory
 * @offset: an offset to share
749
 * @size: size to share or -1 to share bytes from offset
Wim Taymans's avatar
Wim Taymans committed
750
 *
751 752 753 754
 * 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
755 756 757
 *
 * Returns: a new #GstMemory.
 */
758
GstMemory *
759
gst_memory_share (GstMemory * mem, gssize offset, gssize size)
760
{
761 762
  GstMemory *shared;

763
  g_return_val_if_fail (mem != NULL, NULL);
764 765
  g_return_val_if_fail (!GST_MEMORY_FLAG_IS_SET (mem, GST_MEMORY_FLAG_NO_SHARE),
      NULL);
766

767 768 769 770 771 772 773
  shared = mem->allocator->info.mem_share (mem, offset, size);

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

  return shared;
774 775
}

Wim Taymans's avatar
Wim Taymans committed
776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
/**
 * 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
791
gboolean
792
gst_memory_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
Wim Taymans's avatar
Wim Taymans committed
793 794 795 796
{
  g_return_val_if_fail (mem1 != NULL, FALSE);
  g_return_val_if_fail (mem2 != NULL, FALSE);

797 798
  /* need to have the same allocators */
  if (mem1->allocator != mem2->allocator)
Wim Taymans's avatar
Wim Taymans committed
799 800
    return FALSE;

801 802 803
  /* need to have the same parent */
  if (mem1->parent == NULL || mem1->parent != mem2->parent)
    return FALSE;
Wim Taymans's avatar
Wim Taymans committed
804

805
  /* and memory is contiguous */
806
  if (!mem1->allocator->info.mem_is_span (mem1, mem2, offset))
807 808 809
    return FALSE;

  return TRUE;
Wim Taymans's avatar
Wim Taymans committed
810
}
811 812

/**
Wim Taymans's avatar
Wim Taymans committed
813
 * gst_allocator_new:
814 815 816
 * @info: a #GstMemoryInfo
 * @user_data: user data
 * @notify: a #GDestroyNotify for @user_data
817
 *
818
 * Create a new memory allocator with @info and @user_data.
819
 *
820
 * All functions in @info are mandatory exept the copy and is_span
821 822
 * functions, which will have a default implementation when left NULL.
 *
Wim Taymans's avatar
Wim Taymans committed
823 824
 * 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.
825
 *
826
 * Returns: a new #GstAllocator.
827
 */
828 829 830
GstAllocator *
gst_allocator_new (const GstMemoryInfo * info, gpointer user_data,
    GDestroyNotify notify)
831
{
832
  GstAllocator *allocator;
833 834 835 836 837

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

  g_return_val_if_fail (info != NULL, NULL);
838
  g_return_val_if_fail (info->alloc != NULL, NULL);
839 840 841 842
  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);
843

844
  allocator = g_slice_new (GstAllocator);
845
  allocator->refcount = 1;
846
  allocator->info = *info;
847 848 849 850
  allocator->user_data = user_data;
  allocator->notify = notify;
  INSTALL_FALLBACK (mem_copy);
  INSTALL_FALLBACK (mem_is_span);
851 852
#undef INSTALL_FALLBACK

853 854 855 856 857
  GST_CAT_DEBUG (GST_CAT_MEMORY, "new allocator %p", allocator);

#ifndef GST_DISABLE_TRACE
  _gst_alloc_trace_new (_gst_allocator_trace, allocator);
#endif
858 859 860 861

  return allocator;
}

Wim Taymans's avatar
Wim Taymans committed
862
/**
Wim Taymans's avatar
Wim Taymans committed
863
 * gst_allocator_get_memory_type:
Wim Taymans's avatar
Wim Taymans committed
864 865 866 867
 * @allocator: a #GstAllocator
 *
 * Get the memory type allocated by this allocator
 *
Wim Taymans's avatar
Wim Taymans committed
868
 * Returns: the memory type provided by @allocator
Wim Taymans's avatar
Wim Taymans committed
869 870 871 872 873 874 875 876 877
 */
const gchar *
gst_allocator_get_memory_type (GstAllocator * allocator)
{
  g_return_val_if_fail (allocator != NULL, NULL);

  return allocator->info.mem_type;
}

878
/**
Wim Taymans's avatar
Wim Taymans committed
879
 * gst_allocator_ref:
880 881 882 883 884 885 886 887 888 889 890
 * @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);

Wim Taymans's avatar
Wim Taymans committed
891
  GST_CAT_TRACE (GST_CAT_MEMORY, "allocator %p, %d->%d", allocator,
892
      allocator->refcount, allocator->refcount + 1);
893 894 895 896 897 898 899 900 901 902

  g_atomic_int_inc (&allocator->refcount);

  return allocator;
}

/**
 * gst_allocator_unref:
 * @allocator: a #GstAllocator
 *
Wim Taymans's avatar
Wim Taymans committed
903 904
 * Decreases the refcount of @allocator. When the refcount reaches 0, the notify
 * function of @allocator will be called and the allocator will be freed.
905 906 907 908 909 910
 */
void
gst_allocator_unref (GstAllocator * allocator)
{
  g_return_if_fail (allocator != NULL);

911 912
  GST_CAT_TRACE (GST_CAT_MEMORY, "allocator %p, %d->%d", allocator,
      allocator->refcount, allocator->refcount - 1);
913 914 915 916

  if (g_atomic_int_dec_and_test (&allocator->refcount)) {
    if (allocator->notify)
      allocator->notify (allocator->user_data);
917 918 919
#ifndef GST_DISABLE_TRACE
    _gst_alloc_trace_free (_gst_allocator_trace, allocator);
#endif
920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937
    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);

938 939
  GST_CAT_DEBUG (GST_CAT_MEMORY, "registering allocator %p with name \"%s\"",
      allocator, name);
940

Wim Taymans's avatar
Wim Taymans committed
941
  g_rw_lock_writer_lock (&lock);
942
  g_hash_table_insert (allocators, (gpointer) name, (gpointer) allocator);
Wim Taymans's avatar
Wim Taymans committed
943
  g_rw_lock_writer_unlock (&lock);
944 945 946
}

/**
947
 * gst_allocator_find:
948 949
 * @name: the name of the allocator
 *
950 951
 * Find a previously registered allocator with @name. When @name is NULL, the
 * default allocator will be returned.
952
 *
953 954
 * 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.
955
 */
956
GstAllocator *
957
gst_allocator_find (const gchar * name)