gstmemory.c 26.9 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
static GstMemoryDefault *
Wim Taymans's avatar
Wim Taymans committed
172 173
_default_mem_new_block (GstMemoryFlags flags, gsize maxsize, gsize align,
    gsize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
174 175
{
  GstMemoryDefault *mem;
Wim Taymans's avatar
Wim Taymans committed
176
  gsize aoffset, slice_size, padding;
Wim Taymans's avatar
Wim Taymans committed
177 178
  guint8 *data;

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

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

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

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

Wim Taymans's avatar
Wim Taymans committed
195 196 197 198 199 200 201 202 203
  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,
      offset, size, NULL, NULL);
Wim Taymans's avatar
Wim Taymans committed
204 205 206 207

  return mem;
}

208
static GstMemory *
Wim Taymans's avatar
Wim Taymans committed
209 210
_default_alloc_alloc (GstAllocator * allocator, GstMemoryFlags flags,
    gsize maxsize, gsize offset, gsize size, gsize align, gpointer user_data)
211
{
Wim Taymans's avatar
Wim Taymans committed
212 213
  return (GstMemory *) _default_mem_new_block (flags, maxsize, align, offset,
      size);
214 215
}

Wim Taymans's avatar
Wim Taymans committed
216
static gpointer
Wim Taymans's avatar
Wim Taymans committed
217
_default_mem_map (GstMemoryDefault * mem, GstMapFlags flags)
Wim Taymans's avatar
Wim Taymans committed
218
{
Wim Taymans's avatar
Wim Taymans committed
219
  return mem->data;
220 221 222
}

static gboolean
Wim Taymans's avatar
Wim Taymans committed
223
_default_mem_unmap (GstMemoryDefault * mem)
224
{
Wim Taymans's avatar
Wim Taymans committed
225 226
  return TRUE;
}
227 228

static void
Wim Taymans's avatar
Wim Taymans committed
229
_default_mem_free (GstMemoryDefault * mem)
230
{
231
  GST_CAT_DEBUG (GST_CAT_MEMORY, "free memory %p", mem);
Wim Taymans's avatar
Wim Taymans committed
232

Wim Taymans's avatar
Wim Taymans committed
233 234 235
  if (mem->mem.parent)
    gst_memory_unref (mem->mem.parent);

236 237
  if (mem->notify)
    mem->notify (mem->user_data);
238

Wim Taymans's avatar
Wim Taymans committed
239
  g_slice_free1 (mem->slice_size, mem);
240 241
}

Wim Taymans's avatar
Wim Taymans committed
242
static GstMemoryDefault *
243
_default_mem_copy (GstMemoryDefault * mem, gssize offset, gsize size)
244 245 246
{
  GstMemoryDefault *copy;

Wim Taymans's avatar
Wim Taymans committed
247
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
248
    size = mem->mem.size > offset ? mem->mem.size - offset : 0;
Wim Taymans's avatar
Wim Taymans committed
249

Wim Taymans's avatar
Wim Taymans committed
250
  copy =
Wim Taymans's avatar
Wim Taymans committed
251
      _default_mem_new_block (0, mem->mem.maxsize, 0, mem->mem.offset + offset,
Wim Taymans's avatar
Wim Taymans committed
252 253
      size);
  memcpy (copy->data, mem->data, mem->mem.maxsize);
254
  GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "copy memory %p -> %p", mem, copy);
255

Wim Taymans's avatar
Wim Taymans committed
256
  return copy;
257 258
}

