1 /* GStreamer Apple Core Video memory
2 * Copyright (C) 2015 Ilya Konstantinov
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for mordetails.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
24 #include "corevideomemory.h"
26 GST_DEBUG_CATEGORY_STATIC (GST_CAT_APPLE_CORE_VIDEO_MEMORY);
27 #define GST_CAT_DEFAULT GST_CAT_APPLE_CORE_VIDEO_MEMORY
29 static const char *_lock_state_names[] = {
30 "Unlocked", "Locked Read-Only", "Locked Read-Write"
34 * gst_apple_core_video_pixel_buffer_new:
35 * @buf: an unlocked CVPixelBuffer
37 * Initializes a wrapper to manage locking state for a CVPixelBuffer.
38 * This function expects to receive unlocked CVPixelBuffer, and further assumes
39 * that no one else will lock it (as long as the wrapper exists).
41 * This function retains @buf.
43 * Returns: The wrapped @buf.
45 GstAppleCoreVideoPixelBuffer *
46 gst_apple_core_video_pixel_buffer_new (CVPixelBufferRef buf)
48 GstAppleCoreVideoPixelBuffer *gpixbuf =
49 g_slice_new (GstAppleCoreVideoPixelBuffer);
50 gpixbuf->refcount = 1;
51 g_mutex_init (&gpixbuf->mutex);
52 gpixbuf->buf = CVPixelBufferRetain (buf);
53 gpixbuf->lock_state = GST_APPLE_CORE_VIDEO_MEMORY_UNLOCKED;
54 gpixbuf->lock_count = 0;
58 GstAppleCoreVideoPixelBuffer *
59 gst_apple_core_video_pixel_buffer_ref (GstAppleCoreVideoPixelBuffer * gpixbuf)
61 g_atomic_int_inc (&gpixbuf->refcount);
66 gst_apple_core_video_pixel_buffer_unref (GstAppleCoreVideoPixelBuffer * gpixbuf)
68 if (g_atomic_int_dec_and_test (&gpixbuf->refcount)) {
69 if (gpixbuf->lock_state != GST_APPLE_CORE_VIDEO_MEMORY_UNLOCKED) {
71 ("%p: CVPixelBuffer memory still locked (lock_count = %d), likely forgot to unmap GstAppleCoreVideoMemory",
72 gpixbuf, gpixbuf->lock_count);
74 CVPixelBufferRelease (gpixbuf->buf);
75 g_mutex_clear (&gpixbuf->mutex);
76 g_slice_free (GstAppleCoreVideoPixelBuffer, gpixbuf);
81 * gst_apple_core_video_pixel_buffer_lock:
82 * @gpixbuf: the wrapped CVPixelBuffer
83 * @flags: mapping flags for either read-only or read-write locking
85 * Locks the pixel buffer into CPU memory for reading only, or
86 * reading and writing. The desired lock mode is deduced from @flags.
88 * For planar buffers, each plane's #GstAppleCoreVideoMemory will reference
89 * the same #GstAppleCoreVideoPixelBuffer; therefore this function will be
90 * called multiple times for the same @gpixbuf. Each call to this function
91 * should be matched by a call to gst_apple_core_video_pixel_buffer_unlock().
95 * - Read-only locking improves performance by preventing Core Video
96 * from invalidating existing caches of the buffer’s contents.
98 * - Only the first call actually locks; subsequent calls succeed
99 * as long as their requested flags are compatible with how the buffer
102 * For example, the following code will succeed:
103 * |[<!-- language="C" -->
104 * gst_memory_map(plane1, GST_MAP_READWRITE);
105 * gst_memory_map(plane2, GST_MAP_READ);
107 * while the ƒollowing code will fail:
108 * |[<!-- language="C" -->
109 * gst_memory_map(plane1, GST_MAP_READ);
110 * gst_memory_map(plane2, GST_MAP_READWRITE); /<!-- -->* ERROR: already locked for read-only *<!-- -->/
113 * Returns: %TRUE if the buffer was locked as requested
116 gst_apple_core_video_pixel_buffer_lock (GstAppleCoreVideoPixelBuffer * gpixbuf,
120 CVOptionFlags lockFlags;
122 g_mutex_lock (&gpixbuf->mutex);
124 switch (gpixbuf->lock_state) {
125 case GST_APPLE_CORE_VIDEO_MEMORY_UNLOCKED:
126 lockFlags = (flags & GST_MAP_WRITE) ? 0 : kCVPixelBufferLock_ReadOnly;
127 cvret = CVPixelBufferLockBaseAddress (gpixbuf->buf, lockFlags);
128 if (cvret != kCVReturnSuccess) {
129 g_mutex_unlock (&gpixbuf->mutex);
130 /* TODO: Map kCVReturnError etc. into strings */
131 GST_ERROR ("%p: unable to lock base address for pixbuf %p: %d", gpixbuf,
132 gpixbuf->buf, cvret);
135 gpixbuf->lock_state =
136 (flags & GST_MAP_WRITE) ?
137 GST_APPLE_CORE_VIDEO_MEMORY_LOCKED_READ_WRITE :
138 GST_APPLE_CORE_VIDEO_MEMORY_LOCKED_READONLY;
141 case GST_APPLE_CORE_VIDEO_MEMORY_LOCKED_READONLY:
142 if (flags & GST_MAP_WRITE) {
143 g_mutex_unlock (&gpixbuf->mutex);
144 GST_ERROR ("%p: pixel buffer %p already locked for read-only access",
145 gpixbuf, gpixbuf->buf);
150 case GST_APPLE_CORE_VIDEO_MEMORY_LOCKED_READ_WRITE:
151 break; /* nothing to do, already most permissive mapping */
154 g_atomic_int_inc (&gpixbuf->lock_count);
156 g_mutex_unlock (&gpixbuf->mutex);
158 GST_DEBUG ("%p: pixbuf %p, %s (%d times)",
161 _lock_state_names[gpixbuf->lock_state], gpixbuf->lock_count);
167 * gst_apple_core_video_pixel_buffer_unlock:
168 * @gpixbuf: the wrapped CVPixelBuffer
170 * Unlocks the pixel buffer from CPU memory. Should be called
171 * for every gst_apple_core_video_pixel_buffer_lock() call.
174 gst_apple_core_video_pixel_buffer_unlock (GstAppleCoreVideoPixelBuffer *
177 CVOptionFlags lockFlags;
180 if (gpixbuf->lock_state == GST_APPLE_CORE_VIDEO_MEMORY_UNLOCKED) {
181 GST_ERROR ("%p: pixel buffer %p not locked", gpixbuf, gpixbuf->buf);
185 if (!g_atomic_int_dec_and_test (&gpixbuf->lock_count)) {
186 return TRUE; /* still locked, by current and/or other callers */
189 g_mutex_lock (&gpixbuf->mutex);
192 (gpixbuf->lock_state ==
193 GST_APPLE_CORE_VIDEO_MEMORY_LOCKED_READONLY) ? kCVPixelBufferLock_ReadOnly
195 cvret = CVPixelBufferUnlockBaseAddress (gpixbuf->buf, lockFlags);
196 if (cvret != kCVReturnSuccess) {
197 g_mutex_unlock (&gpixbuf->mutex);
198 g_atomic_int_inc (&gpixbuf->lock_count);
199 /* TODO: Map kCVReturnError etc. into strings */
200 GST_ERROR ("%p: unable to unlock base address for pixbuf %p: %d", gpixbuf,
201 gpixbuf->buf, cvret);
205 gpixbuf->lock_state = GST_APPLE_CORE_VIDEO_MEMORY_UNLOCKED;
207 g_mutex_unlock (&gpixbuf->mutex);
209 GST_DEBUG ("%p: pixbuf %p, %s (%d locks remaining)",
212 _lock_state_names[gpixbuf->lock_state], gpixbuf->lock_count);
218 * GstAppleCoreVideoAllocator
221 struct _GstAppleCoreVideoAllocatorClass
223 GstAllocatorClass parent_class;
226 typedef struct _GstAppleCoreVideoAllocatorClass GstAppleCoreVideoAllocatorClass;
228 struct _GstAppleCoreVideoAllocator
230 GstAllocator parent_instance;
233 typedef struct _GstAppleCoreVideoAllocator GstAppleCoreVideoAllocator;
235 /* GType for GstAppleCoreVideoAllocator */
236 GType gst_apple_core_video_allocator_get_type (void);
237 #define GST_TYPE_APPLE_CORE_VIDEO_ALLOCATOR (gst_apple_core_video_allocator_get_type())
238 #define GST_IS_APPLE_CORE_VIDEO_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_APPLE_CORE_VIDEO_ALLOCATOR))
239 #define GST_IS_APPLE_CORE_VIDEO_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_APPLE_CORE_VIDEO_ALLOCATOR))
240 #define GST_APPLE_CORE_VIDEO_ALLOCATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_APPLE_CORE_VIDEO_ALLOCATOR, GstAppleCoreVideoAllocatorClass))
241 #define GST_APPLE_CORE_VIDEO_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_APPLE_CORE_VIDEO_ALLOCATOR, GstAppleCoreVideoAllocator))
242 #define GST_APPLE_CORE_VIDEO_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_APPLE_CORE_VIDEO_ALLOCATOR, GstAppleCoreVideoAllocatorClass))
244 G_DEFINE_TYPE (GstAppleCoreVideoAllocator, gst_apple_core_video_allocator,
247 /* Name for allocator registration */
248 #define GST_APPLE_CORE_VIDEO_ALLOCATOR_NAME "AppleCoreVideoMemory"
250 /* Singleton instance of GstAppleCoreVideoAllocator */
251 static GstAppleCoreVideoAllocator *_apple_core_video_allocator;
254 * gst_apple_core_video_memory_init:
256 * Initializes the Core Video Memory allocator. This function must be called
257 * before #GstAppleCoreVideoMemory can be created.
259 * It is safe to call this function multiple times.
262 gst_apple_core_video_memory_init (void)
264 static gsize _init = 0;
266 if (g_once_init_enter (&_init)) {
267 GST_DEBUG_CATEGORY_INIT (GST_CAT_APPLE_CORE_VIDEO_MEMORY, "corevideomemory",
268 0, "Apple Core Video Memory");
270 _apple_core_video_allocator =
271 g_object_new (GST_TYPE_APPLE_CORE_VIDEO_ALLOCATOR, NULL);
272 gst_object_ref_sink (_apple_core_video_allocator);
274 gst_allocator_register (GST_APPLE_CORE_VIDEO_ALLOCATOR_NAME,
275 gst_object_ref (_apple_core_video_allocator));
276 g_once_init_leave (&_init, 1);
281 * gst_is_apple_core_video_memory:
284 * Checks whether @mem is backed by a CVPixelBuffer.
285 * This has limited use since #GstAppleCoreVideoMemory is transparently
286 * mapped into CPU memory on request.
288 * Returns: %TRUE when @mem is backed by a CVPixelBuffer
291 gst_is_apple_core_video_memory (GstMemory * mem)
293 g_return_val_if_fail (mem != NULL, FALSE);
295 return GST_IS_APPLE_CORE_VIDEO_ALLOCATOR (mem->allocator);
299 * gst_apple_core_video_memory_new:
301 * Helper function for gst_apple_core_video_mem_share().
302 * Users should call gst_apple_core_video_memory_new_wrapped() instead.
304 static GstAppleCoreVideoMemory *
305 gst_apple_core_video_memory_new (GstMemoryFlags flags, GstMemory * parent,
306 GstAppleCoreVideoPixelBuffer * gpixbuf, gsize plane, gsize maxsize,
307 gsize align, gsize offset, gsize size)
309 GstAppleCoreVideoMemory *mem;
311 g_return_val_if_fail (gpixbuf != NULL, NULL);
313 mem = g_slice_new0 (GstAppleCoreVideoMemory);
314 gst_memory_init (GST_MEMORY_CAST (mem), flags,
315 GST_ALLOCATOR_CAST (_apple_core_video_allocator), parent, maxsize, align,
318 mem->gpixbuf = gst_apple_core_video_pixel_buffer_ref (gpixbuf);
321 GST_DEBUG ("%p: gpixbuf %p, plane: %" G_GSSIZE_FORMAT ", size %"
322 G_GSIZE_FORMAT, mem, mem->gpixbuf, mem->plane, mem->mem.size);
328 * gst_apple_core_video_memory_new_wrapped:
329 * @gpixbuf: the backing #GstAppleCoreVideoPixelBuffer
330 * @plane: the plane this memory will represent, or 0 for non-planar buffer
331 * @size: the size of the buffer or specific plane
333 * Returns: a newly allocated #GstAppleCoreVideoMemory
335 GstAppleCoreVideoMemory *
336 gst_apple_core_video_memory_new_wrapped (GstAppleCoreVideoPixelBuffer * gpixbuf,
337 gsize plane, gsize size)
339 return gst_apple_core_video_memory_new (0, NULL, gpixbuf, plane, size, 0, 0,
344 gst_apple_core_video_mem_map (GstMemory * gmem, gsize maxsize,
347 GstAppleCoreVideoMemory *mem = (GstAppleCoreVideoMemory *) gmem;
350 if (!gst_apple_core_video_pixel_buffer_lock (mem->gpixbuf, flags))
353 if (CVPixelBufferIsPlanar (mem->gpixbuf->buf)) {
354 ret = CVPixelBufferGetBaseAddressOfPlane (mem->gpixbuf->buf, mem->plane);
357 GST_DEBUG ("%p: pixbuf %p plane %" G_GSIZE_FORMAT
358 " flags %08x: mapped %p", mem, mem->gpixbuf->buf, mem->plane, flags,
361 GST_ERROR ("%p: invalid plane base address (NULL) for pixbuf %p plane %"
362 G_GSIZE_FORMAT, mem, mem->gpixbuf->buf, mem->plane);
364 ret = CVPixelBufferGetBaseAddress (mem->gpixbuf->buf);
367 GST_DEBUG ("%p: pixbuf %p flags %08x: mapped %p", mem, mem->gpixbuf->buf,
370 GST_ERROR ("%p: invalid base address (NULL) for pixbuf %p"
371 G_GSIZE_FORMAT, mem, mem->gpixbuf->buf);
378 gst_apple_core_video_mem_unmap (GstMemory * gmem)
380 GstAppleCoreVideoMemory *mem = (GstAppleCoreVideoMemory *) gmem;
381 (void) gst_apple_core_video_pixel_buffer_unlock (mem->gpixbuf);
382 GST_DEBUG ("%p: pixbuf %p plane %" G_GSIZE_FORMAT, mem,
383 mem->gpixbuf->buf, mem->plane);
387 gst_apple_core_video_mem_share (GstMemory * gmem, gssize offset, gssize size)
389 GstAppleCoreVideoMemory *mem;
390 GstMemory *parent, *sub;
392 mem = (GstAppleCoreVideoMemory *) gmem;
394 /* find the real parent */
395 parent = gmem->parent;
400 size = gmem->size - offset;
402 /* the shared memory is always readonly */
404 GST_MEMORY_CAST (gst_apple_core_video_memory_new (GST_MINI_OBJECT_FLAGS
405 (parent) | GST_MINI_OBJECT_FLAG_LOCK_READONLY, parent, mem->gpixbuf,
406 mem->plane, gmem->maxsize, gmem->align, gmem->offset + offset, size));
412 gst_apple_core_video_mem_is_span (GstMemory * mem1, GstMemory * mem2,
415 /* We may only return FALSE since:
416 * 1) Core Video gives no guarantees about planes being consecutive.
417 * We may only know this after mapping.
418 * 2) GstAppleCoreVideoMemory instances for planes do not share a common
419 * parent -- i.e. they're not offsets into the same parent
422 * It's not unlikely that planes will be stored in consecutive memory
423 * but it should be checked by the user after mapping.
429 gst_apple_core_video_mem_free (GstAllocator * allocator, GstMemory * gmem)
431 GstAppleCoreVideoMemory *mem = (GstAppleCoreVideoMemory *) gmem;
433 gst_apple_core_video_pixel_buffer_unref (mem->gpixbuf);
435 g_slice_free (GstAppleCoreVideoMemory, mem);
439 gst_apple_core_video_allocator_class_init (GstAppleCoreVideoAllocatorClass *
442 GstAllocatorClass *allocator_class;
444 allocator_class = (GstAllocatorClass *) klass;
446 /* we don't do allocations, only wrap existing pixel buffers */
447 allocator_class->alloc = NULL;
448 allocator_class->free = gst_apple_core_video_mem_free;
452 gst_apple_core_video_allocator_init (GstAppleCoreVideoAllocator * allocator)
454 GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
456 alloc->mem_type = GST_APPLE_CORE_VIDEO_ALLOCATOR_NAME;
457 alloc->mem_map = gst_apple_core_video_mem_map;
458 alloc->mem_unmap = gst_apple_core_video_mem_unmap;
459 alloc->mem_share = gst_apple_core_video_mem_share;
460 alloc->mem_is_span = gst_apple_core_video_mem_is_span;
462 GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);