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

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

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

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

73 74
G_DEFINE_BOXED_TYPE (GstMemory, gst_memory, (GBoxedCopyFunc) gst_memory_ref,
    (GBoxedFreeFunc) gst_memory_unref);
Wim Taymans's avatar
Wim Taymans committed
75

Wim Taymans's avatar
Wim Taymans committed
76 77 78 79 80
/**
 * gst_memory_alignment:
 *
 * The default memory alignment in bytes - 1
 * an alignment of 7 would be the same as what malloc() guarantees.
81 82
 */
#if defined(MEMORY_ALIGNMENT_MALLOC)
Wim Taymans's avatar
Wim Taymans committed
83
size_t gst_memory_alignment = 7;
84
#elif defined(MEMORY_ALIGNMENT_PAGESIZE)
Wim Taymans's avatar
Wim Taymans committed
85
/* we fill this in in the _init method */
Wim Taymans's avatar
Wim Taymans committed
86
size_t gst_memory_alignment = 0;
87
#elif defined(MEMORY_ALIGNMENT)
Wim Taymans's avatar
Wim Taymans committed
88
size_t gst_memory_alignment = MEMORY_ALIGNMENT - 1;
89 90
#else
#error "No memory alignment configured"
Wim Taymans's avatar
Wim Taymans committed
91
size_t gst_memory_alignment = 0;
92 93
#endif

94
struct _GstAllocator
95 96 97 98 99 100
{
  GQuark name;

  GstMemoryInfo info;
};

Wim Taymans's avatar
Wim Taymans committed
101
/* default memory implementation */
102 103 104
typedef struct
{
  GstMemory mem;
Wim Taymans's avatar
Wim Taymans committed
105
  gsize slice_size;
106 107 108 109
  guint8 *data;
  GFreeFunc free_func;
} GstMemoryDefault;

110
/* the default allocator */
111
static const GstAllocator *_default_allocator;
112 113

/* our predefined allocators */
114
static const GstAllocator *_default_mem_impl;
Wim Taymans's avatar
Wim Taymans committed
115

