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 GST_CAT_DEBUG (GST_CAT_PERFORMANCE, 468 "memcpy %" G_GSIZE_FORMAT " memory %p -> %p", size, mem, copy); 469 memcpy (copy->data, mem->data + mem->mem.offset + offset, size); 470 471 return copy; 472 } 473 474 static GstMemorySystem * 475 _sysmem_share (GstMemorySystem * mem, gssize offset, gsize size) 476 { 477 GstMemorySystem *sub; 478 GstMemory *parent; 479 480 /* find the real parent */ 481 if ((parent = mem->mem.parent) == NULL) 482 parent = (GstMemory *) mem; 483 484 if (size == -1) 485 size = mem->mem.size - offset; 486 487 /* the shared memory is always readonly */ 488 sub = 489 _sysmem_new (GST_MINI_OBJECT_FLAGS (parent) | 490 GST_MINI_OBJECT_FLAG_LOCK_READONLY, parent, mem->data, mem->mem.maxsize, 491 mem->mem.align, mem->mem.offset + offset, size, NULL, NULL); 492 493 return sub; 494 } 495 496 static gboolean 497 _sysmem_is_span (GstMemorySystem * mem1, GstMemorySystem * mem2, gsize * offset) 498 { 499 500 if (offset) { 501 GstMemorySystem *parent; 502 503 parent = (GstMemorySystem *) mem1->mem.parent; 504 505 *offset = mem1->mem.offset - parent->mem.offset; 506 } 507 508 /* and memory is contiguous */ 509 return mem1->data + mem1->mem.offset + mem1->mem.size == 510 mem2->data + mem2->mem.offset; 511 } 512 513 static GstMemory * 514 default_alloc (GstAllocator * allocator, gsize size, 515 GstAllocationParams * params) 516 { 517 gsize maxsize = size + params->prefix + params->padding; 518 519 return (GstMemory *) _sysmem_new_block (params->flags, 520 maxsize, params->align, params->prefix, size); 521 } 522 523 static void 524 default_free (GstAllocator * allocator, GstMemory * mem) 525 { 526 GstMemorySystem *dmem = (GstMemorySystem *) mem; 527 gsize slice_size; 528 529 if (dmem->notify) 530 dmem->notify (dmem->user_data); 531 532 slice_size = dmem->slice_size; 533 534 #ifdef USE_POISONING 535 /* just poison the structs, not all the data */ 536 memset (mem, 0xff, sizeof (GstMemorySystem)); 537 #endif 538 539 g_slice_free1 (slice_size, mem); 540 } 541 542 static void 543 gst_allocator_sysmem_finalize (GObject * obj) 544 { 545 g_warning ("The default memory allocator was freed!"); 546 } 547 548 static void 549 gst_allocator_sysmem_class_init (GstAllocatorSysmemClass * klass) 550 { 551 GObjectClass *gobject_class; 552 GstAllocatorClass *allocator_class; 553 554 gobject_class = (GObjectClass *) klass; 555 allocator_class = (GstAllocatorClass *) klass; 556 557 gobject_class->finalize = gst_allocator_sysmem_finalize; 558 559 allocator_class->alloc = default_alloc; 560 allocator_class->free = default_free; 561 } 562 563 static void 564 gst_allocator_sysmem_init (GstAllocatorSysmem * allocator) 565 { 566 GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator); 567 568 GST_CAT_DEBUG (GST_CAT_MEMORY, "init allocator %p", allocator); 569 570 alloc->mem_type = GST_ALLOCATOR_SYSMEM; 571 alloc->mem_map = (GstMemoryMapFunction) _sysmem_map; 572 alloc->mem_unmap = (GstMemoryUnmapFunction) _sysmem_unmap; 573 alloc->mem_copy = (GstMemoryCopyFunction) _sysmem_copy; 574 alloc->mem_share = (GstMemoryShareFunction) _sysmem_share; 575 alloc->mem_is_span = (GstMemoryIsSpanFunction) _sysmem_is_span; 576 } 577 578 void 579 _priv_gst_allocator_initialize (void) 580 { 581 g_rw_lock_init (&lock); 582 allocators = g_hash_table_new (g_str_hash, g_str_equal); 583 584 #ifdef HAVE_GETPAGESIZE 585 #ifdef MEMORY_ALIGNMENT_PAGESIZE 586 gst_memory_alignment = getpagesize () - 1; 587 #endif 588 #endif 589 590 GST_CAT_DEBUG (GST_CAT_MEMORY, "memory alignment: %" G_GSIZE_FORMAT, 591 gst_memory_alignment); 592 593 _sysmem_allocator = g_object_new (gst_allocator_sysmem_get_type (), NULL); 594 595 gst_allocator_register (GST_ALLOCATOR_SYSMEM, 596 gst_object_ref (_sysmem_allocator)); 597 598 _default_allocator = gst_object_ref (_sysmem_allocator); 599 } 600 601 /** 602 * gst_memory_new_wrapped: 603 * @flags: #GstMemoryFlags 604 * @data: (array length=size) (element-type guint8) (transfer none): data to 605 * wrap 606 * @maxsize: allocated size of @data 607 * @offset: offset in @data 608 * @size: size of valid data 609 * @user_data: (allow-none): user_data 610 * @notify: (allow-none) (scope async) (closure user_data): called with @user_data when the memory is freed 611 * 612 * Allocate a new memory block that wraps the given @data. 613 * 614 * The prefix/padding must be filled with 0 if @flags contains 615 * #GST_MEMORY_FLAG_ZERO_PREFIXED and #GST_MEMORY_FLAG_ZERO_PADDED respectively. 616 * 617 * Returns: (transfer full): a new #GstMemory. 618 */ 619 GstMemory * 620 gst_memory_new_wrapped (GstMemoryFlags flags, gpointer data, 621 gsize maxsize, gsize offset, gsize size, gpointer user_data, 622 GDestroyNotify notify) 623 { 624 GstMemorySystem *mem; 625 626 g_return_val_if_fail (data != NULL, NULL); 627 g_return_val_if_fail (offset + size <= maxsize, NULL); 628 629 mem = 630 _sysmem_new (flags, NULL, data, maxsize, 0, offset, size, user_data, 631 notify); 632 633 return (GstMemory *) mem; 634 }