Wim Taymans's avatar
Wim Taymans committed
259
static GstMemoryDefault *
260
_default_mem_share (GstMemoryDefault * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
261 262 263 264 265 266 267 268
{
  GstMemoryDefault *sub;
  GstMemory *parent;

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

269
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
270
    size = mem->mem.size - offset;
271

Wim Taymans's avatar
Wim Taymans committed
272
  sub =
273 274
      _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
275 276

  return sub;
277 278
}

Wim Taymans's avatar
Wim Taymans committed
279
static gboolean
Wim Taymans's avatar
Wim Taymans committed
280 281
_default_mem_is_span (GstMemoryDefault * mem1, GstMemoryDefault * mem2,
    gsize * offset)
282
{
Wim Taymans's avatar
Wim Taymans committed
283 284 285 286 287 288

  if (offset) {
    GstMemoryDefault *parent;

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

Wim Taymans's avatar
Wim Taymans committed
289
    *offset = mem1->mem.offset - parent->mem.offset;
Wim Taymans's avatar
Wim Taymans committed
290
  }
291

Wim Taymans's avatar
Wim Taymans committed
292
  /* and memory is contiguous */
Wim Taymans's avatar
Wim Taymans committed
293 294
  return mem1->data + mem1->mem.offset + mem1->mem.size ==
      mem2->data + mem2->mem.offset;
295 296 297
}

static GstMemory *
298
_fallback_mem_copy (GstMemory * mem, gssize offset, gssize size)
299
{
300
  GstMemory *copy;
Wim Taymans's avatar
Wim Taymans committed
301
  GstMapInfo sinfo, dinfo;
302

Wim Taymans's avatar
Wim Taymans committed
303
  if (!gst_memory_map (mem, &sinfo, GST_MAP_READ))
Wim Taymans's avatar
Wim Taymans committed
304
    return NULL;
Wim Taymans's avatar
Wim Taymans committed
305

Wim Taymans's avatar
Wim Taymans committed
306
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
307 308
    size = sinfo.size > offset ? sinfo.size - offset : 0;

Wim Taymans's avatar
Wim Taymans committed
309
  /* use the same allocator as the memory we copy  */
Wim Taymans's avatar
Wim Taymans committed
310
  copy = gst_allocator_alloc (mem->allocator, 0, size, 0, size, mem->align);
Wim Taymans's avatar
Wim Taymans committed
311
  if (!gst_memory_map (copy, &dinfo, GST_MAP_WRITE)) {
312
    GST_CAT_WARNING (GST_CAT_MEMORY, "could not write map memory %p", copy);
Wim Taymans's avatar
Wim Taymans committed
313 314 315 316
    gst_memory_unmap (mem, &sinfo);
    return NULL;
  }

317
  memcpy (dinfo.data, sinfo.data + offset, size);
318
  GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "copy memory %p -> %p", mem, copy);
Wim Taymans's avatar
Wim Taymans committed
319 320
  gst_memory_unmap (copy, &dinfo);
  gst_memory_unmap (mem, &sinfo);
321

Wim Taymans's avatar
Wim Taymans committed
322
  return copy;
323 324 325
}

static gboolean
326
_fallback_mem_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
327 328 329 330
{
  return FALSE;
}

Wim Taymans's avatar
Wim Taymans committed
331
static GRWLock lock;
332
static GHashTable *allocators;
333

334 335 336 337 338 339
static void
_priv_sysmem_notify (gpointer user_data)
{
  g_warning ("The default memory allocator was freed!");
}

Wim Taymans's avatar
Wim Taymans committed
340
void
Wim Taymans's avatar
Wim Taymans committed
341
_priv_gst_memory_initialize (void)
Wim Taymans's avatar
Wim Taymans committed
342 343
{
  static const GstMemoryInfo _mem_info = {
Wim Taymans's avatar
Wim Taymans committed
344
    GST_ALLOCATOR_SYSMEM,
345
    (GstAllocatorAllocFunction) _default_alloc_alloc,
Wim Taymans's avatar
Wim Taymans committed
346 347 348 349
    (GstMemoryMapFunction) _default_mem_map,
    (GstMemoryUnmapFunction) _default_mem_unmap,
    (GstMemoryFreeFunction) _default_mem_free,
    (GstMemoryCopyFunction) _default_mem_copy,
Wim Taymans's avatar
Wim Taymans committed
350
    (GstMemoryShareFunction) _default_mem_share,
351
    (GstMemoryIsSpanFunction) _default_mem_is_span,
Wim Taymans's avatar
Wim Taymans committed
352 353
  };

354 355 356 357 358
#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
359
  g_rw_lock_init (&lock);
360
  allocators = g_hash_table_new (g_str_hash, g_str_equal);
361

362 363
#ifdef HAVE_GETPAGESIZE
#ifdef MEMORY_ALIGNMENT_PAGESIZE
Wim Taymans's avatar
Wim Taymans committed
364
  gst_memory_alignment = getpagesize () - 1;
365 366 367
#endif
#endif

368 369
  GST_CAT_DEBUG (GST_CAT_MEMORY, "memory alignment: %" G_GSIZE_FORMAT,
      gst_memory_alignment);
Wim Taymans's avatar
Wim Taymans committed
370

371
  _default_mem_impl = gst_allocator_new (&_mem_info, NULL, _priv_sysmem_notify);
372

373 374 375
  _default_allocator = gst_allocator_ref (_default_mem_impl);
  gst_allocator_register (GST_ALLOCATOR_SYSMEM,
      gst_allocator_ref (_default_mem_impl));
376 377
}

