gstmemory.c 21 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 133 134
  mem->slice_size = slice_size;
  mem->data = data;
  mem->free_func = free_func;
}

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

Wim Taymans's avatar
Wim Taymans committed
143 144 145
  slice_size = sizeof (GstMemoryDefault);

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

  return mem;
}
151

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

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

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

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

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

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

  return mem;
}

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

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

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

static void
Wim Taymans's avatar
Wim Taymans committed
201
_default_mem_free (GstMemoryDefault * mem)
202
{
Wim Taymans's avatar
Wim Taymans committed
203 204 205 206 207
  if (mem->mem.parent)
    gst_memory_unref (mem->mem.parent);

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

Wim Taymans's avatar
Wim Taymans committed
209
  g_slice_free1 (mem->slice_size, mem);
210 211
}

Wim Taymans's avatar
Wim Taymans committed
212
static GstMemoryDefault *
213
_default_mem_copy (GstMemoryDefault * mem, gssize offset, gsize size)
214 215 216
{
  GstMemoryDefault *copy;

Wim Taymans's avatar
Wim Taymans committed
217
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
218
    size = mem->mem.size > offset ? mem->mem.size - offset : 0;
Wim Taymans's avatar
Wim Taymans committed
219

Wim Taymans's avatar
Wim Taymans committed
220 221 222 223
  copy =
      _default_mem_new_block (mem->mem.maxsize, 0, mem->mem.offset + offset,
      size);
  memcpy (copy->data, mem->data, mem->mem.maxsize);
224

Wim Taymans's avatar
Wim Taymans committed
225
  return copy;
226 227
}

