• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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