Wim Taymans's avatar
Wim Taymans committed
116
/* initialize the fields */
Wim Taymans's avatar
Wim Taymans committed
117
static void
118 119 120
_default_mem_init (GstMemoryDefault * mem, GstMemoryFlags flags,
    GstMemory * parent, gsize slice_size, gpointer data,
    GFreeFunc free_func, gsize maxsize, gsize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
121
{
122
  mem->mem.allocator = _default_mem_impl;
123
  mem->mem.flags = flags;
Wim Taymans's avatar
Wim Taymans committed
124
  mem->mem.refcount = 1;
Wim Taymans's avatar
Wim Taymans committed
125
  mem->mem.parent = parent ? gst_memory_ref (parent) : NULL;
Wim Taymans's avatar
Wim Taymans committed
126 127 128 129
  mem->mem.state = (flags & GST_MEMORY_FLAG_READONLY ? 0x5 : 0);
  mem->mem.maxsize = maxsize;
  mem->mem.offset = offset;
  mem->mem.size = size;
Wim Taymans's avatar
Wim Taymans committed
130 131 132
  mem->slice_size = slice_size;
  mem->data = data;
  mem->free_func = free_func;
Wim Taymans's avatar
Wim Taymans committed
133 134

  GST_DEBUG ("new memory %p", mem);
Wim Taymans's avatar
Wim Taymans committed
135 136
}

Wim Taymans's avatar
Wim Taymans committed
137
/* create a new memory block that manages the given memory */
Wim Taymans's avatar
Wim Taymans committed
138
static GstMemoryDefault *
139
_default_mem_new (GstMemoryFlags flags, GstMemory * parent, gpointer data,
Wim Taymans's avatar
Wim Taymans committed
140 141 142
    GFreeFunc free_func, gsize maxsize, gsize offset, gsize size)
{
  GstMemoryDefault *mem;
Wim Taymans's avatar
Wim Taymans committed
143
  gsize slice_size;
Wim Taymans's avatar
Wim Taymans committed
144

Wim Taymans's avatar
Wim Taymans committed
145 146 147
  slice_size = sizeof (GstMemoryDefault);

  mem = g_slice_alloc (slice_size);
148
  _default_mem_init (mem, flags, parent, slice_size,
Wim Taymans's avatar
Wim Taymans committed
149 150 151 152
      data, free_func, maxsize, offset, size);

  return mem;
}
153

Wim Taymans's avatar
Wim Taymans committed
154
/* allocate the memory and structure in one block */
Wim Taymans's avatar
Wim Taymans committed
155 156 157 158 159 160 161
static GstMemoryDefault *
_default_mem_new_block (gsize maxsize, gsize align, gsize offset, gsize size)
{
  GstMemoryDefault *mem;
  gsize aoffset, slice_size;
  guint8 *data;

162
  /* ensure configured alignment */
Wim Taymans's avatar
Wim Taymans committed
163
  align |= gst_memory_alignment;
Wim Taymans's avatar
Wim Taymans committed
164 165
  /* allocate more to compensate for alignment */
  maxsize += align;
Wim Taymans's avatar
Wim Taymans committed
166
  /* alloc header and data in one block */
Wim Taymans's avatar
Wim Taymans committed
167
  slice_size = sizeof (GstMemoryDefault) + maxsize;
Wim Taymans's avatar
Wim Taymans committed
168 169 170 171 172 173 174 175

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

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

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

Wim Taymans's avatar
Wim Taymans committed
178
  _default_mem_init (mem, 0, NULL, slice_size, data, NULL, maxsize,
Wim Taymans's avatar
Wim Taymans committed
179 180 181 182 183
      aoffset + offset, size);

  return mem;
}

184
static GstMemory *
185
_default_mem_alloc (const GstAllocator * allocator, gsize maxsize, gsize align)
186 187 188 189
{
  return (GstMemory *) _default_mem_new_block (maxsize, align, 0, maxsize);
}

Wim Taymans's avatar
Wim Taymans committed
190
static gpointer
Wim Taymans's avatar
Wim Taymans committed
191
_default_mem_map (GstMemoryDefault * mem, GstMapFlags flags)
Wim Taymans's avatar
Wim Taymans committed
192
{
Wim Taymans's avatar
Wim Taymans committed
193
  return mem->data;
194 195 196
}

static gboolean
Wim Taymans's avatar
Wim Taymans committed
197
_default_mem_unmap (GstMemoryDefault * mem)
198
{
Wim Taymans's avatar
Wim Taymans committed
199 200
  return TRUE;
}
201 202

static void
Wim Taymans's avatar
Wim Taymans committed
203
_default_mem_free (GstMemoryDefault * mem)
204
{
Wim Taymans's avatar
Wim Taymans committed
205 206
  GST_DEBUG ("free memory %p", mem);

Wim Taymans's avatar
Wim Taymans committed
207 208 209 210 211
  if (mem->mem.parent)
    gst_memory_unref (mem->mem.parent);

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

Wim Taymans's avatar
Wim Taymans committed
213
  g_slice_free1 (mem->slice_size, mem);
214 215
}

Wim Taymans's avatar
Wim Taymans committed
216
static GstMemoryDefault *
217
_default_mem_copy (GstMemoryDefault * mem, gssize offset, gsize size)
218 219 220
{
  GstMemoryDefault *copy;

Wim Taymans's avatar
Wim Taymans committed
221
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
222
    size = mem->mem.size > offset ? mem->mem.size - offset : 0;
Wim Taymans's avatar
Wim Taymans committed
223

Wim Taymans's avatar
Wim Taymans committed
224 225 226 227
  copy =
      _default_mem_new_block (mem->mem.maxsize, 0, mem->mem.offset + offset,
      size);
  memcpy (copy->data, mem->data, mem->mem.maxsize);
228

Wim Taymans's avatar
Wim Taymans committed
229
  return copy;
230 231
}

Wim Taymans's avatar
Wim Taymans committed
232
static GstMemoryDefault *
233
_default_mem_share (GstMemoryDefault * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
234 235 236 237 238 239 240 241
{
  GstMemoryDefault *sub;
  GstMemory *parent;

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

242
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
243
    size = mem->mem.size - offset;
244

Wim Taymans's avatar
Wim Taymans committed
245 246 247
  sub =
      _default_mem_new (parent->flags, parent, mem->data, NULL,
      mem->mem.maxsize, mem->mem.offset + offset, size);
Wim Taymans's avatar
Wim Taymans committed
248 249

  return sub;
250 251
}

Wim Taymans's avatar
Wim Taymans committed
252
static gboolean
Wim Taymans's avatar
Wim Taymans committed
253 254
_default_mem_is_span (GstMemoryDefault * mem1, GstMemoryDefault * mem2,
    gsize * offset)
255
{
Wim Taymans's avatar
Wim Taymans committed
256 257 258 259 260 261

  if (offset) {
    GstMemoryDefault *parent;

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

Wim Taymans's avatar
Wim Taymans committed
262
    *offset = mem1->mem.offset - parent->mem.offset;
Wim Taymans's avatar
Wim Taymans committed
263
  }
264

Wim Taymans's avatar
Wim Taymans committed
265
  /* and memory is contiguous */
Wim Taymans's avatar
Wim Taymans committed
266 267
  return mem1->data + mem1->mem.offset + mem1->mem.size ==
      mem2->data + mem2->mem.offset;
268 269 270
}

static GstMemory *
271
_fallback_copy (GstMemory * mem, gssize offset, gssize size)
272
{
273
  GstMemory *copy;
Wim Taymans's avatar
Wim Taymans committed
274
  GstMapInfo sinfo, dinfo;
275

Wim Taymans's avatar
Wim Taymans committed
276
  if (!gst_memory_map (mem, &sinfo, GST_MAP_READ))
Wim Taymans's avatar
Wim Taymans committed
277
    return NULL;
Wim Taymans's avatar
Wim Taymans committed
278

Wim Taymans's avatar
Wim Taymans committed
279
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
280 281
    size = sinfo.size > offset ? sinfo.size - offset : 0;

Wim Taymans's avatar
Wim Taymans committed
282 283
  /* 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
284 285 286 287 288 289
  if (!gst_memory_map (copy, &dinfo, GST_MAP_WRITE)) {
    GST_WARNING ("could not write map memory %p", copy);
    gst_memory_unmap (mem, &sinfo);
    return NULL;
  }

290
  memcpy (dinfo.data, sinfo.data + offset, size);
Wim Taymans's avatar
Wim Taymans committed
291 292
  gst_memory_unmap (copy, &dinfo);
  gst_memory_unmap (mem, &sinfo);
293

Wim Taymans's avatar
Wim Taymans committed
294
  return copy;
295 296 297
}

static gboolean
Wim Taymans's avatar
Wim Taymans committed
298
_fallback_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
299 300 301 302
{
  return FALSE;
}

Wim Taymans's avatar
Wim Taymans committed
303
static GRWLock lock;
304
static GHashTable *allocators;
305

Wim Taymans's avatar
Wim Taymans committed
306
void
Wim Taymans's avatar
Wim Taymans committed
307
_priv_gst_memory_initialize (void)
Wim Taymans's avatar
Wim Taymans committed
308 309
{
  static const GstMemoryInfo _mem_info = {
310
    (GstMemoryAllocFunction) _default_mem_alloc,
Wim Taymans's avatar
Wim Taymans committed
311 312 313 314
    (GstMemoryMapFunction) _default_mem_map,
    (GstMemoryUnmapFunction) _default_mem_unmap,
    (GstMemoryFreeFunction) _default_mem_free,
    (GstMemoryCopyFunction) _default_mem_copy,
Wim Taymans's avatar
Wim Taymans committed
315
    (GstMemoryShareFunction) _default_mem_share,
316 317
    (GstMemoryIsSpanFunction) _default_mem_is_span,
    NULL
Wim Taymans's avatar
Wim Taymans committed
318 319
  };

Wim Taymans's avatar
Wim Taymans committed
320
  g_rw_lock_init (&lock);
321
  allocators = g_hash_table_new (g_str_hash, g_str_equal);
322

323 324
#ifdef HAVE_GETPAGESIZE
#ifdef MEMORY_ALIGNMENT_PAGESIZE
Wim Taymans's avatar
Wim Taymans committed
325
  gst_memory_alignment = getpagesize () - 1;
326 327 328
#endif
#endif

Wim Taymans's avatar
Wim Taymans committed
329 330
  GST_DEBUG ("memory alignment: %" G_GSIZE_FORMAT, gst_memory_alignment);

331
  _default_mem_impl = gst_allocator_register (GST_ALLOCATOR_SYSMEM, &_mem_info);
332

333
  _default_allocator = _default_mem_impl;
334 335
}

Wim Taymans's avatar
Wim Taymans committed
336 337 338 339 340 341 342 343 344 345 346 347 348
/**
 * gst_memory_new_wrapped:
 * @flags: #GstMemoryFlags
 * @data: data to wrap
 * @free_func: function to free @data
 * @maxsize: allocated size of @data
 * @offset: offset in @data
 * @size: size of valid data
 *
 * Allocate a new memory block that wraps the given @data.
 *
 * Returns: a new #GstMemory.
 */
Wim Taymans's avatar
Wim Taymans committed
349 350 351
GstMemory *
gst_memory_new_wrapped (GstMemoryFlags flags, gpointer data,
    GFreeFunc free_func, gsize maxsize, gsize offset, gsize size)
352
{
Wim Taymans's avatar
Wim Taymans committed
353
  GstMemoryDefault *mem;
Wim Taymans's avatar
Wim Taymans committed
354

Wim Taymans's avatar
Wim Taymans committed
355 356 357
  g_return_val_if_fail (data != NULL, NULL);
  g_return_val_if_fail (offset + size <= maxsize, NULL);

Wim Taymans's avatar
Wim Taymans committed
358 359 360 361 362 363 364 365 366
  mem = _default_mem_new (flags, NULL, data, free_func, maxsize, offset, size);

  return (GstMemory *) mem;
}

/**
 * gst_memory_ref:
 * @mem: a #GstMemory
 *
Wim Taymans's avatar
Wim Taymans committed
367 368
 * Increases the refcount of @mem.
 *
Wim Taymans's avatar
Wim Taymans committed
369 370
 * Returns: @mem with increased refcount
 */
371 372 373 374 375
GstMemory *
gst_memory_ref (GstMemory * mem)
{
  g_return_val_if_fail (mem != NULL, NULL);

Wim Taymans's avatar
Wim Taymans committed
376 377
  GST_DEBUG ("memory %p, %d->%d", mem, mem->refcount, mem->refcount + 1);

378 379 380 381 382
  g_atomic_int_inc (&mem->refcount);

  return mem;
}

Wim Taymans's avatar
Wim Taymans committed
383 384 385
/**
 * gst_memory_unref:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
386 387 388
 *
 * 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
389
 */
390 391 392 393
void
gst_memory_unref (GstMemory * mem)
{
  g_return_if_fail (mem != NULL);
394
  g_return_if_fail (mem->allocator != NULL);
395

Wim Taymans's avatar
Wim Taymans committed
396 397
  GST_DEBUG ("memory %p, %d->%d", mem, mem->refcount, mem->refcount - 1);

398
  if (g_atomic_int_dec_and_test (&mem->refcount))
399
    mem->allocator->info.free (mem);
400 401
}

Wim Taymans's avatar
Wim Taymans committed
402 403 404
/**
 * gst_memory_get_sizes:
 * @mem: a #GstMemory
405
 * @offset: pointer to offset
Wim Taymans's avatar
Wim Taymans committed
406 407
 * @maxsize: pointer to maxsize
 *
408
 * Get the current @size, @offset and @maxsize of @mem.
Wim Taymans's avatar
Wim Taymans committed
409 410
 *
 * Returns: the current sizes of @mem
Wim Taymans's avatar
Wim Taymans committed
411
 */
412
gsize
413
gst_memory_get_sizes (GstMemory * mem, gsize * offset, gsize * maxsize)
414 415 416
{
  g_return_val_if_fail (mem != NULL, 0);

Wim Taymans's avatar
Wim Taymans committed
417 418 419 420 421 422
  if (offset)
    *offset = mem->offset;
  if (maxsize)
    *maxsize = mem->maxsize;

  return mem->size;
423 424
}

Wim Taymans's avatar
Wim Taymans committed
425 426 427 428 429 430 431 432 433 434
/**
 * 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
435
gst_memory_resize (GstMemory * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
436 437
{
  g_return_if_fail (mem != NULL);
438
  g_return_if_fail (gst_memory_is_writable (mem));
Wim Taymans's avatar
Wim Taymans committed
439 440
  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
441

Wim Taymans's avatar
Wim Taymans committed
442 443
  mem->offset += offset;
  mem->size = size;
Wim Taymans's avatar
Wim Taymans committed
444 445
}

446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
/**
 * gst_memory_is_writable:
 * @mem: a #GstMemory
 *
 * Check if @mem is writable.
 *
 * Returns: %TRUE is @mem is writable.
 */
gboolean
gst_memory_is_writable (GstMemory * mem)
{
  g_return_val_if_fail (mem != NULL, FALSE);

  return (mem->refcount == 1) &&
      ((mem->parent == NULL) || (mem->parent->refcount == 1)) &&
      ((mem->flags & GST_MEMORY_FLAG_READONLY) == 0);
}

Wim Taymans's avatar
Wim Taymans committed
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
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:
  {
    GST_DEBUG ("lock failed %p: state %d, access_mode %d", mem, state,
        access_mode);
    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));
}

510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535

/**
 * 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
536 537
    gst_memory_unref (mem);

538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
    if (result == NULL)
      goto cannot_copy;

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

  /* ERRORS */
cannot_copy:
  {
    GST_DEBUG ("cannot copy memory %p", mem);
    return NULL;
  }
cannot_map:
  {
    GST_DEBUG ("cannot map memory %p with flags %d", mem, flags);
Wim Taymans's avatar
Wim Taymans committed
555
    gst_memory_unref (result);
556 557 558 559
    return NULL;
  }
}