Wim Taymans's avatar
Wim Taymans committed
378 379 380 381 382 383 384
/**
 * gst_memory_new_wrapped:
 * @flags: #GstMemoryFlags
 * @data: data to wrap
 * @maxsize: allocated size of @data
 * @offset: offset in @data
 * @size: size of valid data
385 386
 * @user_data: user_data
 * @notify: called with @user_data when the memory is freed
Wim Taymans's avatar
Wim Taymans committed
387 388 389
 *
 * Allocate a new memory block that wraps the given @data.
 *
Wim Taymans's avatar
Wim Taymans committed
390 391 392
 * 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
393 394
 * Returns: a new #GstMemory.
 */
Wim Taymans's avatar
Wim Taymans committed
395 396
GstMemory *
gst_memory_new_wrapped (GstMemoryFlags flags, gpointer data,
397 398
    gsize maxsize, gsize offset, gsize size, gpointer user_data,
    GDestroyNotify notify)
399
{
Wim Taymans's avatar
Wim Taymans committed
400
  GstMemoryDefault *mem;
Wim Taymans's avatar
Wim Taymans committed
401

Wim Taymans's avatar
Wim Taymans committed
402 403 404
  g_return_val_if_fail (data != NULL, NULL);
  g_return_val_if_fail (offset + size <= maxsize, NULL);

405 406 407
  mem =
      _default_mem_new (flags, NULL, data, maxsize, offset, size, user_data,
      notify);
Wim Taymans's avatar
Wim Taymans committed
408

409 410 411 412
#ifndef GST_DISABLE_TRACE
  _gst_alloc_trace_new (_gst_memory_trace, mem);
#endif

Wim Taymans's avatar
Wim Taymans committed
413 414 415 416 417 418 419
  return (GstMemory *) mem;
}

/**
 * gst_memory_ref:
 * @mem: a #GstMemory
 *
Wim Taymans's avatar
Wim Taymans committed
420 421
 * Increases the refcount of @mem.
 *
Wim Taymans's avatar
Wim Taymans committed
422 423
 * Returns: @mem with increased refcount
 */
424 425 426 427 428
GstMemory *
gst_memory_ref (GstMemory * mem)
{
  g_return_val_if_fail (mem != NULL, NULL);

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

432 433 434 435 436
  g_atomic_int_inc (&mem->refcount);

  return mem;
}

Wim Taymans's avatar
Wim Taymans committed
437 438 439
/**
 * gst_memory_unref:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
440 441 442
 *
 * 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
443
 */
444 445 446 447
void
gst_memory_unref (GstMemory * mem)
{
  g_return_if_fail (mem != NULL);
448
  g_return_if_fail (mem->allocator != NULL);
449

450 451
  GST_CAT_TRACE (GST_CAT_MEMORY, "memory %p, %d->%d", mem, mem->refcount,
      mem->refcount - 1);
Wim Taymans's avatar
Wim Taymans committed
452

453
  if (g_atomic_int_dec_and_test (&mem->refcount)) {
Wim Taymans's avatar
Wim Taymans committed
454
    /* there should be no outstanding mappings */
455
    g_return_if_fail (g_atomic_int_get (&mem->state) < 4);
456 457 458
#ifndef GST_DISABLE_TRACE
    _gst_alloc_trace_free (_gst_memory_trace, mem);
#endif
459
    mem->allocator->info.mem_free (mem);
460
  }
461 462
}

