gstmemory.c 24 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

76 77 78
G_DEFINE_BOXED_TYPE (GstAllocator, gst_allocator,
    (GBoxedCopyFunc) gst_allocator_ref, (GBoxedFreeFunc) gst_allocator_unref);

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

97
struct _GstAllocator
98
{
99
  gint refcount;
100 101

  GstMemoryInfo info;
102 103 104

  gpointer user_data;
  GDestroyNotify notify;
105 106
};

Wim Taymans's avatar
Wim Taymans committed
107
/* default memory implementation */
108 109 110
typedef struct
{
  GstMemory mem;
Wim Taymans's avatar
Wim Taymans committed
111
  gsize slice_size;
112 113 114 115
  guint8 *data;
  GFreeFunc free_func;
} GstMemoryDefault;

116
/* the default allocator */
117
static GstAllocator *_default_allocator;
118 119

/* our predefined allocators */
120
static GstAllocator *_default_mem_impl;
Wim Taymans's avatar
Wim Taymans committed
121

Wim Taymans's avatar
Wim Taymans committed
122
/* initialize the fields */
Wim Taymans's avatar
Wim Taymans committed
123
static void
124 125 126
_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
127
{
128
  mem->mem.allocator = _default_mem_impl;
129
  mem->mem.flags = flags;
Wim Taymans's avatar
Wim Taymans committed
130
  mem->mem.refcount = 1;
Wim Taymans's avatar
Wim Taymans committed
131
  mem->mem.parent = parent ? gst_memory_ref (parent) : NULL;
Wim Taymans's avatar
Wim Taymans committed
132 133 134 135
  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
136 137 138
  mem->slice_size = slice_size;
  mem->data = data;
  mem->free_func = free_func;
Wim Taymans's avatar
Wim Taymans committed
139 140

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

Wim Taymans's avatar
Wim Taymans committed
143
/* create a new memory block that manages the given memory */
Wim Taymans's avatar
Wim Taymans committed
144
static GstMemoryDefault *
145
_default_mem_new (GstMemoryFlags flags, GstMemory * parent, gpointer data,
Wim Taymans's avatar
Wim Taymans committed
146 147 148
    GFreeFunc free_func, gsize maxsize, gsize offset, gsize size)
{
  GstMemoryDefault *mem;
Wim Taymans's avatar
Wim Taymans committed
149
  gsize slice_size;
Wim Taymans's avatar
Wim Taymans committed
150

Wim Taymans's avatar
Wim Taymans committed
151 152 153
  slice_size = sizeof (GstMemoryDefault);

  mem = g_slice_alloc (slice_size);
154
  _default_mem_init (mem, flags, parent, slice_size,
Wim Taymans's avatar
Wim Taymans committed
155 156 157 158
      data, free_func, maxsize, offset, size);

  return mem;
}
159

Wim Taymans's avatar
Wim Taymans committed
160
/* allocate the memory and structure in one block */
Wim Taymans's avatar
Wim Taymans committed
161 162 163 164 165 166 167
static GstMemoryDefault *
_default_mem_new_block (gsize maxsize, gsize align, gsize offset, gsize size)
{
  GstMemoryDefault *mem;
  gsize aoffset, slice_size;
  guint8 *data;

168
  /* ensure configured alignment */
Wim Taymans's avatar
Wim Taymans committed
169
  align |= gst_memory_alignment;
Wim Taymans's avatar
Wim Taymans committed
170 171
  /* allocate more to compensate for alignment */
  maxsize += align;
Wim Taymans's avatar
Wim Taymans committed
172
  /* alloc header and data in one block */
Wim Taymans's avatar
Wim Taymans committed
173
  slice_size = sizeof (GstMemoryDefault) + maxsize;
Wim Taymans's avatar
Wim Taymans committed
174 175 176 177 178 179 180 181

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

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

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

Wim Taymans's avatar
Wim Taymans committed
184
  _default_mem_init (mem, 0, NULL, slice_size, data, NULL, maxsize,
Wim Taymans's avatar
Wim Taymans committed
185 186 187 188 189
      aoffset + offset, size);

  return mem;
}

190
static GstMemory *
191 192
_default_alloc_alloc (GstAllocator * allocator, gsize maxsize, gsize align,
    gpointer user_data)
193 194 195 196
{
  return (GstMemory *) _default_mem_new_block (maxsize, align, 0, maxsize);
}

Wim Taymans's avatar
Wim Taymans committed
197
static gpointer
Wim Taymans's avatar
Wim Taymans committed
198
_default_mem_map (GstMemoryDefault * mem, GstMapFlags flags)
Wim Taymans's avatar
Wim Taymans committed
199
{
Wim Taymans's avatar
Wim Taymans committed
200
  return mem->data;
201 202 203
}

static gboolean
Wim Taymans's avatar
Wim Taymans committed
204
_default_mem_unmap (GstMemoryDefault * mem)
205
{
Wim Taymans's avatar
Wim Taymans committed
206 207
  return TRUE;
}
208 209

static void
Wim Taymans's avatar
Wim Taymans committed
210
_default_mem_free (GstMemoryDefault * mem)
211
{
Wim Taymans's avatar
Wim Taymans committed
212 213
  GST_DEBUG ("free memory %p", mem);

Wim Taymans's avatar
Wim Taymans committed
214 215 216 217 218
  if (mem->mem.parent)
    gst_memory_unref (mem->mem.parent);

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

Wim Taymans's avatar
Wim Taymans committed
220
  g_slice_free1 (mem->slice_size, mem);
221 222
}

Wim Taymans's avatar
Wim Taymans committed
223
static GstMemoryDefault *
224
_default_mem_copy (GstMemoryDefault * mem, gssize offset, gsize size)
225 226 227
{
  GstMemoryDefault *copy;

Wim Taymans's avatar
Wim Taymans committed
228
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
229
    size = mem->mem.size > offset ? mem->mem.size - offset : 0;
Wim Taymans's avatar
Wim Taymans committed
230

Wim Taymans's avatar
Wim Taymans committed
231 232 233 234
  copy =
      _default_mem_new_block (mem->mem.maxsize, 0, mem->mem.offset + offset,
      size);
  memcpy (copy->data, mem->data, mem->mem.maxsize);
235
  GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "copy memory %p -> %p", mem, copy);
236

Wim Taymans's avatar
Wim Taymans committed
237
  return copy;
238 239
}