Wim Taymans's avatar
Wim Taymans committed
560 561 562
/**
 * gst_memory_map:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
563
 * @info: (out): pointer for info
Wim Taymans's avatar
Wim Taymans committed
564 565
 * @flags: mapping flags
 *
Wim Taymans's avatar
Wim Taymans committed
566 567
 * 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
568
 *
Wim Taymans's avatar
Wim Taymans committed
569
 * This function can return %FALSE for various reasons:
Wim Taymans's avatar
Wim Taymans committed
570 571 572
 * - the memory backed by @mem is not accessible with the given @flags.
 * - the memory was already mapped with a different mapping.
 *
Wim Taymans's avatar
Wim Taymans committed
573
 * @info and its contents remains valid for as long as @mem is alive and until
Wim Taymans's avatar
Wim Taymans committed
574 575 576 577 578
 * gst_memory_unmap() is called.
 *
 * For each gst_memory_map() call, a corresponding gst_memory_unmap() call
 * should be done.
 *
Wim Taymans's avatar
Wim Taymans committed
579
 * Returns: %TRUE if the map operation was successful.
Wim Taymans's avatar
Wim Taymans committed
580
 */
Wim Taymans's avatar
Wim Taymans committed
581 582
gboolean
gst_memory_map (GstMemory * mem, GstMapInfo * info, GstMapFlags flags)
583
{
Wim Taymans's avatar
Wim Taymans committed
584 585
  g_return_val_if_fail (mem != NULL, FALSE);
  g_return_val_if_fail (info != NULL, FALSE);
586

Wim Taymans's avatar
Wim Taymans committed
587 588 589
  if (!gst_memory_lock (mem, flags))
    goto lock_failed;

Wim Taymans's avatar
Wim Taymans committed
590
  info->data = mem->allocator->info.map (mem, mem->maxsize, flags);
Wim Taymans's avatar
Wim Taymans committed
591

Wim Taymans's avatar
Wim Taymans committed
592
  if (G_UNLIKELY (info->data == NULL))
Wim Taymans's avatar
Wim Taymans committed
593 594
    goto error;

Wim Taymans's avatar
Wim Taymans committed
595
  info->memory = mem;
Wim Taymans's avatar
Wim Taymans committed
596
  info->flags = flags;
Wim Taymans's avatar
Wim Taymans committed
597 598
  info->size = mem->size;
  info->maxsize = mem->maxsize - mem->offset;
599
  info->data = info->data + mem->offset;
600

Wim Taymans's avatar
Wim Taymans committed
601
  return TRUE;
602

Wim Taymans's avatar
Wim Taymans committed
603 604 605
  /* ERRORS */
lock_failed:
  {
606
    GST_DEBUG ("mem %p: lock %d failed", mem, flags);
Wim Taymans's avatar
Wim Taymans committed
607
    return FALSE;
Wim Taymans's avatar
Wim Taymans committed
608 609 610
  }
error:
  {
611
    /* something went wrong, restore the orginal state again */
Wim Taymans's avatar
Wim Taymans committed
612 613
    GST_ERROR ("mem %p: map failed", mem);
    gst_memory_unlock (mem);
Wim Taymans's avatar
Wim Taymans committed
614
    return FALSE;
615
  }
616 617
}