Wim Taymans's avatar
Wim Taymans committed
228
static GstMemoryDefault *
229
_default_mem_share (GstMemoryDefault * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
230 231 232 233 234 235 236 237
{
  GstMemoryDefault *sub;
  GstMemory *parent;

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

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

Wim Taymans's avatar
Wim Taymans committed
241 242 243
  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
244 245

  return sub;
246 247
}

Wim Taymans's avatar
Wim Taymans committed
248
static gboolean
Wim Taymans's avatar
Wim Taymans committed
249 250
_default_mem_is_span (GstMemoryDefault * mem1, GstMemoryDefault * mem2,
    gsize * offset)
251
{
Wim Taymans's avatar
Wim Taymans committed
252 253 254 255 256 257

  if (offset) {
    GstMemoryDefault *parent;

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

Wim Taymans's avatar
Wim Taymans committed
258
    *offset = mem1->mem.offset - parent->mem.offset;
Wim Taymans's avatar
Wim Taymans committed
259
  }
260

Wim Taymans's avatar
Wim Taymans committed
261
  /* and memory is contiguous */
Wim Taymans's avatar
Wim Taymans committed
262 263
  return mem1->data + mem1->mem.offset + mem1->mem.size ==
      mem2->data + mem2->mem.offset;
264 265 266
}

static GstMemory *
267
_fallback_copy (GstMemory * mem, gssize offset, gssize size)
268
{
269
  GstMemory *copy;
Wim Taymans's avatar
Wim Taymans committed
270
  GstMapInfo sinfo, dinfo;
271

Wim Taymans's avatar
Wim Taymans committed
272
  if (!gst_memory_map (mem, &sinfo, GST_MAP_READ))
Wim Taymans's avatar
Wim Taymans committed
273
    return NULL;
Wim Taymans's avatar
Wim Taymans committed
274

Wim Taymans's avatar
Wim Taymans committed
275
  if (size == -1)
Wim Taymans's avatar
Wim Taymans committed
276 277
    size = sinfo.size > offset ? sinfo.size - offset : 0;

Wim Taymans's avatar
Wim Taymans committed
278 279
  /* 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
280 281 282 283 284 285 286 287 288
  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;
  }

  memcpy (dinfo.data, (guint8 *) sinfo.data + offset, size);
  gst_memory_unmap (copy, &dinfo);
  gst_memory_unmap (mem, &sinfo);
289

Wim Taymans's avatar
Wim Taymans committed
290
  return copy;
291 292 293
}

static gboolean
Wim Taymans's avatar
Wim Taymans committed
294
_fallback_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
295 296 297 298
{
  return FALSE;
}

Wim Taymans's avatar
Wim Taymans committed
299
static GRWLock lock;
300
static GHashTable *allocators;
301

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

Wim Taymans's avatar
Wim Taymans committed
316
  g_rw_lock_init (&lock);
317
  allocators = g_hash_table_new (g_str_hash, g_str_equal);
318

319 320
#ifdef HAVE_GETPAGESIZE
#ifdef MEMORY_ALIGNMENT_PAGESIZE
Wim Taymans's avatar
Wim Taymans committed
321
  gst_memory_alignment = getpagesize () - 1;
322 323 324
#endif
#endif

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

327
  _default_mem_impl = gst_allocator_register (GST_ALLOCATOR_SYSMEM, &_mem_info);
328

329
  _default_allocator = _default_mem_impl;
330 331
}

Wim Taymans's avatar
Wim Taymans committed
332 333 334 335 336 337 338 339 340 341 342 343 344
/**
 * 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
345 346 347
GstMemory *
gst_memory_new_wrapped (GstMemoryFlags flags, gpointer data,
    GFreeFunc free_func, gsize maxsize, gsize offset, gsize size)
348
{
Wim Taymans's avatar
Wim Taymans committed
349
  GstMemoryDefault *mem;
Wim Taymans's avatar
Wim Taymans committed
350

Wim Taymans's avatar
Wim Taymans committed
351 352 353
  g_return_val_if_fail (data != NULL, NULL);
  g_return_val_if_fail (offset + size <= maxsize, NULL);

Wim Taymans's avatar
Wim Taymans committed
354 355 356 357 358 359 360 361 362
  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
363 364
 * Increases the refcount of @mem.
 *
Wim Taymans's avatar
Wim Taymans committed
365 366
 * Returns: @mem with increased refcount
 */
367 368 369 370 371 372 373 374 375 376
GstMemory *
gst_memory_ref (GstMemory * mem)
{
  g_return_val_if_fail (mem != NULL, NULL);

  g_atomic_int_inc (&mem->refcount);

  return mem;
}

Wim Taymans's avatar
Wim Taymans committed
377 378 379
/**
 * gst_memory_unref:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
380 381 382
 *
 * 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
383
 */
384 385 386 387
void
gst_memory_unref (GstMemory * mem)
{
  g_return_if_fail (mem != NULL);
388
  g_return_if_fail (mem->allocator != NULL);
389 390

  if (g_atomic_int_dec_and_test (&mem->refcount))
391
    mem->allocator->info.free (mem);
392 393
}

Wim Taymans's avatar
Wim Taymans committed
394 395 396
/**
 * gst_memory_get_sizes:
 * @mem: a #GstMemory
397
 * @offset: pointer to offset
Wim Taymans's avatar
Wim Taymans committed
398 399
 * @maxsize: pointer to maxsize
 *
400
 * Get the current @size, @offset and @maxsize of @mem.
Wim Taymans's avatar
Wim Taymans committed
401 402
 *
 * Returns: the current sizes of @mem
Wim Taymans's avatar
Wim Taymans committed
403
 */
404
gsize
405
gst_memory_get_sizes (GstMemory * mem, gsize * offset, gsize * maxsize)
406 407 408
{
  g_return_val_if_fail (mem != NULL, 0);

Wim Taymans's avatar
Wim Taymans committed
409 410 411 412 413 414
  if (offset)
    *offset = mem->offset;
  if (maxsize)
    *maxsize = mem->maxsize;

  return mem->size;
415 416
}

Wim Taymans's avatar
Wim Taymans committed
417 418 419 420 421 422 423 424 425 426
/**
 * 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
427
gst_memory_resize (GstMemory * mem, gssize offset, gsize size)
Wim Taymans's avatar
Wim Taymans committed
428 429
{
  g_return_if_fail (mem != NULL);
430
  g_return_if_fail (gst_memory_is_writable (mem));
Wim Taymans's avatar
Wim Taymans committed
431 432
  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
433

Wim Taymans's avatar
Wim Taymans committed
434 435
  mem->offset += offset;
  mem->size = size;
Wim Taymans's avatar
Wim Taymans committed
436 437
}

438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
/**
 * 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
456 457 458 459 460 461 462 463 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
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));
}

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 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548

/**
 * 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);
    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);
    return NULL;
  }
}

Wim Taymans's avatar
Wim Taymans committed
549 550 551
/**
 * gst_memory_map:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
552
 * @info: (out): pointer for info
Wim Taymans's avatar
Wim Taymans committed
553 554
 * @flags: mapping flags
 *
Wim Taymans's avatar
Wim Taymans committed
555 556
 * 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
557
 *
Wim Taymans's avatar
Wim Taymans committed
558
 * This function can return %FALSE for various reasons:
Wim Taymans's avatar
Wim Taymans committed
559 560 561
 * - 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
562
 * @info and its contents remains valid for as long as @mem is alive and until
Wim Taymans's avatar
Wim Taymans committed
563 564 565 566 567
 * 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
568
 * Returns: %TRUE if the map operation was successful.
Wim Taymans's avatar
Wim Taymans committed
569
 */
Wim Taymans's avatar
Wim Taymans committed
570 571
gboolean
gst_memory_map (GstMemory * mem, GstMapInfo * info, GstMapFlags flags)
572
{
Wim Taymans's avatar
Wim Taymans committed
573 574
  g_return_val_if_fail (mem != NULL, FALSE);
  g_return_val_if_fail (info != NULL, FALSE);
575

Wim Taymans's avatar
Wim Taymans committed
576 577 578
  if (!gst_memory_lock (mem, flags))
    goto lock_failed;

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

Wim Taymans's avatar
Wim Taymans committed
581
  if (G_UNLIKELY (info->data == NULL))
Wim Taymans's avatar
Wim Taymans committed
582 583
    goto error;

Wim Taymans's avatar
Wim Taymans committed
584 585 586 587
  info->memory = mem;
  info->size = mem->size;
  info->maxsize = mem->maxsize - mem->offset;
  info->data = (guint8 *) info->data + mem->offset;
588

Wim Taymans's avatar
Wim Taymans committed
589
  return TRUE;
590

Wim Taymans's avatar
Wim Taymans committed
591 592 593
  /* ERRORS */
lock_failed:
  {
Wim Taymans's avatar
Wim Taymans committed
594 595
    GST_DEBUG ("mem %p: lock %d failed", flags);
    return FALSE;
Wim Taymans's avatar
Wim Taymans committed
596 597 598
  }
error:
  {
599
    /* something went wrong, restore the orginal state again */
Wim Taymans's avatar
Wim Taymans committed
600 601
    GST_ERROR ("mem %p: map failed", mem);
    gst_memory_unlock (mem);
Wim Taymans's avatar
Wim Taymans committed
602
    return FALSE;
603
  }
604 605
}

Wim Taymans's avatar
Wim Taymans committed
606 607 608
/**
 * gst_memory_unmap:
 * @mem: a #GstMemory
Wim Taymans's avatar
Wim Taymans committed
609
 * @info: a #GstMapInfo
Wim Taymans's avatar
Wim Taymans committed
610
 *
Wim Taymans's avatar
Wim Taymans committed
611
 * Release the memory obtained with gst_memory_map()
Wim Taymans's avatar
Wim Taymans committed
612
 */
Wim Taymans's avatar
Wim Taymans committed
613
void
Wim Taymans's avatar
Wim Taymans committed
614
gst_memory_unmap (GstMemory * mem, GstMapInfo * info)
615
{
Wim Taymans's avatar
Wim Taymans committed
616
  g_return_if_fail (mem != NULL);
Wim Taymans's avatar
Wim Taymans committed
617 618
  g_return_if_fail (info != NULL);
  g_return_if_fail (info->memory == mem);
Wim Taymans's avatar
Wim Taymans committed
619 620
  /* there must be a ref */
  g_return_if_fail (g_atomic_int_get (&mem->state) >= 4);
621

Wim Taymans's avatar
Wim Taymans committed
622 623
  mem->allocator->info.unmap (mem);
  gst_memory_unlock (mem);
624 625
}

Wim Taymans's avatar
Wim Taymans committed
626 627 628 629
/**
 * gst_memory_copy:
 * @mem: a #GstMemory
 * @offset: an offset to copy
630
 * @size: size to copy or -1 to copy all bytes from offset
Wim Taymans's avatar
Wim Taymans committed
631 632 633 634 635 636 637
 *
 * 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.
 */
638
GstMemory *
639
gst_memory_copy (GstMemory * mem, gssize offset, gssize size)
640
{
Wim Taymans's avatar
Wim Taymans committed
641 642
  GstMemory *copy;

643
  g_return_val_if_fail (mem != NULL, NULL);
Wim Taymans's avatar
Wim Taymans committed
644 645 646
  g_return_val_if_fail (gst_memory_lock (mem, GST_MAP_READ), NULL);

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

Wim Taymans's avatar
Wim Taymans committed
648 649 650
  gst_memory_unlock (mem);

  return copy;
651 652
}

Wim Taymans's avatar
Wim Taymans committed
653 654 655 656
/**
 * gst_memory_share:
 * @mem: a #GstMemory
 * @offset: an offset to share
657
 * @size: size to share or -1 to share bytes from offset
Wim Taymans's avatar
Wim Taymans committed
658
 *
659 660 661 662
 * 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
663 664 665
 *
 * Returns: a new #GstMemory.
 */
666
GstMemory *
667
gst_memory_share (GstMemory * mem, gssize offset, gssize size)
668 669 670
{
  g_return_val_if_fail (mem != NULL, NULL);

671
  return mem->allocator->info.share (mem, offset, size);
672 673
}

Wim Taymans's avatar
Wim Taymans committed
674 675 676 677 678 679 680 681 682 683 684 685 686 687 688
/**
 * 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
689
gboolean
690
gst_memory_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
Wim Taymans's avatar
Wim Taymans committed
691 692 693 694
{
  g_return_val_if_fail (mem1 != NULL, FALSE);
  g_return_val_if_fail (mem2 != NULL, FALSE);

695 696
  /* need to have the same allocators */
  if (mem1->allocator != mem2->allocator)
Wim Taymans's avatar
Wim Taymans committed
697 698
    return FALSE;

699 700 701
  /* need to have the same parent */
  if (mem1->parent == NULL || mem1->parent != mem2->parent)
    return FALSE;
Wim Taymans's avatar
Wim Taymans committed
702

703
  /* and memory is contiguous */
704
  if (!mem1->allocator->info.is_span (mem1, mem2, offset))
705 706 707
    return FALSE;

  return TRUE;
Wim Taymans's avatar
Wim Taymans committed
708
}
709 710

/**
711
 * gst_allocator_register:
712 713 714 715 716 717
 * @name: the name of the allocator
 * @info: #GstMemoryInfo
 *
 * Registers the memory allocator with @name and implementation functions
 * @info.
 *
718
 * All functions in @info are mandatory exept the copy and is_span
719 720 721 722 723
 * 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.
 *
724
 * Returns: a new #GstAllocator.
725
 */
726 727
const GstAllocator *
gst_allocator_register (const gchar * name, const GstMemoryInfo * info)
728
{
729
  GstAllocator *allocator;
730 731 732 733 734 735

#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);
736
  g_return_val_if_fail (info->alloc != NULL, NULL);
737 738 739
  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);
740
  g_return_val_if_fail (info->share != NULL, NULL);
741

742
  allocator = g_slice_new (GstAllocator);
743 744 745 746 747 748
  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
749
  GST_DEBUG ("registering allocator \"%s\"", name);
750

Wim Taymans's avatar
Wim Taymans committed
751
  g_rw_lock_writer_lock (&lock);
752
  g_hash_table_insert (allocators, (gpointer) name, (gpointer) allocator);
Wim Taymans's avatar
Wim Taymans committed
753
  g_rw_lock_writer_unlock (&lock);
754 755 756 757 758

  return allocator;
}