Wim Taymans's avatar
Wim Taymans committed
240
static GstMemoryDefault *
241
_default_mem_share (GstMemoryDefault * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
242 243 244 245 246 247 248 249
{
  GstMemoryDefault *sub;
  GstMemory *parent;

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

250
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
251
    size = mem->mem.size - offset;
252

Wim Taymans's avatar
Wim Taymans committed
253 254 255
  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
256 257

  return sub;
258 259
}

Wim Taymans's avatar
Wim Taymans committed
260
static gboolean
Wim Taymans's avatar
Wim Taymans committed
261 262
_default_mem_is_span (GstMemoryDefault * mem1, GstMemoryDefault * mem2,
    gsize * offset)
263
{
Wim Taymans's avatar
Wim Taymans committed
264 265 266 267 268 269

  if (offset) {
    GstMemoryDefault *parent;

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

Wim Taymans's avatar
Wim Taymans committed
270
    *offset = mem1->mem.offset - parent->mem.offset;
Wim Taymans's avatar
Wim Taymans committed
271
  }
272

Wim Taymans's avatar
Wim Taymans committed
273
  /* and memory is contiguous */
Wim Taymans's avatar
Wim Taymans committed
274 275
  return mem1->data + mem1->mem.offset + mem1->mem.size ==
      mem2->data + mem2->mem.offset;
276 277 278
}

static GstMemory *
279
_fallback_mem_copy (GstMemory * mem, gssize offset, gssize size)
280
{
281
  GstMemory *copy;
Wim Taymans's avatar
Wim Taymans committed
282
  GstMapInfo sinfo, dinfo;
283

Wim Taymans's avatar
Wim Taymans committed
284
  if (!gst_memory_map (mem, &sinfo, GST_MAP_READ))
Wim Taymans's avatar
Wim Taymans committed
285
    return NULL;
Wim Taymans's avatar
Wim Taymans committed
286

Wim Taymans's avatar
Wim Taymans committed
287
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
288 289
    size = sinfo.size > offset ? sinfo.size - offset : 0;

Wim Taymans's avatar
Wim Taymans committed
290 291
  /* 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
292 293 294 295 296 297
  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;
  }

298
  memcpy (dinfo.data, sinfo.data + offset, size);
299
  GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "copy memory %p -> %p", mem, copy);
Wim Taymans's avatar
Wim Taymans committed
300 301
  gst_memory_unmap (copy, &dinfo);
  gst_memory_unmap (mem, &sinfo);
302

Wim Taymans's avatar
Wim Taymans committed
303
  return copy;
304 305 306
}

static gboolean
307
_fallback_mem_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
308 309 310 311
{
  return FALSE;
}

Wim Taymans's avatar
Wim Taymans committed
312
static GRWLock lock;
313
static GHashTable *allocators;
314

315 316 317 318 319 320
static void
_priv_sysmem_notify (gpointer user_data)
{
  g_warning ("The default memory allocator was freed!");
}

Wim Taymans's avatar
Wim Taymans committed
321
void
Wim Taymans's avatar
Wim Taymans committed
322
_priv_gst_memory_initialize (void)
Wim Taymans's avatar
Wim Taymans committed
323 324
{
  static const GstMemoryInfo _mem_info = {
Wim Taymans's avatar
Wim Taymans committed
325
    GST_ALLOCATOR_SYSMEM,
326
    (GstAllocatorAllocFunction) _default_alloc_alloc,
Wim Taymans's avatar
Wim Taymans committed
327 328 329 330
    (GstMemoryMapFunction) _default_mem_map,
    (GstMemoryUnmapFunction) _default_mem_unmap,
    (GstMemoryFreeFunction) _default_mem_free,
    (GstMemoryCopyFunction) _default_mem_copy,
Wim Taymans's avatar
Wim Taymans committed
331
    (GstMemoryShareFunction) _default_mem_share,
332
    (GstMemoryIsSpanFunction) _default_mem_is_span,
Wim Taymans's avatar
Wim Taymans committed
333 334
  };

Wim Taymans's avatar
Wim Taymans committed
335
  g_rw_lock_init (&lock);
336
  allocators = g_hash_table_new (g_str_hash, g_str_equal);
337

338 339
#ifdef HAVE_GETPAGESIZE
#ifdef MEMORY_ALIGNMENT_PAGESIZE
Wim Taymans's avatar
Wim Taymans committed
340
  gst_memory_alignment = getpagesize () - 1;
341 342 343
#endif
#endif

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

346
  _default_mem_impl = gst_allocator_new (&_mem_info, NULL, _priv_sysmem_notify);
347

348 349 350
  _default_allocator = gst_allocator_ref (_default_mem_impl);
  gst_allocator_register (GST_ALLOCATOR_SYSMEM,
      gst_allocator_ref (_default_mem_impl));
351 352
}

Wim Taymans's avatar
Wim Taymans committed
353 354 355 356 357 358 359 360 361 362 363 364 365
/**
 * 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
366 367 368
GstMemory *
gst_memory_new_wrapped (GstMemoryFlags flags, gpointer data,
    GFreeFunc free_func, gsize maxsize, gsize offset, gsize size)
369
{
Wim Taymans's avatar
Wim Taymans committed
370
  GstMemoryDefault *mem;
Wim Taymans's avatar
Wim Taymans committed
371

Wim Taymans's avatar
Wim Taymans committed
372 373 374
  g_return_val_if_fail (data != NULL, NULL);
  g_return_val_if_fail (offset + size <= maxsize, NULL);

Wim Taymans's avatar
Wim Taymans committed
375 376 377 378 379 380 381 382 383
  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
384 385
 * Increases the refcount of @mem.
 *
Wim Taymans's avatar
Wim Taymans committed
386 387
 * Returns: @mem with increased refcount
 */
388 389 390 391 392
GstMemory *
gst_memory_ref (GstMemory * mem)
{
  g_return_val_if_fail (mem != NULL, NULL);

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

395 396 397 398 399
  g_atomic_int_inc (&mem->refcount);

  return mem;
}

Wim Taymans's avatar
Wim Taymans committed
400 401 402
/**
 * gst_memory_unref:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
403 404 405
 *
 * 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
406
 */
407 408 409 410
void
gst_memory_unref (GstMemory * mem)
{
  g_return_if_fail (mem != NULL);
411
  g_return_if_fail (mem->allocator != NULL);
412

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

415
  if (g_atomic_int_dec_and_test (&mem->refcount))
416
    mem->allocator->info.mem_free (mem);
417 418
}

Wim Taymans's avatar
Wim Taymans committed
419 420 421
/**
 * gst_memory_get_sizes:
 * @mem: a #GstMemory
422
 * @offset: pointer to offset
Wim Taymans's avatar
Wim Taymans committed
423 424
 * @maxsize: pointer to maxsize
 *
425
 * Get the current @size, @offset and @maxsize of @mem.
Wim Taymans's avatar
Wim Taymans committed
426 427
 *
 * Returns: the current sizes of @mem
Wim Taymans's avatar
Wim Taymans committed
428
 */
429
gsize
430
gst_memory_get_sizes (GstMemory * mem, gsize * offset, gsize * maxsize)
431 432 433
{
  g_return_val_if_fail (mem != NULL, 0);

Wim Taymans's avatar
Wim Taymans committed
434 435 436 437 438 439
  if (offset)
    *offset = mem->offset;
  if (maxsize)
    *maxsize = mem->maxsize;

  return mem->size;
440 441
}

Wim Taymans's avatar
Wim Taymans committed
442 443 444 445 446 447 448 449 450 451
/**
 * 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
452
gst_memory_resize (GstMemory * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
453 454
{
  g_return_if_fail (mem != NULL);
455
  g_return_if_fail (gst_memory_is_writable (mem));
Wim Taymans's avatar
Wim Taymans committed
456 457
  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
458

Wim Taymans's avatar
Wim Taymans committed
459 460
  mem->offset += offset;
  mem->size = size;
Wim Taymans's avatar
Wim Taymans committed
461 462
}

463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
/**
 * 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
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 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526
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));
}

527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552

/**
 * 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
553 554
    gst_memory_unref (mem);

555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571
    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
572
    gst_memory_unref (result);
573 574 575 576
    return NULL;
  }
}

Wim Taymans's avatar
Wim Taymans committed
577 578 579
/**
 * gst_memory_map:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
580
 * @info: (out): pointer for info
Wim Taymans's avatar
Wim Taymans committed
581 582
 * @flags: mapping flags
 *
Wim Taymans's avatar
Wim Taymans committed
583 584
 * 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
585
 *
Wim Taymans's avatar
Wim Taymans committed
586
 * This function can return %FALSE for various reasons:
Wim Taymans's avatar
Wim Taymans committed
587 588 589
 * - 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
590
 * @info and its contents remains valid for as long as @mem is alive and until
Wim Taymans's avatar
Wim Taymans committed
591 592 593 594 595
 * 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
596
 * Returns: %TRUE if the map operation was successful.
Wim Taymans's avatar
Wim Taymans committed
597
 */
Wim Taymans's avatar
Wim Taymans committed
598 599
gboolean
gst_memory_map (GstMemory * mem, GstMapInfo * info, GstMapFlags flags)
600
{
Wim Taymans's avatar
Wim Taymans committed
601 602
  g_return_val_if_fail (mem != NULL, FALSE);
  g_return_val_if_fail (info != NULL, FALSE);
603

Wim Taymans's avatar
Wim Taymans committed
604 605 606
  if (!gst_memory_lock (mem, flags))
    goto lock_failed;

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

Wim Taymans's avatar
Wim Taymans committed
609
  if (G_UNLIKELY (info->data == NULL))
Wim Taymans's avatar
Wim Taymans committed
610 611
    goto error;

Wim Taymans's avatar
Wim Taymans committed
612
  info->memory = mem;
Wim Taymans's avatar
Wim Taymans committed
613
  info->flags = flags;
Wim Taymans's avatar
Wim Taymans committed
614 615
  info->size = mem->size;
  info->maxsize = mem->maxsize - mem->offset;
616
  info->data = info->data + mem->offset;
617

Wim Taymans's avatar
Wim Taymans committed
618
  return TRUE;
619

Wim Taymans's avatar
Wim Taymans committed
620 621 622
  /* ERRORS */
lock_failed:
  {
623
    GST_DEBUG ("mem %p: lock %d failed", mem, flags);
Wim Taymans's avatar
Wim Taymans committed
624
    return FALSE;
Wim Taymans's avatar
Wim Taymans committed
625 626 627
  }
error:
  {
628
    /* something went wrong, restore the orginal state again */
Wim Taymans's avatar
Wim Taymans committed
629 630
    GST_ERROR ("mem %p: map failed", mem);
    gst_memory_unlock (mem);
Wim Taymans's avatar
Wim Taymans committed
631
    return FALSE;
632
  }
633 634
}

Wim Taymans's avatar
Wim Taymans committed
635 636 637
/**
 * gst_memory_unmap:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
638
 * @info: a #GstMapInfo
Wim Taymans's avatar
Wim Taymans committed
639
 *
Wim Taymans's avatar
Wim Taymans committed
640
 * Release the memory obtained with gst_memory_map()
Wim Taymans's avatar
Wim Taymans committed
641
 */
Wim Taymans's avatar
Wim Taymans committed
642
void
Wim Taymans's avatar
Wim Taymans committed
643
gst_memory_unmap (GstMemory * mem, GstMapInfo * info)
644
{
Wim Taymans's avatar
Wim Taymans committed
645
  g_return_if_fail (mem != NULL);
Wim Taymans's avatar
Wim Taymans committed
646 647
  g_return_if_fail (info != NULL);
  g_return_if_fail (info->memory == mem);
Wim Taymans's avatar
Wim Taymans committed
648 649
  /* there must be a ref */
  g_return_if_fail (g_atomic_int_get (&mem->state) >= 4);
650

651
  mem->allocator->info.mem_unmap (mem);
Wim Taymans's avatar
Wim Taymans committed
652
  gst_memory_unlock (mem);
653 654
}

Wim Taymans's avatar
Wim Taymans committed
655 656 657 658
/**
 * gst_memory_copy:
 * @mem: a #GstMemory
 * @offset: an offset to copy
659
 * @size: size to copy or -1 to copy all bytes from offset
Wim Taymans's avatar
Wim Taymans committed
660 661 662 663 664 665 666
 *
 * 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.
 */
667
GstMemory *
668
gst_memory_copy (GstMemory * mem, gssize offset, gssize size)
669
{
Wim Taymans's avatar
Wim Taymans committed
670 671
  GstMemory *copy;

672
  g_return_val_if_fail (mem != NULL, NULL);
Wim Taymans's avatar
Wim Taymans committed
673 674
  g_return_val_if_fail (gst_memory_lock (mem, GST_MAP_READ), NULL);

675
  copy = mem->allocator->info.mem_copy (mem, offset, size);
676

Wim Taymans's avatar
Wim Taymans committed
677 678 679
  gst_memory_unlock (mem);

  return copy;
680 681
}

Wim Taymans's avatar
Wim Taymans committed
682 683 684 685
/**
 * gst_memory_share:
 * @mem: a #GstMemory
 * @offset: an offset to share
686
 * @size: size to share or -1 to share bytes from offset
Wim Taymans's avatar
Wim Taymans committed
687
 *
688 689 690 691
 * 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
692 693 694
 *
 * Returns: a new #GstMemory.
 */
695
GstMemory *
696
gst_memory_share (GstMemory * mem, gssize offset, gssize size)
697 698 699
{
  g_return_val_if_fail (mem != NULL, NULL);

700
  return mem->allocator->info.mem_share (mem, offset, size);
701 702
}

Wim Taymans's avatar
Wim Taymans committed
703 704 705 706 707 708 709 710 711 712 713 714 715 716 717
/**
 * 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
718
gboolean
719
gst_memory_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
Wim Taymans's avatar
Wim Taymans committed
720 721 722 723
{
  g_return_val_if_fail (mem1 != NULL, FALSE);
  g_return_val_if_fail (mem2 != NULL, FALSE);

724 725
  /* need to have the same allocators */
  if (mem1->allocator != mem2->allocator)
Wim Taymans's avatar
Wim Taymans committed
726 727
    return FALSE;

728 729 730
  /* need to have the same parent */
  if (mem1->parent == NULL || mem1->parent != mem2->parent)
    return FALSE;
Wim Taymans's avatar
Wim Taymans committed
731

732
  /* and memory is contiguous */
733
  if (!mem1->allocator->info.mem_is_span (mem1, mem2, offset))
734 735 736
    return FALSE;

  return TRUE;
Wim Taymans's avatar
Wim Taymans committed
737
}
738 739

/**
740
 * gst_allocator_register:
741 742 743
 * @info: a #GstMemoryInfo
 * @user_data: user data
 * @notify: a #GDestroyNotify for @user_data
744
 *
745
 * Create a new memory allocator with @info and @user_data.
746
 *
747
 * All functions in @info are mandatory exept the copy and is_span
748 749
 * functions, which will have a default implementation when left NULL.
 *
750 751
 * The @user_data will be passed to all calls of the alloc function and the
 * @notify function.
752
 *
753
 * Returns: a new #GstAllocator.
754
 */
755 756 757
GstAllocator *
gst_allocator_new (const GstMemoryInfo * info, gpointer user_data,
    GDestroyNotify notify)
758
{
759
  GstAllocator *allocator;
760 761 762 763 764

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

  g_return_val_if_fail (info != NULL, NULL);
765
  g_return_val_if_fail (info->alloc != NULL, NULL);
766 767 768 769
  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);
770

771
  allocator = g_slice_new (GstAllocator);
772
  allocator->refcount = 1;
773
  allocator->info = *info;
774 775 776 777
  allocator->user_data = user_data;
  allocator->notify = notify;
  INSTALL_FALLBACK (mem_copy);
  INSTALL_FALLBACK (mem_is_span);
778 779
#undef INSTALL_FALLBACK

780 781 782 783 784
  GST_DEBUG ("new allocator %p", allocator);

  return allocator;
}