Wim Taymans's avatar
Wim Taymans committed
618 619 620
/**
 * gst_memory_unmap:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
621
 * @info: a #GstMapInfo
Wim Taymans's avatar
Wim Taymans committed
622
 *
Wim Taymans's avatar
Wim Taymans committed
623
 * Release the memory obtained with gst_memory_map()
Wim Taymans's avatar
Wim Taymans committed
624
 */
Wim Taymans's avatar
Wim Taymans committed
625
void
Wim Taymans's avatar
Wim Taymans committed
626
gst_memory_unmap (GstMemory * mem, GstMapInfo * info)
627
{
Wim Taymans's avatar
Wim Taymans committed
628
  g_return_if_fail (mem != NULL);
Wim Taymans's avatar
Wim Taymans committed
629 630
  g_return_if_fail (info != NULL);
  g_return_if_fail (info->memory == mem);
Wim Taymans's avatar
Wim Taymans committed
631 632
  /* there must be a ref */
  g_return_if_fail (g_atomic_int_get (&mem->state) >= 4);
633

Wim Taymans's avatar
Wim Taymans committed
634 635
  mem->allocator->info.unmap (mem);
  gst_memory_unlock (mem);
636 637
}

Wim Taymans's avatar
Wim Taymans committed
638 639 640 641
/**
 * gst_memory_copy:
 * @mem: a #GstMemory
 * @offset: an offset to copy
642
 * @size: size to copy or -1 to copy all bytes from offset
Wim Taymans's avatar
Wim Taymans committed
643 644 645 646 647 648 649
 *
 * 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.
 */
