1 /* GStreamer 2 * Copyright (C) 2011 Wim Taymans <wim.taymans@gmail.be> 3 * 4 * gstallocator.c: memory block allocator 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Library General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public 17 * License along with this library; if not, write to the 18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 */ 21 22 /** 23 * SECTION:gstallocator 24 * @short_description: allocate memory blocks 25 * @see_also: #GstMemory 26 * 27 * Memory is usually created by allocators with a gst_allocator_alloc() 28 * method call. When %NULL is used as the allocator, the default allocator will 29 * be used. 30 * 31 * New allocators can be registered with gst_allocator_register(). 32 * Allocators are identified by name and can be retrieved with 33 * gst_allocator_find(). gst_allocator_set_default() can be used to change the 34 * default allocator. 35 * 36 * New memory can be created with gst_memory_new_wrapped() that wraps the memory 37 * allocated elsewhere. 38 */ 39 40 #ifdef HAVE_CONFIG_H 41 #include "config.h" 42 #endif 43 44 #include "gst_private.h" 45 #include "gstmemory.h" 46 47 GST_DEBUG_CATEGORY_STATIC (gst_allocator_debug); 48 #define GST_CAT_DEFAULT gst_allocator_debug 49 50 #define GST_ALLOCATOR_GET_PRIVATE(obj) \ 51 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_ALLOCATOR, GstAllocatorPrivate)) 52 53 struct _GstAllocatorPrivate 54 { 55 gpointer dummy; 56 }; 57 58 #ifdef GSTREAMER_LITE 59 // In GStLite we require to have memory aligned to 16 bytes for RAW video. 60 // For now lets have all memory 16 bytes aligned. 61 gsize gst_memory_alignment = 15; 62 #else // GSTREAMER_LITE 63 #if defined(MEMORY_ALIGNMENT_MALLOC) 64 gsize gst_memory_alignment = 7; 65 #elif defined(MEMORY_ALIGNMENT_PAGESIZE) 66 /* we fill this in in the _init method */ 67 gsize gst_memory_alignment = 0; 68 #elif defined(MEMORY_ALIGNMENT) 69 gsize gst_memory_alignment = MEMORY_ALIGNMENT - 1; 70 #else 71 #error "No memory alignment configured" 72 gsize gst_memory_alignment = 0; 73 #endif 74 #endif // GSTREAMER_LITE 75 76 /* the default allocator */ 77 static GstAllocator *_default_allocator; 78 79 static GstAllocator *_sysmem_allocator; 80 81 /* registered allocators */ 82 static GRWLock lock; 83 static GHashTable *allocators; 84 85 G_DEFINE_ABSTRACT_TYPE (GstAllocator, gst_allocator, GST_TYPE_OBJECT); 86 87 static void 88 gst_allocator_class_init (GstAllocatorClass * klass) 89 { 90 g_type_class_add_private (klass, sizeof (GstAllocatorPrivate)); 91 92 GST_DEBUG_CATEGORY_INIT (gst_allocator_debug, "allocator", 0, 93 "allocator debug"); 94 } 95 96 static GstMemory * 97 _fallback_mem_copy (GstMemory * mem, gssize offset, gssize size) 98 { 99 GstMemory *copy; 100 GstMapInfo sinfo, dinfo; 101 GstAllocationParams params = { 0, mem->align, 0, 0, }; 102 GstAllocator *allocator; 103 104 if (!gst_memory_map (mem, &sinfo, GST_MAP_READ)) 105 return NULL; 106 107 if (size == -1) 108 size = sinfo.size > offset ? sinfo.size - offset : 0; 109 110 /* use the same allocator as the memory we copy */ 111 allocator = mem->allocator; 112 if (GST_OBJECT_FLAG_IS_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC)) 113 allocator = NULL; 114 copy = gst_allocator_alloc (allocator, size, ¶ms); 115 116 if (!gst_memory_map (copy, &dinfo, GST_MAP_WRITE)) { 117 GST_CAT_WARNING (GST_CAT_MEMORY, "could not write map memory %p", copy); 118 gst_allocator_free (mem->allocator, copy); 119 gst_memory_unmap (mem, &sinfo); 120 return NULL; 121 } 122 123 GST_CAT_DEBUG (GST_CAT_PERFORMANCE, 124 "memcpy %" G_GSSIZE_FORMAT " memory %p -> %p", size, mem, copy); 125 memcpy (dinfo.data, sinfo.data + offset, size); 126 gst_memory_unmap (copy, &dinfo); 127 gst_memory_unmap (mem, &sinfo); 128 129 return copy; 130 } 131 132 static gboolean 133 _fallback_mem_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset) 134 { 135 return FALSE; 136 } 137 138 static void 139 gst_allocator_init (GstAllocator * allocator) 140 { 141 allocator->priv = GST_ALLOCATOR_GET_PRIVATE (allocator); 142 143 allocator->mem_copy = _fallback_mem_copy; 144 allocator->mem_is_span = _fallback_mem_is_span; 145 } 146 147 G_DEFINE_BOXED_TYPE (GstAllocationParams, gst_allocation_params, 148 (GBoxedCopyFunc) gst_allocation_params_copy, 149 (GBoxedFreeFunc) gst_allocation_params_free); 150 151 /** 152 * gst_allocation_params_init: 153 * @params: a #GstAllocationParams 154 * 155 * Initialize @params to its default values 156 */ 157 void 158 gst_allocation_params_init (GstAllocationParams * params) 159 { 160 g_return_if_fail (params != NULL); 161 162 memset (params, 0, sizeof (GstAllocationParams)); 163 } 164 165 /** 166 * gst_allocation_params_copy: 167 * @params: (transfer none): a #GstAllocationParams 168 * 169 * Create a copy of @params. 170 * 171 * Free-function: gst_allocation_params_free 172 * 173 * Returns: (transfer full): a new ##GstAllocationParams, free with 174 * gst_allocation_params_free(). 175 */ 176 GstAllocationParams * 177 gst_allocation_params_copy (const GstAllocationParams * params) 178 { 179 GstAllocationParams *result = NULL; 180 181 if (params) { 182 result = 183 (GstAllocationParams *) g_slice_copy (sizeof (GstAllocationParams), 184 params); 185 } 186 return result; 187 } 188 189 /** 190 * gst_allocation_params_free: 191 * @params: (in) (transfer full): a #GstAllocationParams 192 * 193 * Free @params 194 */ 195 void 196 gst_allocation_params_free (GstAllocationParams * params) 197 { 198 g_slice_free (GstAllocationParams, params); 199 } 200 201 /** 202 * gst_allocator_register: 203 * @name: the name of the allocator 204 * @allocator: (transfer full): #GstAllocator 205 * 206 * Registers the memory @allocator with @name. This function takes ownership of 207 * @allocator. 208 */ 209 void 210 gst_allocator_register (const gchar * name, GstAllocator * allocator) 211 { 212 g_return_if_fail (name != NULL); 213 g_return_if_fail (allocator != NULL); 214 215 GST_CAT_DEBUG (GST_CAT_MEMORY, "registering allocator %p with name \"%s\"", 216 allocator, name); 217 218 g_rw_lock_writer_lock (&lock); 219 g_hash_table_insert (allocators, (gpointer) name, (gpointer) allocator); 220 g_rw_lock_writer_unlock (&lock); 221 } 222 223 /** 224 * gst_allocator_find: 225 * @name: (allow-none): the name of the allocator 226 * 227 * Find a previously registered allocator with @name. When @name is %NULL, the 228 * default allocator will be returned. 229 * 230 * Returns: (transfer full) (nullable): a #GstAllocator or %NULL when 231 * the allocator with @name was not registered. Use gst_object_unref() 232 * to release the allocator after usage. 233 */ 234 GstAllocator * 235 gst_allocator_find (const gchar * name) 236 { 237 GstAllocator *allocator; 238 239 g_rw_lock_reader_lock (&lock); 240 if (name) { 241 allocator = g_hash_table_lookup (allocators, (gconstpointer) name); 242 } else { 243 allocator = _default_allocator; 244 } 245 if (allocator) 246 gst_object_ref (allocator); 247 g_rw_lock_reader_unlock (&lock); 248 249 return allocator; 250 } 251 252 /** 253 * gst_allocator_set_default: 254 * @allocator: (transfer full): a #GstAllocator 255 * 256 * Set the default allocator. This function takes ownership of @allocator. 257 */ 258 void 259 gst_allocator_set_default (GstAllocator * allocator) 260 { 261 GstAllocator *old; 262 263 g_return_if_fail (GST_IS_ALLOCATOR (allocator)); 264 265 g_rw_lock_writer_lock (&lock); 266 old = _default_allocator; 267 _default_allocator = allocator; 268 g_rw_lock_writer_unlock (&lock); 269 270 if (old) 271 gst_object_unref (old); 272 } 273 274 /** 275 * gst_allocator_alloc: 276 * @allocator: (transfer none) (allow-none): a #GstAllocator to use 277 * @size: size of the visible memory area 278 * @params: (transfer none) (allow-none): optional parameters 279 * 280 * Use @allocator to allocate a new memory block with memory that is at least 281 * @size big. 282 * 283 * The optional @params can specify the prefix and padding for the memory. If 284 * %NULL is passed, no flags, no extra prefix/padding and a default alignment is 285 * used. 286 * 287 * The prefix/padding will be filled with 0 if flags contains 288 * #GST_MEMORY_FLAG_ZERO_PREFIXED and #GST_MEMORY_FLAG_ZERO_PADDED respectively. 289 * 290 * When @allocator is %NULL, the default allocator will be used. 291 * 292 * The alignment in @params is given as a bitmask so that @align + 1 equals 293 * the amount of bytes to align to. For example, to align to 8 bytes, 294 * use an alignment of 7. 295 * 296 * Returns: (transfer full): a new #GstMemory. 297 */ 298 GstMemory * 299 gst_allocator_alloc (GstAllocator * allocator, gsize size, 300 GstAllocationParams * params) 301 { 302 GstMemory *mem; 303 static GstAllocationParams defparams = { 0, 0, 0, 0, }; 304 GstAllocatorClass *aclass; 305 306 if (params) { 307 g_return_val_if_fail (((params->align + 1) & params->align) == 0, NULL); 308 } else { 309 params = &defparams; 310 } 311 312 if (allocator == NULL) 313 allocator = _default_allocator; 314 315 aclass = GST_ALLOCATOR_GET_CLASS (allocator); 316 if (aclass->alloc) 317 mem = aclass->alloc (allocator, size, params); 318 else 319 mem = NULL; 320 321 return mem; 322 } 323 324 /** 325 * gst_allocator_free: 326 * @allocator: (transfer none): a #GstAllocator to use 327 * @memory: (transfer full): the memory to free 328 * 329 * Free @memory that was previously allocated with gst_allocator_alloc(). 330 */ 331 void 332 gst_allocator_free (GstAllocator * allocator, GstMemory * memory) 333 { 334 GstAllocatorClass *aclass; 335 336 g_return_if_fail (GST_IS_ALLOCATOR (allocator)); 337 g_return_if_fail (memory != NULL); 338 g_return_if_fail (memory->allocator == allocator); 339 340 aclass = GST_ALLOCATOR_GET_CLASS (allocator); 341 if (aclass->free) 342 aclass->free (allocator, memory); 343 } 344 345 /* default memory implementation */ 346 typedef struct 347 { 348 GstMemory mem; 349 350 gsize slice_size; 351 guint8 *data; 352 353 gpointer user_data; 354 GDestroyNotify notify; 355 } GstMemorySystem; 356 357 typedef struct 358 { 359 GstAllocator parent; 360 } GstAllocatorSysmem; 361 362 typedef struct 363 { 364 GstAllocatorClass parent_class; 365 } GstAllocatorSysmemClass; 366 367 GType gst_allocator_sysmem_get_type (void); 368 G_DEFINE_TYPE (GstAllocatorSysmem, gst_allocator_sysmem, GST_TYPE_ALLOCATOR); 369 370 /* initialize the fields */ 371 static inline void 372 _sysmem_init (GstMemorySystem * mem, GstMemoryFlags flags, 373 GstMemory * parent, gsize slice_size, 374 gpointer data, gsize maxsize, gsize align, gsize offset, gsize size, 375 gpointer user_data, GDestroyNotify notify) 376 { 377 gst_memory_init (GST_MEMORY_CAST (mem), 378 flags, _sysmem_allocator, parent, maxsize, align, offset, size); 379 380 mem->slice_size = slice_size; 381 mem->data = data; 382 mem->user_data = user_data; 383 mem->notify = notify; 384 } 385 386 /* create a new memory block that manages the given memory */ 387 static inline GstMemorySystem * 388 _sysmem_new (GstMemoryFlags flags, 389 GstMemory * parent, gpointer data, gsize maxsize, gsize align, gsize offset, 390 gsize size, gpointer user_data, GDestroyNotify notify) 391 { 392 GstMemorySystem *mem; 393 gsize slice_size; 394 395 slice_size = sizeof (GstMemorySystem); 396 397 mem = g_slice_alloc (slice_size); 398 _sysmem_init (mem, flags, parent, slice_size, 399 data, maxsize, align, offset, size, user_data, notify); 400 401 return mem; 402 } 403 404 /* allocate the memory and structure in one block */ 405 static GstMemorySystem * 406 _sysmem_new_block (GstMemoryFlags flags, 407 gsize maxsize, gsize align, gsize offset, gsize size) 408 { 409 GstMemorySystem *mem; 410 gsize aoffset, slice_size, padding; 411 guint8 *data; 412 413 /* ensure configured alignment */ 414 align |= gst_memory_alignment; 415 /* allocate more to compensate for alignment */ 416 maxsize += align; 417 /* alloc header and data in one block */ 418 slice_size = sizeof (GstMemorySystem) + maxsize; 419 420 mem = g_slice_alloc (slice_size); 421 if (mem == NULL) 422 return NULL; 423 424 data = (guint8 *) mem + sizeof (GstMemorySystem); 425 426 /* do alignment */ 427 if ((aoffset = ((guintptr) data & align))) { 428 aoffset = (align + 1) - aoffset; 429 data += aoffset; 430 maxsize -= aoffset; 431 } 432 433 if (offset && (flags & GST_MEMORY_FLAG_ZERO_PREFIXED)) 434 memset (data, 0, offset); 435 436 padding = maxsize - (offset + size); 437 if (padding && (flags & GST_MEMORY_FLAG_ZERO_PADDED)) 438 memset (data + offset + size, 0, padding); 439 440 _sysmem_init (mem, flags, NULL, slice_size, data, maxsize, 441 align, offset, size, NULL, NULL); 442 443 return mem; 444 } 445 446 static gpointer 447 _sysmem_map (GstMemorySystem * mem, gsize maxsize, GstMapFlags flags) 448 { 449 return mem->data; 450 } 451 452 static gboolean 453 _sysmem_unmap (GstMemorySystem * mem) 454 { 455 return TRUE; 456 } 457 458 static GstMemorySystem * 459 _sysmem_copy (GstMemorySystem * mem, gssize offset, gsize size) 460 { 461 GstMemorySystem *copy; 462 463 if (size == -1) 464 size = mem->mem.size > offset ? mem->mem.size - offset : 0; 465 466 copy = _sysmem_new_block (0, size, mem->mem.align, 0, size); 467 #ifdef GSTREAMER_LITE 468 if (copy == NULL) 469 return NULL; 470 #endif // GSTREAMER_LITE 471 GST_CAT_DEBUG (GST_CAT_PERFORMANCE, 472 "memcpy %" G_GSIZE_FORMAT " memory %p -> %p", size, mem, copy); 473 memcpy (copy->data, mem->data + mem->mem.offset + offset, size); 474 475 return copy; 476 } 477 478 static GstMemorySystem * 479 _sysmem_share (GstMemorySystem * mem, gssize offset, gsize size) 480 { 481 GstMemorySystem *sub; 482 GstMemory *parent; 483 484 /* find the real parent */ 485 if ((parent = mem->mem.parent) == NULL) 486 parent = (GstMemory *) mem; 487 488 if (size == -1) 489 size = mem->mem.size - offset; 490 491 /* the shared memory is always readonly */ 492 sub = 493 _sysmem_new (GST_MINI_OBJECT_FLAGS (parent) | 494 GST_MINI_OBJECT_FLAG_LOCK_READONLY, parent, mem->data, mem->mem.maxsize, 495 mem->mem.align, mem->mem.offset + offset, size, NULL, NULL); 496 497 return sub; 498 } 499 500 static gboolean 501 _sysmem_is_span (GstMemorySystem * mem1, GstMemorySystem * mem2, gsize * offset) 502 { 503 504 if (offset) { 505 GstMemorySystem *parent; 506 507 parent = (GstMemorySystem *) mem1->mem.parent; 508 509 *offset = mem1->mem.offset - parent->mem.offset; 510 } 511 512 /* and memory is contiguous */ 513 return mem1->data + mem1->mem.offset + mem1->mem.size == 514 mem2->data + mem2->mem.offset; 515 } 516 517 static GstMemory * 518 default_alloc (GstAllocator * allocator, gsize size, 519 GstAllocationParams * params) 520 { 521 gsize maxsize = size + params->prefix + params->padding; 522 523 return (GstMemory *) _sysmem_new_block (params->flags, 524 maxsize, params->align, params->prefix, size); 525 } 526 527 static void 528 default_free (GstAllocator * allocator, GstMemory * mem) 529 { 530 GstMemorySystem *dmem = (GstMemorySystem *) mem; 531 gsize slice_size; 532 533 if (dmem->notify) 534 dmem->notify (dmem->user_data); 535 536 slice_size = dmem->slice_size; 537 538 #ifdef USE_POISONING 539 /* just poison the structs, not all the data */ 540 memset (mem, 0xff, sizeof (GstMemorySystem)); 541 #endif 542 543 g_slice_free1 (slice_size, mem); 544 } 545 546 static void 547 gst_allocator_sysmem_finalize (GObject * obj) 548 { 549 g_warning ("The default memory allocator was freed!"); 550 } 551 552 static void 553 gst_allocator_sysmem_class_init (GstAllocatorSysmemClass * klass) 554 { 555 GObjectClass *gobject_class; 556 GstAllocatorClass *allocator_class; 557 558 gobject_class = (GObjectClass *) klass; 559 allocator_class = (GstAllocatorClass *) klass; 560 561 gobject_class->finalize = gst_allocator_sysmem_finalize; 562 563 allocator_class->alloc = default_alloc; 564 allocator_class->free = default_free; 565 } 566 567 static void 568 gst_allocator_sysmem_init (GstAllocatorSysmem * allocator) 569 { 570 GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator); 571 572 GST_CAT_DEBUG (GST_CAT_MEMORY, "init allocator %p", allocator); 573 574 alloc->mem_type = GST_ALLOCATOR_SYSMEM; 575 alloc->mem_map = (GstMemoryMapFunction) _sysmem_map; 576 alloc->mem_unmap = (GstMemoryUnmapFunction) _sysmem_unmap; 577 alloc->mem_copy = (GstMemoryCopyFunction) _sysmem_copy; 578 alloc->mem_share = (GstMemoryShareFunction) _sysmem_share; 579 alloc->mem_is_span = (GstMemoryIsSpanFunction) _sysmem_is_span; 580 } 581 582 void 583 _priv_gst_allocator_initialize (void) 584 { 585 g_rw_lock_init (&lock); 586 allocators = g_hash_table_new (g_str_hash, g_str_equal); 587 588 #ifdef HAVE_GETPAGESIZE 589 #ifdef MEMORY_ALIGNMENT_PAGESIZE 590 gst_memory_alignment = getpagesize () - 1; 591 #endif 592 #endif 593 594 GST_CAT_DEBUG (GST_CAT_MEMORY, "memory alignment: %" G_GSIZE_FORMAT, 595 gst_memory_alignment); 596 597 _sysmem_allocator = g_object_new (gst_allocator_sysmem_get_type (), NULL); 598 599 gst_allocator_register (GST_ALLOCATOR_SYSMEM, 600 gst_object_ref (_sysmem_allocator)); 601 602 _default_allocator = gst_object_ref (_sysmem_allocator); 603 } 604 605 /** 606 * gst_memory_new_wrapped: 607 * @flags: #GstMemoryFlags 608 * @data: (array length=size) (element-type guint8) (transfer none): data to 609 * wrap 610 * @maxsize: allocated size of @data 611 * @offset: offset in @data 612 * @size: size of valid data 613 * @user_data: (allow-none): user_data 614 * @notify: (allow-none) (scope async) (closure user_data): called with @user_data when the memory is freed 615 * 616 * Allocate a new memory block that wraps the given @data. 617 * 618 * The prefix/padding must be filled with 0 if @flags contains 619 * #GST_MEMORY_FLAG_ZERO_PREFIXED and #GST_MEMORY_FLAG_ZERO_PADDED respectively. 620 * 621 * Returns: (transfer full): a new #GstMemory. 622 */ 623 GstMemory * 624 gst_memory_new_wrapped (GstMemoryFlags flags, gpointer data, 625 gsize maxsize, gsize offset, gsize size, gpointer user_data, 626 GDestroyNotify notify) 627 { 628 GstMemorySystem *mem; 629 630 g_return_val_if_fail (data != NULL, NULL); 631 g_return_val_if_fail (offset + size <= maxsize, NULL); 632 633 mem = 634 _sysmem_new (flags, NULL, data, maxsize, 0, offset, size, user_data, 635 notify); 636 637 return (GstMemory *) mem; 638 }