463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
/**
 * 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
478 479 480
/**
 * gst_memory_get_sizes:
 * @mem: a #GstMemory
481
 * @offset: pointer to offset
Wim Taymans's avatar
Wim Taymans committed
482 483
 * @maxsize: pointer to maxsize
 *
484
 * Get the current @size, @offset and @maxsize of @mem.
Wim Taymans's avatar
Wim Taymans committed
485 486
 *
 * Returns: the current sizes of @mem
Wim Taymans's avatar
Wim Taymans committed
487
 */
488
gsize
489
gst_memory_get_sizes (GstMemory * mem, gsize * offset, gsize * maxsize)
490 491 492
{
  g_return_val_if_fail (mem != NULL, 0);

Wim Taymans's avatar
Wim Taymans committed
493 494 495 496 497 498
  if (offset)
    *offset = mem->offset;
  if (maxsize)
    *maxsize = mem->maxsize;

  return mem->size;
499 500
}

Wim Taymans's avatar
Wim Taymans committed
501 502 503 504 505 506 507 508
/**
 * 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
509 510 511
 *
 * #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
512 513
 */
void
514
gst_memory_resize (GstMemory * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
515 516
{
  g_return_if_fail (mem != NULL);
Wim Taymans's avatar
Wim Taymans committed
517 518
  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
519

Wim Taymans's avatar
Wim Taymans committed
520 521 522 523 524 525 526 527
  /* 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
528 529
  mem->offset += offset;
  mem->size = size;
Wim Taymans's avatar
Wim Taymans committed
530 531
}

Wim Taymans's avatar
Wim Taymans committed
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
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:
  {
557 558
    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
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
    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));
}

578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603

/**
 * 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
604 605
    gst_memory_unref (mem);

606 607 608 609 610 611 612 613 614 615 616
    if (result == NULL)
      goto cannot_copy;

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

  /* ERRORS */
cannot_copy:
  {
617
    GST_CAT_DEBUG (GST_CAT_MEMORY, "cannot copy memory %p", mem);
618 619 620 621
    return NULL;
  }
cannot_map:
  {
622 623
    GST_CAT_DEBUG (GST_CAT_MEMORY, "cannot map memory %p with flags %d", mem,
        flags);
Wim Taymans's avatar
Wim Taymans committed
624
    gst_memory_unref (result);
625 626 627 628
    return NULL;
  }
}

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

Wim Taymans's avatar
Wim Taymans committed
656 657 658
  if (!gst_memory_lock (mem, flags))
    goto lock_failed;

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

Wim Taymans's avatar
Wim Taymans committed
661
  if (G_UNLIKELY (info->data == NULL))
Wim Taymans's avatar
Wim Taymans committed
662 663
    goto error;

Wim Taymans's avatar
Wim Taymans committed
664
  info->memory = mem;
Wim Taymans's avatar
Wim Taymans committed
665
  info->flags = flags;
Wim Taymans's avatar
Wim Taymans committed
666 667
  info->size = mem->size;
  info->maxsize = mem->maxsize - mem->offset;
668
  info->data = info->data + mem->offset;
669

Wim Taymans's avatar
Wim Taymans committed
670
  return TRUE;
671

Wim Taymans's avatar
Wim Taymans committed
672 673 674
  /* ERRORS */
lock_failed:
  {
675
    GST_CAT_DEBUG (GST_CAT_MEMORY, "mem %p: lock %d failed", mem, flags);
Wim Taymans's avatar
Wim Taymans committed
676
    return FALSE;
Wim Taymans's avatar
Wim Taymans committed
677 678 679
  }
error:
  {
680
    /* something went wrong, restore the orginal state again */
681
    GST_CAT_ERROR (GST_CAT_MEMORY, "mem %p: map failed", mem);
Wim Taymans's avatar
Wim Taymans committed
682
    gst_memory_unlock (mem);
Wim Taymans's avatar
Wim Taymans committed
683
    return FALSE;
684
  }
685 686
}