Wim Taymans's avatar
Wim Taymans committed
785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800
/**
 * gst_alocator_get_memory_type:
 * @allocator: a #GstAllocator
 *
 * Get the memory type allocated by this allocator
 *
 * Returns: @allocator with increased refcount
 */
const gchar *
gst_allocator_get_memory_type (GstAllocator * allocator)
{
  g_return_val_if_fail (allocator != NULL, NULL);

  return allocator->info.mem_type;
}

801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858
/**
 * 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);

  GST_DEBUG ("alocator %p, %d->%d", allocator, allocator->refcount,
      allocator->refcount + 1);

  g_atomic_int_inc (&allocator->refcount);

  return allocator;
}

/**
 * gst_allocator_unref:
 * @allocator: a #GstAllocator
 *
 * Decreases the refcount of @allocator. When the refcount reaches 0, the free
 * function of @allocator will be called.
 */
void
gst_allocator_unref (GstAllocator * allocator)
{
  g_return_if_fail (allocator != NULL);

  GST_DEBUG ("allocator %p, %d->%d", allocator, allocator->refcount,
      allocator->refcount - 1);

  if (g_atomic_int_dec_and_test (&allocator->refcount)) {
    if (allocator->notify)
      allocator->notify (allocator->user_data);
    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);

  GST_DEBUG ("registering allocator %p with name \"%s\"", allocator, name);
859

Wim Taymans's avatar
Wim Taymans committed
860
  g_rw_lock_writer_lock (&lock);
861
  g_hash_table_insert (allocators, (gpointer) name, (gpointer) allocator);
Wim Taymans's avatar
Wim Taymans committed
862
  g_rw_lock_writer_unlock (&lock);
863 864 865
}