650
GstMemory *
651
gst_memory_copy (GstMemory * mem, gssize offset, gssize size)
652
{
Wim Taymans's avatar
Wim Taymans committed
653 654
  GstMemory *copy;

655
  g_return_val_if_fail (mem != NULL, NULL);
Wim Taymans's avatar
Wim Taymans committed
656 657 658
  g_return_val_if_fail (gst_memory_lock (mem, GST_MAP_READ), NULL);

  copy = mem->allocator->info.copy (mem, offset, size);
659

Wim Taymans's avatar
Wim Taymans committed
660 661 662
  gst_memory_unlock (mem);

  return copy;
663 664
}

Wim Taymans's avatar
Wim Taymans committed
665 666 667 668
/**
 * gst_memory_share:
 * @mem: a #GstMemory
 * @offset: an offset to share
669
 * @size: size to share or -1 to share bytes from offset
Wim Taymans's avatar
Wim Taymans committed
670
 *
671 672 673 674
 * 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
675 676 677
 *
 * Returns: a new #GstMemory.
 */
678
GstMemory *
679
gst_memory_share (GstMemory * mem, gssize offset, gssize size)
680 681 682
{
  g_return_val_if_fail (mem != NULL, NULL);

683
  return mem->allocator->info.share (mem, offset, size);
684 685
}