Wim Taymans's avatar
Wim Taymans committed
687 688 689
/**
 * gst_memory_unmap:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
690
 * @info: a #GstMapInfo
Wim Taymans's avatar
Wim Taymans committed
691
 *
Wim Taymans's avatar
Wim Taymans committed
692
 * Release the memory obtained with gst_memory_map()
Wim Taymans's avatar
Wim Taymans committed
693
 */
Wim Taymans's avatar
Wim Taymans committed
694
void
Wim Taymans's avatar
Wim Taymans committed
695
gst_memory_unmap (GstMemory * mem, GstMapInfo * info)
696
{
Wim Taymans's avatar
Wim Taymans committed
697
  g_return_if_fail (mem != NULL);
Wim Taymans's avatar
Wim Taymans committed
698 699
  g_return_if_fail (info != NULL);
  g_return_if_fail (info->memory == mem);
Wim Taymans's avatar
Wim Taymans committed
700 701
  /* there must be a ref */
  g_return_if_fail (g_atomic_int_get (&mem->state) >= 4);
702

703
  mem->allocator->info.mem_unmap (mem);
Wim Taymans's avatar
Wim Taymans committed
704
  gst_memory_unlock (mem);
705 706
}

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

724
  g_return_val_if_fail (mem != NULL, NULL);
Wim Taymans's avatar
Wim Taymans committed
725

726
  copy = mem->allocator->info.mem_copy (mem, offset, size);
727

728 729 730
#ifndef GST_DISABLE_TRACE
  _gst_alloc_trace_new (_gst_memory_trace, copy);
#endif
Wim Taymans's avatar
Wim Taymans committed
731 732

  return copy;
733 734
}

Wim Taymans's avatar
Wim Taymans committed
735 736 737 738
/**
 * gst_memory_share:
 * @mem: a #GstMemory
 * @offset: an offset to share
739
 * @size: size to share or -1 to share bytes from offset
Wim Taymans's avatar
Wim Taymans committed
740
 *
741 742 743 744
 * 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
745 746 747
 *
 * Returns: a new #GstMemory.
 */
748
GstMemory *
749
gst_memory_share (GstMemory * mem, gssize offset, gssize size)
750
{
751 752
  GstMemory *shared;

753
  g_return_val_if_fail (mem != NULL, NULL);
754 755
  g_return_val_if_fail (!GST_MEMORY_FLAG_IS_SET (mem, GST_MEMORY_FLAG_NO_SHARE),
      NULL);
756

757 758 759 760 761 762 763
  shared = mem->allocator->info.mem_share (mem, offset, size);

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

  return shared;
764 765
}

Wim Taymans's avatar
Wim Taymans committed
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
/**
 * 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
781
gboolean
782
gst_memory_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
Wim Taymans's avatar
Wim Taymans committed
783 784 785 786
{
  g_return_val_if_fail (mem1 != NULL, FALSE);
  g_return_val_if_fail (mem2 != NULL, FALSE);

787 788
  /* need to have the same allocators */
  if (mem1->allocator != mem2->allocator)
Wim Taymans's avatar
Wim Taymans committed
789 790
    return FALSE;

791 792 793
  /* need to have the same parent */
  if (mem1->parent == NULL || mem1->parent != mem2->parent)
    return FALSE;
Wim Taymans's avatar
Wim Taymans committed
794

795
  /* and memory is contiguous */
796
  if (!mem1->allocator->info.mem_is_span (mem1, mem2, offset))
797 798 799
    return FALSE;

  return TRUE;
Wim Taymans's avatar
Wim Taymans committed
800
}
801 802

