1 /*
2 * Copyright (C) 2010 Ole André Vadla Ravnås <oleavr@soundrop.com>
3 *
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.
8 *
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 more details.
13 *
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., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23 #include "corevideobuffer.h"
24 #include "corevideomemory.h"
25 #if !HAVE_IOS
26 #include "iosurfacememory.h"
27 #endif
28
29 static const GstMetaInfo *gst_core_video_meta_get_info (void);
30
31 static void
gst_core_video_meta_add(GstBuffer * buffer,CVBufferRef cvbuf)32 gst_core_video_meta_add (GstBuffer * buffer, CVBufferRef cvbuf)
33 {
34 GstCoreVideoMeta *meta;
35
36 meta = (GstCoreVideoMeta *) gst_buffer_add_meta (buffer,
37 gst_core_video_meta_get_info (), NULL);
38 meta->cvbuf = CVBufferRetain (cvbuf);
39 meta->pixbuf = (CVPixelBufferRef) cvbuf;
40 }
41
42 static gboolean
gst_core_video_meta_init(GstCoreVideoMeta * meta,gpointer params,GstBuffer * buf)43 gst_core_video_meta_init (GstCoreVideoMeta * meta, gpointer params,
44 GstBuffer * buf)
45 {
46 meta->cvbuf = NULL;
47 meta->pixbuf = NULL;
48
49 return TRUE;
50 }
51
52 static void
gst_core_video_meta_free(GstCoreVideoMeta * meta,GstBuffer * buf)53 gst_core_video_meta_free (GstCoreVideoMeta * meta, GstBuffer * buf)
54 {
55 CVBufferRelease (meta->cvbuf);
56 }
57
58 static gboolean
gst_core_video_meta_transform(GstBuffer * transbuf,GstCoreVideoMeta * meta,GstBuffer * buffer,GQuark type,GstMetaTransformCopy * data)59 gst_core_video_meta_transform (GstBuffer * transbuf, GstCoreVideoMeta * meta,
60 GstBuffer * buffer, GQuark type, GstMetaTransformCopy * data)
61 {
62 if (!data->region) {
63 /* only copy if the complete data is copied as well */
64 gst_core_video_meta_add (transbuf, meta->cvbuf);
65 } else {
66 GST_WARNING_OBJECT (transbuf,
67 "dropping Core Video metadata due to partial buffer");
68 }
69
70 return TRUE; /* retval unused */
71 }
72
73 GType
gst_core_video_meta_api_get_type(void)74 gst_core_video_meta_api_get_type (void)
75 {
76 static volatile GType type;
77 static const gchar *tags[] = { "memory", NULL };
78
79 if (g_once_init_enter (&type)) {
80 GType _type = gst_meta_api_type_register ("GstCoreVideoMetaAPI", tags);
81 g_once_init_leave (&type, _type);
82 }
83 return type;
84 }
85
86 static const GstMetaInfo *
gst_core_video_meta_get_info(void)87 gst_core_video_meta_get_info (void)
88 {
89 static const GstMetaInfo *core_video_meta_info = NULL;
90
91 if (g_once_init_enter (&core_video_meta_info)) {
92 const GstMetaInfo *meta = gst_meta_register (GST_CORE_VIDEO_META_API_TYPE,
93 "GstCoreVideoMeta", sizeof (GstCoreVideoMeta),
94 (GstMetaInitFunction) gst_core_video_meta_init,
95 (GstMetaFreeFunction) gst_core_video_meta_free,
96 (GstMetaTransformFunction) gst_core_video_meta_transform);
97 g_once_init_leave (&core_video_meta_info, meta);
98 }
99 return core_video_meta_info;
100 }
101
102 static GstMemory *
_create_glmem(GstAppleCoreVideoPixelBuffer * gpixbuf,GstVideoInfo * info,guint plane,gsize size,GstVideoTextureCache * cache)103 _create_glmem (GstAppleCoreVideoPixelBuffer * gpixbuf,
104 GstVideoInfo * info, guint plane, gsize size, GstVideoTextureCache * cache)
105 {
106 #if HAVE_IOS
107 return gst_video_texture_cache_create_memory (cache, gpixbuf, plane, size);
108 #else
109 GstIOSurfaceMemory *mem;
110 GstGLFormat tex_format =
111 gst_gl_format_from_video_info (cache->ctx, info, plane);
112 CVPixelBufferRef pixel_buf = gpixbuf->buf;
113 IOSurfaceRef surface = CVPixelBufferGetIOSurface (pixel_buf);
114
115 CFRetain (pixel_buf);
116 mem = gst_io_surface_memory_wrapped (cache->ctx,
117 surface, GST_GL_TEXTURE_TARGET_RECTANGLE, tex_format,
118 info, plane, NULL, pixel_buf, (GDestroyNotify) CFRelease);
119 return GST_MEMORY_CAST (mem);
120 #endif
121 }
122
123 void
gst_core_video_wrap_pixel_buffer(GstBuffer * buf,GstVideoInfo * info,CVPixelBufferRef pixel_buf,GstVideoTextureCache * cache,gboolean * has_padding)124 gst_core_video_wrap_pixel_buffer (GstBuffer * buf,
125 GstVideoInfo * info,
126 CVPixelBufferRef pixel_buf,
127 GstVideoTextureCache * cache, gboolean * has_padding)
128 {
129 guint n_planes;
130 gsize offset[GST_VIDEO_MAX_PLANES] = { 0 };
131 gint stride[GST_VIDEO_MAX_PLANES] = { 0 };
132 UInt32 size;
133 GstAppleCoreVideoPixelBuffer *gpixbuf;
134 GstMemory *mem = NULL;
135 gboolean do_gl = cache != NULL;
136
137 gpixbuf = gst_apple_core_video_pixel_buffer_new (pixel_buf);
138
139 if (has_padding)
140 *has_padding = FALSE;
141
142 if (CVPixelBufferIsPlanar (pixel_buf)) {
143 gint i, size = 0, plane_offset = 0;
144
145 n_planes = CVPixelBufferGetPlaneCount (pixel_buf);
146 for (i = 0; i < n_planes; i++) {
147 stride[i] = CVPixelBufferGetBytesPerRowOfPlane (pixel_buf, i);
148
149 if (stride[i] != GST_VIDEO_INFO_PLANE_STRIDE (info, i) && has_padding)
150 *has_padding = TRUE;
151
152 size = stride[i] * CVPixelBufferGetHeightOfPlane (pixel_buf, i);
153 offset[i] = plane_offset;
154 plane_offset += size;
155
156 if (do_gl)
157 mem = _create_glmem (gpixbuf, info, i, size, cache);
158 else
159 mem =
160 GST_MEMORY_CAST (gst_apple_core_video_memory_new_wrapped (gpixbuf,
161 i, size));
162 gst_buffer_append_memory (buf, mem);
163 }
164 } else {
165 n_planes = 1;
166 stride[0] = CVPixelBufferGetBytesPerRow (pixel_buf);
167 offset[0] = 0;
168 size = stride[0] * CVPixelBufferGetHeight (pixel_buf);
169
170 if (do_gl)
171 mem = _create_glmem (gpixbuf, info, 0, size, cache);
172 else
173 mem =
174 GST_MEMORY_CAST (gst_apple_core_video_memory_new_wrapped (gpixbuf, 0,
175 size));
176 gst_buffer_append_memory (buf, mem);
177 }
178
179 gst_apple_core_video_pixel_buffer_unref (gpixbuf);
180
181 if (info) {
182 GstVideoMeta *video_meta;
183
184 video_meta =
185 gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE,
186 GST_VIDEO_INFO_FORMAT (info), info->width, info->height, n_planes,
187 offset, stride);
188 }
189 }
190
191 static GstVideoFormat
gst_core_video_get_video_format(OSType format)192 gst_core_video_get_video_format (OSType format)
193 {
194 switch (format) {
195 case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
196 return GST_VIDEO_FORMAT_NV12;
197 case kCVPixelFormatType_422YpCbCr8_yuvs:
198 return GST_VIDEO_FORMAT_YUY2;
199 case kCVPixelFormatType_422YpCbCr8:
200 return GST_VIDEO_FORMAT_UYVY;
201 case kCVPixelFormatType_32BGRA:
202 return GST_VIDEO_FORMAT_BGRA;
203 case kCVPixelFormatType_32RGBA:
204 return GST_VIDEO_FORMAT_RGBA;
205 default:
206 GST_WARNING ("Unknown OSType format: %d", (gint) format);
207 return GST_VIDEO_FORMAT_UNKNOWN;
208 }
209 }
210
211
212 gboolean
gst_core_video_info_init_from_pixel_buffer(GstVideoInfo * info,CVPixelBufferRef pixel_buf)213 gst_core_video_info_init_from_pixel_buffer (GstVideoInfo * info,
214 CVPixelBufferRef pixel_buf)
215 {
216 size_t width, height;
217 OSType format_type;
218 GstVideoFormat video_format;
219
220 width = CVPixelBufferGetWidth (pixel_buf);
221 height = CVPixelBufferGetHeight (pixel_buf);
222 format_type = CVPixelBufferGetPixelFormatType (pixel_buf);
223 video_format = gst_core_video_get_video_format (format_type);
224
225 if (video_format == GST_VIDEO_FORMAT_UNKNOWN) {
226 return FALSE;
227 }
228
229 gst_video_info_init (info);
230 gst_video_info_set_format (info, video_format, width, height);
231
232 return TRUE;
233 }
234
235
236 GstBuffer *
gst_core_video_buffer_new(CVBufferRef cvbuf,GstVideoInfo * vinfo,GstVideoTextureCache * cache)237 gst_core_video_buffer_new (CVBufferRef cvbuf, GstVideoInfo * vinfo,
238 GstVideoTextureCache * cache)
239 {
240 CVPixelBufferRef pixbuf = NULL;
241 GstBuffer *buf;
242 GstCoreVideoMeta *meta;
243
244 if (CFGetTypeID (cvbuf) != CVPixelBufferGetTypeID ())
245 /* TODO: Do we need to handle other buffer types? */
246 return NULL;
247
248 pixbuf = (CVPixelBufferRef) cvbuf;
249
250 buf = gst_buffer_new ();
251
252 /* add the corevideo meta to pass the underlying corevideo buffer */
253 meta = (GstCoreVideoMeta *) gst_buffer_add_meta (buf,
254 gst_core_video_meta_get_info (), NULL);
255 meta->cvbuf = CVBufferRetain (cvbuf);
256 meta->pixbuf = pixbuf;
257
258 gst_core_video_wrap_pixel_buffer (buf, vinfo, pixbuf, cache, NULL);
259
260 return buf;
261 }
262