Wim Taymans's avatar
Wim Taymans committed
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
/**
 * 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
701
gboolean
702
gst_memory_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
Wim Taymans's avatar
Wim Taymans committed
703 704 705 706
{
  g_return_val_if_fail (mem1 != NULL, FALSE);
  g_return_val_if_fail (mem2 != NULL, FALSE);

707 708
  /* need to have the same allocators */
  if (mem1->allocator != mem2->allocator)
Wim Taymans's avatar
Wim Taymans committed
709 710
    return FALSE;

711 712 713
  /* need to have the same parent */
  if (mem1->parent == NULL || mem1->parent != mem2->parent)
    return FALSE;
Wim Taymans's avatar
Wim Taymans committed
714

715
  /* and memory is contiguous */
716
  if (!mem1->allocator->info.is_span (mem1, mem2, offset))
717 718 719
    return FALSE;

  return TRUE;
Wim Taymans's avatar
Wim Taymans committed
720
}
721 722

/**
723
 * gst_allocator_register:
724 725 726 727 728 729
 * @name: the name of the allocator
 * @info: #GstMemoryInfo
 *
 * Registers the memory allocator with @name and implementation functions
 * @info.
 *
730
 * All functions in @info are mandatory exept the copy and is_span
731 732 733 734 735
 * functions, which will have a default implementation when left NULL.
 *
 * The user_data field in @info will be passed to all calls of the alloc
 * function.
 *
736
 * Returns: a new #GstAllocator.
737
 */