/**
803
 * gst_allocator_register:
804 805 806
 * @info: a #GstMemoryInfo
 * @user_data: user data
 * @notify: a #GDestroyNotify for @user_data
807
 *
808
 * Create a new memory allocator with @info and @user_data.
809
 *
810
 * All functions in @info are mandatory exept the copy and is_span
811 812
 * functions, which will have a default implementation when left NULL.
 *
Wim Taymans's avatar
Wim Taymans committed
813 814
 * 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.
815
 *
816
 * Returns: a new #GstAllocator.
817
 */
818 819 820
GstAllocator *
gst_allocator_new (const GstMemoryInfo * info, gpointer user_data,
    GDestroyNotify notify)
821
{
822
  GstAllocator *allocator;
823 824 825 826 827

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

  g_return_val_if_fail (info != NULL, NULL);
828
  g_return_val_if_fail (info->alloc != NULL, NULL);
829 830 831 832
  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);
833

834
  allocator = g_slice_new (GstAllocator);
835
  allocator->refcount = 1;
836
  allocator->info = *info;
837 838 839 840
  allocator->user_data = user_data;
  allocator->notify = notify;
  INSTALL_FALLBACK (mem_copy);
  INSTALL_FALLBACK (mem_is_span);
841 842
#undef INSTALL_FALLBACK

843 844 845 846 847
  GST_CAT_DEBUG (GST_CAT_MEMORY, "new allocator %p", allocator);

#ifndef GST_DISABLE_TRACE
  _gst_alloc_trace_new (_gst_allocator_trace, allocator);
#endif
848 849 850 851

  return allocator;
}

Wim Taymans's avatar
Wim Taymans committed
852 853 854 855 856 857
/**
 * gst_alocator_get_memory_type:
 * @allocator: a #GstAllocator
 *
 * Get the memory type allocated by this allocator
 *
Wim Taymans's avatar
Wim Taymans committed
858
 * Returns: the memory type provided by @allocator
Wim Taymans's avatar
Wim Taymans committed
859 860 861 862 863 864 865 866 867
 */
const gchar *
gst_allocator_get_memory_type (GstAllocator * allocator)
{
  g_return_val_if_fail (allocator != NULL, NULL);

  return allocator->info.mem_type;
}

868 869 870 871 872 873 874 875 876 877 878 879 880
/**
 * 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);

881 882
  GST_CAT_TRACE (GST_CAT_MEMORY, "alocator %p, %d->%d", allocator,
      allocator->refcount, allocator->refcount + 1);
883 884 885 886 887 888 889 890 891 892

  g_atomic_int_inc (&allocator->refcount);

  return allocator;
}

/**
 * gst_allocator_unref:
 * @allocator: a #GstAllocator
 *
Wim Taymans's avatar
Wim Taymans committed
893 894
 * Decreases the refcount of @allocator. When the refcount reaches 0, the notify
 * function of @allocator will be called and the allocator will be freed.
895 896 897 898 899 900
 */
void
gst_allocator_unref (GstAllocator * allocator)
{
  g_return_if_fail (allocator != NULL);

901 902
  GST_CAT_TRACE (GST_CAT_MEMORY, "allocator %p, %d->%d", allocator,
      allocator->refcount, allocator->refcount - 1);
903 904 905 906

  if (g_atomic_int_dec_and_test (&allocator->refcount)) {
    if (allocator->notify)
      allocator->notify (allocator->user_data);
907 908 909
#ifndef GST_DISABLE_TRACE
    _gst_alloc_trace_free (_gst_allocator_trace, allocator);
#endif
910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927
    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);

928 929
  GST_CAT_DEBUG (GST_CAT_MEMORY, "registering allocator %p with name \"%s\"",
      allocator, name);
930

Wim Taymans's avatar
Wim Taymans committed
931
  g_rw_lock_writer_lock (&lock);
932
  g_hash_table_insert (allocators, (gpointer) name, (gpointer) allocator);
Wim Taymans's avatar
Wim Taymans committed
933
  g_rw_lock_writer_unlock (&lock);
934 935 936
}

/**
937
 * gst_allocator_find:
938 939
 * @name: the name of the allocator
 *
940 941
 * Find a previously registered allocator with @name. When @name is NULL, the
 * default allocator will be returned.
942
 *