| /* |
| * Copyright (C) 2010 Ole André Vadla Ravnås <oleavr@soundrop.com> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public |
| * License along with this library; if not, write to the |
| * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| # include "config.h" |
| #endif |
| #include "corevideobuffer.h" |
| #include "corevideomemory.h" |
| #if !HAVE_IOS |
| #include "iosurfacememory.h" |
| #endif |
| |
| static const GstMetaInfo *gst_core_video_meta_get_info (void); |
| |
| static void |
| gst_core_video_meta_add (GstBuffer * buffer, CVBufferRef cvbuf) |
| { |
| GstCoreVideoMeta *meta; |
| |
| meta = (GstCoreVideoMeta *) gst_buffer_add_meta (buffer, |
| gst_core_video_meta_get_info (), NULL); |
| meta->cvbuf = CVBufferRetain (cvbuf); |
| meta->pixbuf = (CVPixelBufferRef) cvbuf; |
| } |
| |
| static gboolean |
| gst_core_video_meta_init (GstCoreVideoMeta * meta, gpointer params, |
| GstBuffer * buf) |
| { |
| meta->cvbuf = NULL; |
| meta->pixbuf = NULL; |
| |
| return TRUE; |
| } |
| |
| static void |
| gst_core_video_meta_free (GstCoreVideoMeta * meta, GstBuffer * buf) |
| { |
| CVBufferRelease (meta->cvbuf); |
| } |
| |
| static gboolean |
| gst_core_video_meta_transform (GstBuffer * transbuf, GstCoreVideoMeta * meta, |
| GstBuffer * buffer, GQuark type, GstMetaTransformCopy * data) |
| { |
| if (!data->region) { |
| /* only copy if the complete data is copied as well */ |
| gst_core_video_meta_add (transbuf, meta->cvbuf); |
| } else { |
| GST_WARNING_OBJECT (transbuf, |
| "dropping Core Video metadata due to partial buffer"); |
| } |
| |
| return TRUE; /* retval unused */ |
| } |
| |
| GType |
| gst_core_video_meta_api_get_type (void) |
| { |
| static volatile GType type; |
| static const gchar *tags[] = { "memory", NULL }; |
| |
| if (g_once_init_enter (&type)) { |
| GType _type = gst_meta_api_type_register ("GstCoreVideoMetaAPI", tags); |
| g_once_init_leave (&type, _type); |
| } |
| return type; |
| } |
| |
| static const GstMetaInfo * |
| gst_core_video_meta_get_info (void) |
| { |
| static const GstMetaInfo *core_video_meta_info = NULL; |
| |
| if (g_once_init_enter (&core_video_meta_info)) { |
| const GstMetaInfo *meta = gst_meta_register (GST_CORE_VIDEO_META_API_TYPE, |
| "GstCoreVideoMeta", sizeof (GstCoreVideoMeta), |
| (GstMetaInitFunction) gst_core_video_meta_init, |
| (GstMetaFreeFunction) gst_core_video_meta_free, |
| (GstMetaTransformFunction) gst_core_video_meta_transform); |
| g_once_init_leave (&core_video_meta_info, meta); |
| } |
| return core_video_meta_info; |
| } |
| |
| static GstMemory * |
| _create_glmem (GstAppleCoreVideoPixelBuffer * gpixbuf, |
| GstVideoInfo * info, guint plane, gsize size, GstVideoTextureCache * cache) |
| { |
| #if HAVE_IOS |
| return gst_video_texture_cache_create_memory (cache, gpixbuf, plane, size); |
| #else |
| GstIOSurfaceMemory *mem; |
| GstGLFormat tex_format = |
| gst_gl_format_from_video_info (cache->ctx, info, plane); |
| CVPixelBufferRef pixel_buf = gpixbuf->buf; |
| IOSurfaceRef surface = CVPixelBufferGetIOSurface (pixel_buf); |
| |
| CFRetain (pixel_buf); |
| mem = gst_io_surface_memory_wrapped (cache->ctx, |
| surface, GST_GL_TEXTURE_TARGET_RECTANGLE, tex_format, |
| info, plane, NULL, pixel_buf, (GDestroyNotify) CFRelease); |
| return GST_MEMORY_CAST (mem); |
| #endif |
| } |
| |
| void |
| gst_core_video_wrap_pixel_buffer (GstBuffer * buf, |
| GstVideoInfo * info, |
| CVPixelBufferRef pixel_buf, |
| GstVideoTextureCache * cache, gboolean * has_padding) |
| { |
| guint n_planes; |
| gsize offset[GST_VIDEO_MAX_PLANES] = { 0 }; |
| gint stride[GST_VIDEO_MAX_PLANES] = { 0 }; |
| UInt32 size; |
| GstAppleCoreVideoPixelBuffer *gpixbuf; |
| GstMemory *mem = NULL; |
| gboolean do_gl = cache != NULL; |
| |
| gpixbuf = gst_apple_core_video_pixel_buffer_new (pixel_buf); |
| |
| if (has_padding) |
| *has_padding = FALSE; |
| |
| if (CVPixelBufferIsPlanar (pixel_buf)) { |
| gint i, size = 0, plane_offset = 0; |
| |
| n_planes = CVPixelBufferGetPlaneCount (pixel_buf); |
| for (i = 0; i < n_planes; i++) { |
| stride[i] = CVPixelBufferGetBytesPerRowOfPlane (pixel_buf, i); |
| |
| if (stride[i] != GST_VIDEO_INFO_PLANE_STRIDE (info, i) && has_padding) |
| *has_padding = TRUE; |
| |
| size = stride[i] * CVPixelBufferGetHeightOfPlane (pixel_buf, i); |
| offset[i] = plane_offset; |
| plane_offset += size; |
| |
| if (do_gl) |
| mem = _create_glmem (gpixbuf, info, i, size, cache); |
| else |
| mem = |
| GST_MEMORY_CAST (gst_apple_core_video_memory_new_wrapped (gpixbuf, |
| i, size)); |
| gst_buffer_append_memory (buf, mem); |
| } |
| } else { |
| n_planes = 1; |
| stride[0] = CVPixelBufferGetBytesPerRow (pixel_buf); |
| offset[0] = 0; |
| size = stride[0] * CVPixelBufferGetHeight (pixel_buf); |
| |
| if (do_gl) |
| mem = _create_glmem (gpixbuf, info, 0, size, cache); |
| else |
| mem = |
| GST_MEMORY_CAST (gst_apple_core_video_memory_new_wrapped (gpixbuf, 0, |
| size)); |
| gst_buffer_append_memory (buf, mem); |
| } |
| |
| gst_apple_core_video_pixel_buffer_unref (gpixbuf); |
| |
| if (info) { |
| GstVideoMeta *video_meta; |
| |
| video_meta = |
| gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE, |
| GST_VIDEO_INFO_FORMAT (info), info->width, info->height, n_planes, |
| offset, stride); |
| } |
| } |
| |
| static GstVideoFormat |
| gst_core_video_get_video_format (OSType format) |
| { |
| switch (format) { |
| case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: |
| return GST_VIDEO_FORMAT_NV12; |
| case kCVPixelFormatType_422YpCbCr8_yuvs: |
| return GST_VIDEO_FORMAT_YUY2; |
| case kCVPixelFormatType_422YpCbCr8: |
| return GST_VIDEO_FORMAT_UYVY; |
| case kCVPixelFormatType_32BGRA: |
| return GST_VIDEO_FORMAT_BGRA; |
| case kCVPixelFormatType_32RGBA: |
| return GST_VIDEO_FORMAT_RGBA; |
| default: |
| GST_WARNING ("Unknown OSType format: %d", (gint) format); |
| return GST_VIDEO_FORMAT_UNKNOWN; |
| } |
| } |
| |
| |
| gboolean |
| gst_core_video_info_init_from_pixel_buffer (GstVideoInfo * info, |
| CVPixelBufferRef pixel_buf) |
| { |
| size_t width, height; |
| OSType format_type; |
| GstVideoFormat video_format; |
| |
| width = CVPixelBufferGetWidth (pixel_buf); |
| height = CVPixelBufferGetHeight (pixel_buf); |
| format_type = CVPixelBufferGetPixelFormatType (pixel_buf); |
| video_format = gst_core_video_get_video_format (format_type); |
| |
| if (video_format == GST_VIDEO_FORMAT_UNKNOWN) { |
| return FALSE; |
| } |
| |
| gst_video_info_init (info); |
| gst_video_info_set_format (info, video_format, width, height); |
| |
| return TRUE; |
| } |
| |
| |
| GstBuffer * |
| gst_core_video_buffer_new (CVBufferRef cvbuf, GstVideoInfo * vinfo, |
| GstVideoTextureCache * cache) |
| { |
| CVPixelBufferRef pixbuf = NULL; |
| GstBuffer *buf; |
| GstCoreVideoMeta *meta; |
| |
| if (CFGetTypeID (cvbuf) != CVPixelBufferGetTypeID ()) |
| /* TODO: Do we need to handle other buffer types? */ |
| return NULL; |
| |
| pixbuf = (CVPixelBufferRef) cvbuf; |
| |
| buf = gst_buffer_new (); |
| |
| /* add the corevideo meta to pass the underlying corevideo buffer */ |
| meta = (GstCoreVideoMeta *) gst_buffer_add_meta (buf, |
| gst_core_video_meta_get_info (), NULL); |
| meta->cvbuf = CVBufferRetain (cvbuf); |
| meta->pixbuf = pixbuf; |
| |
| gst_core_video_wrap_pixel_buffer (buf, vinfo, pixbuf, cache, NULL); |
| |
| return buf; |
| } |