738 739
const GstAllocator *
gst_allocator_register (const gchar * name, const GstMemoryInfo * info)
740
{
741
  GstAllocator *allocator;
742 743 744 745 746 747

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

  g_return_val_if_fail (name != NULL, NULL);
  g_return_val_if_fail (info != NULL, NULL);
748
  g_return_val_if_fail (info->alloc != NULL, NULL);
749 750 751
  g_return_val_if_fail (info->map != NULL, NULL);
  g_return_val_if_fail (info->unmap != NULL, NULL);
  g_return_val_if_fail (info->free != NULL, NULL);
752
  g_return_val_if_fail (info->share != NULL, NULL);
753

754
  allocator = g_slice_new (GstAllocator);
755 756 757 758 759 760
  allocator->name = g_quark_from_string (name);
  allocator->info = *info;
  INSTALL_FALLBACK (copy);
  INSTALL_FALLBACK (is_span);
#undef INSTALL_FALLBACK

Josep Torra's avatar
Josep Torra committed
761
  GST_DEBUG ("registering allocator \"%s\"", name);
762

Wim Taymans's avatar
Wim Taymans committed
763
  g_rw_lock_writer_lock (&lock);
764
  g_hash_table_insert (allocators, (gpointer) name, (gpointer) allocator);
Wim Taymans's avatar
Wim Taymans committed
765
  g_rw_lock_writer_unlock (&lock);
766 767 768 769 770

  return allocator;
}

/**
771
 * gst_allocator_find:
772 773
 * @name: the name of the allocator
 *
774 775
 * Find a previously registered allocator with @name. When @name is NULL, the
 * default allocator will be returned.
776
 *
777
 * Returns: a #GstAllocator or NULL when the allocator with @name was not
778 779
 * registered.
 */
780 781
const GstAllocator *
gst_allocator_find (const gchar * name)
782
{
783
  const GstAllocator *allocator;
784

Wim Taymans's avatar
Wim Taymans committed
785
  g_rw_lock_reader_lock (&lock);
786
  if (name) {
787
    allocator = g_hash_table_lookup (allocators, (gconstpointer) name);
788 789 790
  } else {
    allocator = _default_allocator;
  }
Wim Taymans's avatar
Wim Taymans committed
791
  g_rw_lock_reader_unlock (&lock);
792 793 794 795 796

  return allocator;
}

/**
797 798
 * gst_allocator_set_default:
 * @allocator: a #GstAllocator
799 800 801 802
 *
 * Set the default allocator.
 */
void
803
gst_allocator_set_default (const GstAllocator * allocator)
804 805 806
{
  g_return_if_fail (allocator != NULL);

Wim Taymans's avatar
Wim Taymans committed
807
  g_rw_lock_writer_lock (&lock);
808
  _default_allocator = allocator;
Wim Taymans's avatar
Wim Taymans committed
809
  g_rw_lock_writer_unlock (&lock);
810 811 812
}

/**
813
 * gst_allocator_alloc:
814
 * @allocator: (transfer none) (allow-none): a #GstAllocator to use
815 816 817 818 819 820
 * @maxsize: allocated size of @data
 * @align: alignment for the data
 *
 * Use @allocator to allocate a new memory block with memory that is at least
 * @maxsize big and has the given alignment.
 *
821 822
 * When @allocator is NULL, the default allocator will be used.
 *
823 824 825
 * @align is given as a bitmask so that @align + 1 equals the amount of bytes to
 * align to. For example, to align to 8 bytes, use an alignment of 7.
 *
826
 * Returns: (transfer full): a new #GstMemory.
827 828
 */
GstMemory *
829
gst_allocator_alloc (const GstAllocator * allocator, gsize maxsize, gsize align)
830 831 832 833 834 835
{
  g_return_val_if_fail (((align + 1) & align) == 0, NULL);

  if (allocator == NULL)
    allocator = _default_allocator;

836 837
  return allocator->info.alloc (allocator, maxsize, align,
      allocator->info.user_data);
838
}