/**
866
 * gst_allocator_find:
867 868
 * @name: the name of the allocator
 *
869 870
 * Find a previously registered allocator with @name. When @name is NULL, the
 * default allocator will be returned.
871
 *
872 873
 * 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.
874
 */
875
GstAllocator *
876
gst_allocator_find (const gchar * name)
877
{
878
  GstAllocator *allocator;
879

Wim Taymans's avatar
Wim Taymans committed
880
  g_rw_lock_reader_lock (&lock);
881
  if (name) {
882
    allocator = g_hash_table_lookup (allocators, (gconstpointer) name);
883 884 885
  } else {
    allocator = _default_allocator;
  }
886 887
  if (allocator)
    gst_allocator_ref (allocator);
Wim Taymans's avatar
Wim Taymans committed
888
  g_rw_lock_reader_unlock (&lock);
889 890 891 892 893

  return allocator;
}

/**
894
 * gst_allocator_set_default:
895
 * @allocator: (transfer full): a #GstAllocator
896
 *
897
 * Set the default allocator. This function takes ownership of @allocator.
898 899
 */
void
900
gst_allocator_set_default (GstAllocator * allocator)
901
{
902
  GstAllocator *old;
903 904
  g_return_if_fail (allocator != NULL);

Wim Taymans's avatar
Wim Taymans committed
905
  g_rw_lock_writer_lock (&lock);
906
  old = _default_allocator;
907
  _default_allocator = allocator;
Wim Taymans's avatar
Wim Taymans committed
908
  g_rw_lock_writer_unlock (&lock);
909 910 911

  if (old)
    gst_allocator_unref (old);
912 913 914
}

/**
915
 * gst_allocator_alloc:
916
 * @allocator: (transfer none) (allow-none): a #GstAllocator to use
917 918 919 920 921 922
 * @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.
 *
923 924
 * When @allocator is NULL, the default allocator will be used.
 *
925 926 927
 * @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.
 *
928
 * Returns: (transfer full): a new #GstMemory.
929 930
 */
GstMemory *
931
gst_allocator_alloc (GstAllocator * allocator, gsize maxsize, gsize align)
932 933 934 935 936 937
{
  g_return_val_if_fail (((align + 1) & align) == 0, NULL);

  if (allocator == NULL)
    allocator = _default_allocator;

938
  return allocator->info.alloc (allocator, maxsize, align,
939
      allocator->user_data);
940
}