/**
759
 * gst_allocator_find:
760 761
 * @name: the name of the allocator
 *
762 763
 * Find a previously registered allocator with @name. When @name is NULL, the
 * default allocator will be returned.
764
 *
765
 * Returns: a #GstAllocator or NULL when the allocator with @name was not
766 767
 * registered.
 */
768 769
const GstAllocator *
gst_allocator_find (const gchar * name)
770
{
771
  const GstAllocator *allocator;
772

Wim Taymans's avatar
Wim Taymans committed
773
  g_rw_lock_reader_lock (&lock);
774
  if (name) {
775
    allocator = g_hash_table_lookup (allocators, (gconstpointer) name);
776 777 778
  } else {
    allocator = _default_allocator;
  }
Wim Taymans's avatar
Wim Taymans committed
779
  g_rw_lock_reader_unlock (&lock);
780 781 782 783 784

  return allocator;
}

/**
785 786
 * gst_allocator_set_default:
 * @allocator: a #GstAllocator
787 788 789 790
 *
 * Set the default allocator.
 */
void
791
gst_allocator_set_default (const GstAllocator * allocator)
792 793 794
{
  g_return_if_fail (allocator != NULL);

Wim Taymans's avatar
Wim Taymans committed
795
  g_rw_lock_writer_lock (&lock);
796
  _default_allocator = allocator;
Wim Taymans's avatar
Wim Taymans committed
797
  g_rw_lock_writer_unlock (&lock);
798 799 800
}

/**
801
 * gst_allocator_alloc:
802
 * @allocator: (transfer none) (allow-none): a #GstAllocator to use
803 804 805 806 807 808
 * @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.
 *
809 810
 * When @allocator is NULL, the default allocator will be used.
 *
811 812 813
 * @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.
 *
814
 * Returns: (transfer full): a new #GstMemory.
815 816
 */
GstMemory *
817
gst_allocator_alloc (const GstAllocator * allocator, gsize maxsize, gsize align)
818 819 820 821 822 823
{
  g_return_val_if_fail (((align + 1) & align) == 0, NULL);

  if (allocator == NULL)
    allocator = _default_allocator;

824 825
  return allocator->info.alloc (allocator, maxsize, align,
      allocator->info.user_data);
826
}