1 /*
2 * Copyright (C) 2009 Ole André Vadla Ravnås <oleavr@soundrop.com>
3 * Copyright (C) 2014 Collabora Ltd.
4 * Authors: Matthieu Bouron <matthieu.bouron@collabora.com>
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 #include "corevideobuffer.h"
23 #include "coremediabuffer.h"
24 #include "corevideomemory.h"
25
26 static const GstMetaInfo *gst_core_media_meta_get_info (void);
27
28 static void
gst_core_media_meta_add(GstBuffer * buffer,CMSampleBufferRef sample_buf,CVImageBufferRef image_buf,CMBlockBufferRef block_buf)29 gst_core_media_meta_add (GstBuffer * buffer, CMSampleBufferRef sample_buf,
30 CVImageBufferRef image_buf, CMBlockBufferRef block_buf)
31 {
32 GstCoreMediaMeta *meta;
33
34 meta =
35 (GstCoreMediaMeta *) gst_buffer_add_meta (buffer,
36 gst_core_media_meta_get_info (), NULL);
37 CFRetain (sample_buf);
38 if (image_buf)
39 CVBufferRetain (image_buf);
40 if (block_buf)
41 CFRetain (block_buf);
42 meta->sample_buf = sample_buf;
43 meta->image_buf = image_buf;
44 meta->block_buf = block_buf;
45 if (image_buf != NULL && CFGetTypeID (image_buf) == CVPixelBufferGetTypeID ())
46 meta->pixel_buf = (CVPixelBufferRef) image_buf;
47 else
48 meta->pixel_buf = NULL;
49 }
50
51 static gboolean
gst_core_media_meta_init(GstCoreMediaMeta * meta,gpointer params,GstBuffer * buf)52 gst_core_media_meta_init (GstCoreMediaMeta * meta, gpointer params,
53 GstBuffer * buf)
54 {
55 meta->sample_buf = NULL;
56 meta->image_buf = NULL;
57 meta->pixel_buf = NULL;
58 meta->block_buf = NULL;
59
60 return TRUE;
61 }
62
63 static void
gst_core_media_meta_free(GstCoreMediaMeta * meta,GstBuffer * buf)64 gst_core_media_meta_free (GstCoreMediaMeta * meta, GstBuffer * buf)
65 {
66 if (meta->image_buf != NULL) {
67 CVBufferRelease (meta->image_buf);
68 }
69
70 if (meta->block_buf != NULL) {
71 CFRelease (meta->block_buf);
72 }
73
74 CFRelease (meta->sample_buf);
75 }
76
77 static gboolean
gst_core_media_meta_transform(GstBuffer * transbuf,GstCoreMediaMeta * meta,GstBuffer * buffer,GQuark type,GstMetaTransformCopy * data)78 gst_core_media_meta_transform (GstBuffer * transbuf, GstCoreMediaMeta * meta,
79 GstBuffer * buffer, GQuark type, GstMetaTransformCopy * data)
80 {
81 if (!data->region) {
82 /* only copy if the complete data is copied as well */
83 gst_core_media_meta_add (transbuf, meta->sample_buf, meta->image_buf,
84 meta->block_buf);
85 } else {
86 GST_WARNING_OBJECT (transbuf,
87 "dropping Core Media metadata due to partial buffer");
88 }
89
90 return TRUE; /* retval unused */
91 }
92
93 GType
gst_core_media_meta_api_get_type(void)94 gst_core_media_meta_api_get_type (void)
95 {
96 static GType type;
97 static const gchar *tags[] = { "memory", NULL };
98
99 if (g_once_init_enter (&type)) {
100 GType _type = gst_meta_api_type_register ("GstCoreMediaMetaAPI", tags);
101 g_once_init_leave (&type, _type);
102 }
103 return type;
104 }
105
106 static const GstMetaInfo *
gst_core_media_meta_get_info(void)107 gst_core_media_meta_get_info (void)
108 {
109 static const GstMetaInfo *core_media_meta_info = NULL;
110
111 if (g_once_init_enter (&core_media_meta_info)) {
112 const GstMetaInfo *meta = gst_meta_register (GST_CORE_MEDIA_META_API_TYPE,
113 "GstCoreMediaMeta", sizeof (GstCoreMediaMeta),
114 (GstMetaInitFunction) gst_core_media_meta_init,
115 (GstMetaFreeFunction) gst_core_media_meta_free,
116 (GstMetaTransformFunction) gst_core_media_meta_transform);
117 g_once_init_leave (&core_media_meta_info, meta);
118 }
119 return core_media_meta_info;
120 }
121
122 static GstVideoFormat
gst_core_media_buffer_get_video_format(OSType format)123 gst_core_media_buffer_get_video_format (OSType format)
124 {
125 switch (format) {
126 case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
127 return GST_VIDEO_FORMAT_NV12;
128 case kCVPixelFormatType_422YpCbCr8_yuvs:
129 return GST_VIDEO_FORMAT_YUY2;
130 case kCVPixelFormatType_422YpCbCr8:
131 return GST_VIDEO_FORMAT_UYVY;
132 case kCVPixelFormatType_32BGRA:
133 return GST_VIDEO_FORMAT_BGRA;
134 case kCVPixelFormatType_32RGBA:
135 return GST_VIDEO_FORMAT_RGBA;
136 default:
137 GST_WARNING ("Unknown OSType format: %d", (gint) format);
138 return GST_VIDEO_FORMAT_UNKNOWN;
139 }
140 }
141
142 static gboolean
gst_core_media_buffer_wrap_block_buffer(GstBuffer * buf,CMBlockBufferRef block_buf)143 gst_core_media_buffer_wrap_block_buffer (GstBuffer * buf,
144 CMBlockBufferRef block_buf)
145 {
146 OSStatus status;
147 gchar *data = NULL;
148 size_t offset = 0, length_at_offset, total_length;
149
150 /* CMBlockBuffer can contain multiple non-continuous memory blocks */
151 do {
152 status =
153 CMBlockBufferGetDataPointer (block_buf, offset, &length_at_offset,
154 &total_length, &data);
155 if (status != kCMBlockBufferNoErr) {
156 return FALSE;
157 }
158
159 /* retaining the CMBlockBuffer so it won't go away for the lifetime of the GstMemory */
160 gst_buffer_append_memory (buf,
161 gst_memory_new_wrapped (0, data, length_at_offset, 0, length_at_offset,
162 (gpointer) CFRetain (block_buf), (GDestroyNotify) CFRelease));
163
164 offset += length_at_offset;
165 } while (offset < total_length);
166
167 return TRUE;
168 }
169
170 static GstBuffer *
gst_core_media_buffer_new_from_buffer(GstBuffer * buf,GstVideoInfo * info)171 gst_core_media_buffer_new_from_buffer (GstBuffer * buf, GstVideoInfo * info)
172 {
173 gboolean ret;
174 GstBuffer *copy_buf;
175 GstVideoFrame dest, src;
176 GstAllocator *allocator;
177
178 allocator = gst_allocator_find (GST_ALLOCATOR_SYSMEM);
179 if (!allocator) {
180 GST_ERROR ("Could not find SYSMEM allocator");
181 return NULL;
182 }
183
184 copy_buf = gst_buffer_new_allocate (allocator, info->size, NULL);
185
186 gst_object_unref (allocator);
187
188 if (!gst_video_frame_map (&dest, info, copy_buf, GST_MAP_WRITE)) {
189 GST_ERROR ("Could not map destination frame");
190 goto error;
191 }
192
193 if (!gst_video_frame_map (&src, info, buf, GST_MAP_READ)) {
194 GST_ERROR ("Could not map source frame");
195 gst_video_frame_unmap (&dest);
196 goto error;
197 }
198
199 ret = gst_video_frame_copy (&dest, &src);
200
201 gst_video_frame_unmap (&dest);
202 gst_video_frame_unmap (&src);
203
204 if (!ret) {
205 GST_ERROR ("Could not copy frame");
206 goto error;
207 }
208
209 return copy_buf;
210
211 error:
212 if (copy_buf) {
213 gst_buffer_unref (copy_buf);
214 }
215 return NULL;
216 }
217
218 static gboolean
gst_video_info_init_from_pixel_buffer(GstVideoInfo * info,CVPixelBufferRef pixel_buf)219 gst_video_info_init_from_pixel_buffer (GstVideoInfo * info,
220 CVPixelBufferRef pixel_buf)
221 {
222 size_t width, height;
223 OSType format_type;
224 GstVideoFormat video_format;
225
226 width = CVPixelBufferGetWidth (pixel_buf);
227 height = CVPixelBufferGetHeight (pixel_buf);
228 format_type = CVPixelBufferGetPixelFormatType (pixel_buf);
229 video_format = gst_core_media_buffer_get_video_format (format_type);
230
231 if (video_format == GST_VIDEO_FORMAT_UNKNOWN) {
232 return FALSE;
233 }
234
235 gst_video_info_init (info);
236 gst_video_info_set_format (info, video_format, width, height);
237
238 return TRUE;
239 }
240
241 GstBuffer *
gst_core_media_buffer_new(CMSampleBufferRef sample_buf,gboolean use_video_meta,GstVideoTextureCache * cache)242 gst_core_media_buffer_new (CMSampleBufferRef sample_buf,
243 gboolean use_video_meta, GstVideoTextureCache * cache)
244 {
245 CVImageBufferRef image_buf;
246 CMBlockBufferRef block_buf;
247 GstBuffer *buf;
248
249 image_buf = CMSampleBufferGetImageBuffer (sample_buf);
250 block_buf = CMSampleBufferGetDataBuffer (sample_buf);
251
252 buf = gst_buffer_new ();
253
254 gst_core_media_meta_add (buf, sample_buf, image_buf, block_buf);
255
256 if (image_buf != NULL && CFGetTypeID (image_buf) == CVPixelBufferGetTypeID ()) {
257 GstVideoInfo info;
258 gboolean has_padding = FALSE;
259 CVPixelBufferRef pixel_buf = (CVPixelBufferRef) image_buf;
260
261 if (!gst_video_info_init_from_pixel_buffer (&info, pixel_buf)) {
262 goto error;
263 }
264
265 gst_core_video_wrap_pixel_buffer (buf, &info, pixel_buf, cache,
266 &has_padding);
267
268 /* If the video meta API is not supported, remove padding by
269 * copying the core media buffer to a system memory buffer */
270 if (has_padding && !use_video_meta) {
271 GstBuffer *copy_buf;
272 copy_buf = gst_core_media_buffer_new_from_buffer (buf, &info);
273 if (!copy_buf) {
274 goto error;
275 }
276
277 gst_buffer_unref (buf);
278 buf = copy_buf;
279 }
280
281 } else if (block_buf != NULL) {
282 if (!gst_core_media_buffer_wrap_block_buffer (buf, block_buf)) {
283 goto error;
284 }
285 } else {
286 goto error;
287 }
288
289 return buf;
290
291 error:
292 if (buf) {
293 gst_buffer_unref (buf);
294 }
295 return NULL;
296 }
297
298 CVPixelBufferRef
gst_core_media_buffer_get_pixel_buffer(GstBuffer * buf)299 gst_core_media_buffer_get_pixel_buffer (GstBuffer * buf)
300 {
301 GstCoreMediaMeta *meta = (GstCoreMediaMeta *) gst_buffer_get_meta (buf,
302 GST_CORE_MEDIA_META_API_TYPE);
303 g_return_val_if_fail (meta != NULL, NULL);
304
305 return CVPixelBufferRetain (meta->pixel_buf);
306 }
307