| /* |
| * GStreamer QuickTime video decoder codecs wrapper |
| * Copyright <2006, 2007> Fluendo <gstreamer@fluendo.com> |
| * Copyright <2006, 2007> Pioneers of the Inevitable <songbird@songbirdnest.com> |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| * DEALINGS IN THE SOFTWARE. |
| * |
| * Alternatively, the contents of this file may be used under the |
| * GNU Lesser General Public License Version 2.1 (the "LGPL"), in |
| * which case the following provisions apply instead of the ones |
| * mentioned above: |
| * |
| * 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., 59 Temple Place - Suite 330, |
| * Boston, MA 02111-1307, USA. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <string.h> |
| |
| #include "qtwrapper.h" |
| #include "codecmapping.h" |
| #include "qtutils.h" |
| #include "imagedescription.h" |
| |
| #define QTWRAPPER_VDEC_PARAMS_QDATA g_quark_from_static_string("qtwrapper-vdec-params") |
| |
| static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src", |
| GST_PAD_SRC, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS ("video/x-raw-yuv")); |
| |
| typedef struct _QTWrapperVideoDecoder QTWrapperVideoDecoder; |
| typedef struct _QTWrapperVideoDecoderClass QTWrapperVideoDecoderClass; |
| |
| #define MAC_LOCK(qtwrapper) g_mutex_lock (qtwrapper->lock) |
| #define MAC_UNLOCK(qtwrapper) g_mutex_unlock (qtwrapper->lock) |
| |
| struct _QTWrapperVideoDecoder |
| { |
| GstElement parent; |
| |
| GstPad *sinkpad; |
| GstPad *srcpad; |
| |
| GMutex *lock; |
| ComponentInstance instance; |
| CodecInfo codecinfo; |
| ImageDescriptionHandle idesc; |
| CodecDecompressParams *dparams; |
| CodecCapabilities codeccaps; |
| guint64 frameNumber; |
| ICMDecompressionSessionRef decsession; |
| GstFlowReturn lastret; |
| guint64 outsize; |
| guint width, height; |
| GstClockTime last_ts; |
| GstClockTime last_duration; |
| GstBuffer *prevbuf; |
| gboolean flushing; |
| gboolean framebuffering; |
| |
| /* width/height of output buffer */ |
| Rect rect; |
| }; |
| |
| struct _QTWrapperVideoDecoderClass |
| { |
| GstElementClass parent_class; |
| |
| Component component; |
| guint32 componentType; |
| guint32 componentSubType; |
| |
| GstPadTemplate *sinktempl; |
| }; |
| |
| typedef struct _QTWrapperVideoDecoderParams QTWrapperVideoDecoderParams; |
| |
| struct _QTWrapperVideoDecoderParams |
| { |
| Component component; |
| GstCaps *sinkcaps; |
| }; |
| |
| static GstElementClass *parent_class = NULL; |
| |
| static gboolean |
| qtwrapper_video_decoder_sink_setcaps (GstPad * pad, GstCaps * caps); |
| static GstFlowReturn qtwrapper_video_decoder_chain (GstPad * pad, |
| GstBuffer * buf); |
| static gboolean qtwrapper_video_decoder_sink_event (GstPad * pad, |
| GstEvent * event); |
| |
| static void qtwrapper_video_decoder_finalize (GObject * object); |
| static void decompressCb (void *decompressionTrackingRefCon, |
| OSStatus result, |
| ICMDecompressionTrackingFlags decompressionTrackingFlags, |
| CVPixelBufferRef pixelBuffer, |
| TimeValue64 displayTime, |
| TimeValue64 displayDuration, |
| ICMValidTimeFlags validTimeFlags, void *reserved, void *sourceFrameRefCon); |
| |
| /* |
| * Class setup |
| */ |
| |
| static void |
| qtwrapper_video_decoder_base_init (QTWrapperVideoDecoderClass * klass) |
| { |
| GstElementClass *element_class = GST_ELEMENT_CLASS (klass); |
| gchar *name = NULL; |
| gchar *info = NULL; |
| char *longname, *description; |
| ComponentDescription desc; |
| QTWrapperVideoDecoderParams *params; |
| |
| params = (QTWrapperVideoDecoderParams *) |
| g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass), |
| QTWRAPPER_VDEC_PARAMS_QDATA); |
| g_assert (params); |
| |
| get_name_info_from_component (params->component, &desc, &name, &info); |
| |
| /* Fill in details */ |
| longname = |
| g_strdup_printf ("QTWrapper Video Decoder : %s", GST_STR_NULL (name)); |
| description = |
| g_strdup_printf ("QTWrapper SCAudio wrapper for decoder: %s", |
| GST_STR_NULL (info)); |
| gst_element_class_set_metadata (element_class, longname, |
| "Codec/Decoder/Video", description, |
| "Fluendo <gstreamer@fluendo.com>, " |
| "Pioneers of the Inevitable <songbird@songbirdnest.com>"); |
| g_free (longname); |
| g_free (description); |
| g_free (name); |
| g_free (info); |
| |
| klass->sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK, |
| GST_PAD_ALWAYS, params->sinkcaps); |
| |
| gst_element_class_add_pad_template (element_class, klass->sinktempl); |
| gst_element_class_add_pad_template (element_class, |
| gst_static_pad_template_get (&src_templ)); |
| |
| /* Store class-global values */ |
| klass->component = params->component; |
| klass->componentType = desc.componentType; |
| klass->componentSubType = desc.componentSubType; |
| } |
| |
| static void |
| qtwrapper_video_decoder_class_init (QTWrapperVideoDecoderClass * klass) |
| { |
| GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
| |
| parent_class = g_type_class_peek_parent (klass); |
| |
| gobject_class->finalize = |
| GST_DEBUG_FUNCPTR (qtwrapper_video_decoder_finalize); |
| } |
| |
| static void |
| qtwrapper_video_decoder_init (QTWrapperVideoDecoder * qtwrapper) |
| { |
| QTWrapperVideoDecoderClass *oclass; |
| ImageSubCodecDecompressCapabilities capabs; |
| |
| GST_LOG ("..."); |
| oclass = (QTWrapperVideoDecoderClass *) (G_OBJECT_GET_CLASS (qtwrapper)); |
| |
| /* 1. Create a ocmponent instance */ |
| if (!(qtwrapper->instance = OpenComponent (oclass->component))) { |
| GST_ERROR_OBJECT (qtwrapper, "Couldn't create a component instance !"); |
| return; |
| } |
| |
| /* 2. Initialize decoder */ |
| memset (&capabs, 0, sizeof (ImageSubCodecDecompressCapabilities)); |
| if (ImageCodecInitialize (qtwrapper->instance, &capabs) != noErr) { |
| GST_ERROR_OBJECT (qtwrapper, "Couldn't initialize the QT component !"); |
| return; |
| } |
| |
| /* 3. Get codec info */ |
| memset (&qtwrapper->codecinfo, 0, sizeof (CodecInfo)); |
| if (ImageCodecGetCodecInfo (qtwrapper->instance, |
| &qtwrapper->codecinfo) != noErr) { |
| GST_ERROR_OBJECT (qtwrapper, "Couldn't get Codec Information !"); |
| return; |
| } |
| |
| /* sink pad */ |
| qtwrapper->sinkpad = gst_pad_new_from_template (oclass->sinktempl, "sink"); |
| gst_pad_set_setcaps_function (qtwrapper->sinkpad, |
| GST_DEBUG_FUNCPTR (qtwrapper_video_decoder_sink_setcaps)); |
| gst_pad_set_chain_function (qtwrapper->sinkpad, |
| GST_DEBUG_FUNCPTR (qtwrapper_video_decoder_chain)); |
| gst_pad_set_event_function (qtwrapper->sinkpad, |
| GST_DEBUG_FUNCPTR (qtwrapper_video_decoder_sink_event)); |
| gst_element_add_pad (GST_ELEMENT (qtwrapper), qtwrapper->sinkpad); |
| |
| /* src pad */ |
| qtwrapper->srcpad = gst_pad_new_from_static_template (&src_templ, "src"); |
| gst_element_add_pad (GST_ELEMENT (qtwrapper), qtwrapper->srcpad); |
| |
| qtwrapper->lock = g_mutex_new (); |
| } |
| |
| static void |
| qtwrapper_video_decoder_finalize (GObject * object) |
| { |
| QTWrapperVideoDecoder *qtwrapper; |
| |
| qtwrapper = (QTWrapperVideoDecoder *) object; |
| |
| if (qtwrapper->lock) |
| g_mutex_free (qtwrapper->lock); |
| |
| if (G_OBJECT_CLASS (parent_class)->finalize) |
| G_OBJECT_CLASS (parent_class)->finalize (object); |
| } |
| |
| /* fill_image_description |
| * Fills an ImageDescription with codec-specific values |
| * |
| * Doesn't fill in the idSize, width and height. |
| */ |
| |
| static void |
| fill_image_description (QTWrapperVideoDecoder * qtwrapper, |
| ImageDescription * desc) |
| { |
| QTWrapperVideoDecoderClass *oclass; |
| |
| oclass = (QTWrapperVideoDecoderClass *) (G_OBJECT_GET_CLASS (qtwrapper)); |
| |
| desc->cType = oclass->componentSubType; |
| desc->version = qtwrapper->codecinfo.version; |
| desc->revisionLevel = qtwrapper->codecinfo.revisionLevel; |
| desc->vendor = qtwrapper->codecinfo.vendor; |
| desc->temporalQuality = codecMaxQuality; |
| desc->spatialQuality = codecNormalQuality; |
| desc->hRes = Long2Fix (72); |
| desc->vRes = Long2Fix (72); |
| desc->frameCount = 1; |
| /* The following is a pure empiric calculation ... so there's are chances it |
| * might not work. To be fixed when we can figure out what the exact value should |
| * be. */ |
| desc->depth = 24; |
| /* no color table */ |
| desc->clutID = -1; |
| } |
| |
| |
| /* new_image_description |
| * |
| * Create an ImageDescription for the given 'codec_data' buffer. |
| */ |
| |
| static ImageDescription * |
| new_image_description (QTWrapperVideoDecoder * qtwrapper, GstBuffer * buf, |
| guint width, guint height) |
| { |
| QTWrapperVideoDecoderClass *oclass; |
| ImageDescription *desc = NULL; |
| |
| oclass = (QTWrapperVideoDecoderClass *) (G_OBJECT_GET_CLASS (qtwrapper)); |
| |
| if (buf) { |
| GST_LOG ("buf %p , size:%d", buf, GST_BUFFER_SIZE (buf)); |
| #if DEBUG_DUMP |
| gst_util_dump_mem (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); |
| #endif |
| } |
| |
| if (!buf) { |
| /* standard case, no codec data */ |
| desc = g_new0 (ImageDescription, 1); |
| desc->idSize = sizeof (ImageDescription); |
| fill_image_description (qtwrapper, desc); |
| } else { |
| if ((desc = |
| image_description_from_codec_data (buf, oclass->componentSubType))) |
| fill_image_description (qtwrapper, desc); |
| else |
| goto beach; |
| } |
| |
| /* Fix up values */ |
| desc->width = width; |
| desc->height = height; |
| desc->hRes = Long2Fix (72); |
| desc->vRes = Long2Fix (72); |
| |
| /* if we have h264, we need frame buffering */ |
| if ((oclass->componentSubType == QT_MAKE_FOURCC_LE ('a', 'v', 'c', '1'))) |
| qtwrapper->framebuffering = TRUE; |
| else |
| qtwrapper->framebuffering = FALSE; |
| |
| beach: |
| return desc; |
| } |
| |
| /* close_decoder |
| * |
| * Close and free decoder |
| */ |
| #if 0 |
| static void |
| close_decoder (QTWrapperVideoDecoder * qtwrapper) |
| { |
| if (qtwrapper->idesc) { |
| DisposeHandle ((Handle) qtwrapper->idesc); |
| qtwrapper->idesc = NULL; |
| } |
| |
| if (qtwrapper->prevbuf) { |
| gst_buffer_unref (qtwrapper->prevbuf); |
| qtwrapper->prevbuf = NULL; |
| } |
| |
| if (qtwrapper->dparams) { |
| g_free (qtwrapper->dparams); |
| qtwrapper->dparams = NULL; |
| } |
| |
| } |
| #endif |
| /* open_decoder |
| * |
| * Attempt to initialize the ImageDecompressorComponent with the given |
| * caps. |
| * |
| * Returns TRUE and fills *outcaps if the decoder was properly initialized |
| * Returns FALSE if something went wrong. |
| */ |
| |
| static gboolean |
| open_decoder (QTWrapperVideoDecoder * qtwrapper, GstCaps * caps, |
| GstCaps ** outcaps) |
| { |
| ImageDescription *desc; |
| gint width, height; |
| GstStructure *s; |
| const GValue *par = NULL; |
| const GValue *rate = NULL; |
| const GValue *cdata = NULL; |
| OSStatus status; |
| gboolean res = FALSE; |
| guint32 outformat; |
| |
| ICMDecompressionSessionOptionsRef sessionoptions = NULL; |
| ICMDecompressionTrackingCallbackRecord cbrecord; |
| CFMutableDictionaryRef pixelBufferAttributes = NULL; |
| |
| |
| s = gst_caps_get_structure (caps, 0); |
| |
| /* 1. Extract information from incoming caps */ |
| if ((!gst_structure_get_int (s, "width", &width)) || |
| (!gst_structure_get_int (s, "height", &height)) || |
| (!(rate = gst_structure_get_value (s, "framerate")))) |
| goto beach; |
| par = gst_structure_get_value (s, "pixel-aspect-ratio"); |
| cdata = gst_structure_get_value (s, "codec_data"); |
| |
| /* 2. Create ImageDescription */ |
| if (cdata) { |
| GstBuffer *cdatabuf; |
| |
| cdatabuf = gst_value_get_buffer (cdata); |
| desc = new_image_description (qtwrapper, cdatabuf, width, height); |
| } else { |
| desc = new_image_description (qtwrapper, NULL, width, height); |
| } |
| |
| #if DEBUG_DUMP |
| dump_image_description (desc); |
| #endif |
| |
| /* 3.a. Create a handle to receive the ImageDescription */ |
| GST_LOG_OBJECT (qtwrapper, |
| "Creating a new ImageDescriptionHandle of %" G_GSIZE_FORMAT " bytes", |
| desc->idSize); |
| qtwrapper->idesc = (ImageDescriptionHandle) NewHandleClear (desc->idSize); |
| if (G_UNLIKELY (qtwrapper->idesc == NULL)) { |
| GST_WARNING_OBJECT (qtwrapper, |
| "Failed to create an ImageDescriptionHandle of size %" G_GSIZE_FORMAT, |
| desc->idSize); |
| g_free (desc); |
| goto beach; |
| } |
| |
| /* 3.b. Copy the ImageDescription to the handle */ |
| GST_LOG_OBJECT (qtwrapper, |
| "Copying %" G_GSIZE_FORMAT |
| " bytes from desc [%p] to *qtwrapper->video [%p]", desc->idSize, desc, |
| *qtwrapper->idesc); |
| memcpy (*qtwrapper->idesc, desc, desc->idSize); |
| g_free (desc); |
| |
| #if G_BYTE_ORDER == G_BIG_ENDIAN |
| outformat = kYUVSPixelFormat; |
| #else |
| outformat = k2vuyPixelFormat; |
| #endif |
| |
| /* 4. Put output pixel info in dictionnnary */ |
| pixelBufferAttributes = |
| CFDictionaryCreateMutable (NULL, 0, &kCFTypeDictionaryKeyCallBacks, |
| &kCFTypeDictionaryValueCallBacks); |
| |
| addSInt32ToDictionary (pixelBufferAttributes, kCVPixelBufferWidthKey, width); |
| addSInt32ToDictionary (pixelBufferAttributes, kCVPixelBufferHeightKey, |
| height); |
| addSInt32ToDictionary (pixelBufferAttributes, |
| kCVPixelBufferPixelFormatTypeKey, outformat); |
| |
| /* 5. fill in callback structure */ |
| |
| cbrecord.decompressionTrackingCallback = decompressCb; |
| cbrecord.decompressionTrackingRefCon = qtwrapper; |
| |
| /* 6. create decompressionsession */ |
| status = ICMDecompressionSessionCreate (NULL, |
| qtwrapper->idesc, |
| sessionoptions, pixelBufferAttributes, &cbrecord, &qtwrapper->decsession); |
| |
| qtwrapper->outsize = width * height * 2; |
| qtwrapper->width = width; |
| qtwrapper->height = height; |
| |
| if (status) { |
| GST_DEBUG_OBJECT (qtwrapper, |
| "Error when Calling ICMDecompressionSessionCreate : %ld", status); |
| goto beach; |
| } |
| #if G_BYTE_ORDER == G_BIG_ENDIAN |
| outformat = GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'); |
| #else |
| outformat = GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'); |
| #endif |
| |
| /* 9. Create output caps */ |
| *outcaps = gst_caps_new_simple ("video/x-raw-yuv", |
| "format", GST_TYPE_FOURCC, outformat, |
| "width", G_TYPE_INT, width, |
| "height", G_TYPE_INT, height, |
| "framerate", GST_TYPE_FRACTION, |
| gst_value_get_fraction_numerator (rate), |
| gst_value_get_fraction_denominator (rate), NULL); |
| if (par) |
| gst_structure_set_value (gst_caps_get_structure (*outcaps, 0), |
| "pixel-aspect-ratio", par); |
| res = TRUE; |
| |
| beach: |
| return res; |
| } |
| |
| static gboolean |
| qtwrapper_video_decoder_sink_setcaps (GstPad * pad, GstCaps * caps) |
| { |
| QTWrapperVideoDecoder *qtwrapper; |
| gboolean ret = FALSE; |
| GstCaps *othercaps = NULL; |
| |
| qtwrapper = (QTWrapperVideoDecoder *) gst_pad_get_parent (pad); |
| |
| GST_LOG_OBJECT (qtwrapper, "caps:%" GST_PTR_FORMAT, caps); |
| |
| /* Setup the decoder with the given input caps */ |
| if (!(open_decoder (qtwrapper, caps, &othercaps))) { |
| goto beach; |
| } |
| |
| ret = gst_pad_set_caps (qtwrapper->srcpad, othercaps); |
| if (!ret) |
| goto beach; |
| |
| beach: |
| if (othercaps) |
| gst_caps_unref (othercaps); |
| gst_object_unref (qtwrapper); |
| return ret; |
| } |
| |
| static void |
| decompressCb (void *decompressionTrackingRefCon, |
| OSStatus result, |
| ICMDecompressionTrackingFlags decompressionTrackingFlags, |
| CVPixelBufferRef pixelBuffer, |
| TimeValue64 displayTime, |
| TimeValue64 displayDuration, |
| ICMValidTimeFlags validTimeFlags, void *reserved, void *sourceFrameRefCon) |
| { |
| QTWrapperVideoDecoder *qtwrapper; |
| GstBuffer *origbuf = (GstBuffer *) sourceFrameRefCon; |
| |
| qtwrapper = (QTWrapperVideoDecoder *) decompressionTrackingRefCon; |
| |
| GST_LOG_OBJECT (qtwrapper, |
| "result:%d, flags:0x%x, pixelBuffer:%p, displayTime:%lld, displayDuration:%lld", |
| (guint32) result, (guint32) decompressionTrackingFlags, pixelBuffer, |
| displayTime, displayDuration); |
| |
| GST_LOG_OBJECT (qtwrapper, |
| "validTimeFlags:0x%x, reserved:%p, sourceFrameRefCon:%p", |
| (guint32) validTimeFlags, reserved, sourceFrameRefCon); |
| |
| if (decompressionTrackingFlags & kICMDecompressionTracking_ReleaseSourceData) { |
| GST_LOG ("removing previous buffer : %p", origbuf); |
| gst_buffer_unref (origbuf); |
| } |
| |
| if (decompressionTrackingFlags & kICMDecompressionTracking_EmittingFrame) |
| GST_LOG ("EMITTING FRAME"); |
| if (decompressionTrackingFlags & kICMDecompressionTracking_FrameDecoded) |
| GST_LOG ("FRAME DECODED"); |
| if (decompressionTrackingFlags & kICMDecompressionTracking_FrameDropped) |
| GST_LOG ("FRAME DROPPED"); |
| if (decompressionTrackingFlags & |
| kICMDecompressionTracking_FrameNeedsRequeueing) |
| GST_LOG ("FRAME NEEDS REQUEUING"); |
| |
| if ((decompressionTrackingFlags & kICMDecompressionTracking_EmittingFrame) |
| && pixelBuffer) { |
| guint8 *addr; |
| GstBuffer *outbuf; |
| size_t size; |
| GstClockTime outtime; |
| |
| size = CVPixelBufferGetDataSize (pixelBuffer); |
| outtime = gst_util_uint64_scale (displayTime, GST_SECOND, 600); |
| |
| GST_LOG ("Got a buffer ready outtime : %" GST_TIME_FORMAT, |
| GST_TIME_ARGS (outtime)); |
| |
| if (qtwrapper->flushing) { |
| CVPixelBufferRelease (pixelBuffer); |
| goto beach; |
| } |
| |
| dump_cvpixel_buffer (pixelBuffer); |
| |
| CVPixelBufferRetain (pixelBuffer); |
| if (CVPixelBufferLockBaseAddress (pixelBuffer, 0)) |
| GST_WARNING ("Couldn't lock base adress on pixel buffer !"); |
| addr = CVPixelBufferGetBaseAddress (pixelBuffer); |
| |
| /* allocate buffer */ |
| qtwrapper->lastret = |
| gst_pad_alloc_buffer (qtwrapper->srcpad, GST_BUFFER_OFFSET_NONE, |
| (gint) qtwrapper->outsize, GST_PAD_CAPS (qtwrapper->srcpad), &outbuf); |
| if (G_UNLIKELY (qtwrapper->lastret != GST_FLOW_OK)) { |
| GST_LOG ("gst_pad_alloc_buffer() returned %s", |
| gst_flow_get_name (qtwrapper->lastret)); |
| goto beach; |
| } |
| |
| /* copy data */ |
| GST_LOG ("copying data in buffer from %p to %p", |
| addr, GST_BUFFER_DATA (outbuf)); |
| if (G_UNLIKELY ((qtwrapper->width * 2) != |
| CVPixelBufferGetBytesPerRow (pixelBuffer))) { |
| guint i; |
| gulong realpixels; |
| size_t stride; |
| |
| stride = CVPixelBufferGetBytesPerRow (pixelBuffer); |
| realpixels = qtwrapper->width * 2; |
| |
| /* special copy for stride handling */ |
| for (i = 0; i < qtwrapper->height; i++) |
| g_memmove (GST_BUFFER_DATA (outbuf) + realpixels * i, |
| addr + stride * i, realpixels); |
| |
| } else |
| g_memmove (GST_BUFFER_DATA (outbuf), addr, (int) qtwrapper->outsize); |
| |
| /* Release CVPixelBuffer */ |
| CVPixelBufferUnlockBaseAddress (pixelBuffer, 0); |
| CVPixelBufferRelease (pixelBuffer); |
| |
| /* Set proper timestamp ! */ |
| gst_buffer_set_caps (outbuf, GST_PAD_CAPS (qtwrapper->srcpad)); |
| GST_BUFFER_TIMESTAMP (outbuf) = qtwrapper->last_ts; |
| GST_BUFFER_DURATION (outbuf) = qtwrapper->last_duration; |
| GST_BUFFER_SIZE (outbuf) = (int) qtwrapper->outsize; |
| |
| /* See if we push buffer downstream */ |
| if (G_LIKELY (!qtwrapper->framebuffering)) { |
| GST_LOG ("No buffering needed, pushing buffer downstream"); |
| MAC_UNLOCK (qtwrapper); |
| qtwrapper->lastret = gst_pad_push (qtwrapper->srcpad, outbuf); |
| MAC_LOCK (qtwrapper); |
| } else { |
| /* Check if we push the current buffer or the stored buffer */ |
| if (!qtwrapper->prevbuf) { |
| GST_LOG ("Storing buffer"); |
| qtwrapper->prevbuf = outbuf; |
| qtwrapper->lastret = GST_FLOW_OK; |
| } else if (GST_BUFFER_TIMESTAMP (qtwrapper->prevbuf) > |
| GST_BUFFER_TIMESTAMP (outbuf)) { |
| GST_LOG ("Newly decoded buffer is earliest, pushing that one !"); |
| MAC_UNLOCK (qtwrapper); |
| qtwrapper->lastret = gst_pad_push (qtwrapper->srcpad, outbuf); |
| MAC_LOCK (qtwrapper); |
| } else { |
| GstBuffer *tmp; |
| |
| tmp = qtwrapper->prevbuf; |
| qtwrapper->prevbuf = outbuf; |
| GST_LOG ("Stored buffer is earliest, pushing that one !"); |
| MAC_UNLOCK (qtwrapper); |
| qtwrapper->lastret = gst_pad_push (qtwrapper->srcpad, tmp); |
| MAC_LOCK (qtwrapper); |
| } |
| } |
| } else { |
| qtwrapper->lastret = GST_FLOW_OK; |
| } |
| |
| beach: |
| return; |
| } |
| |
| /* _chain |
| * |
| * Here we feed the data to the decoder and ask to decode frames. |
| * |
| * Known issues/questions are: |
| * * How can we be guaranteed that one frame in automatically gives one output |
| * frame ? |
| * * PTS/DTS timestamp issues. With mpeg-derivate formats, the incoming order is |
| * different from the output order. |
| */ |
| |
| static GstFlowReturn |
| qtwrapper_video_decoder_chain (GstPad * pad, GstBuffer * buf) |
| { |
| QTWrapperVideoDecoder *qtwrapper; |
| GstFlowReturn ret = GST_FLOW_OK; |
| ICMFrameTimeRecord frameTime = { {0} }; |
| OSStatus status; |
| guint64 intime; |
| |
| qtwrapper = (QTWrapperVideoDecoder *) gst_pad_get_parent (pad); |
| |
| intime = gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 600, GST_SECOND); |
| |
| GST_DEBUG_OBJECT (qtwrapper, |
| "buffer:%p timestamp:%" GST_TIME_FORMAT " intime:%llu Size:%d", buf, |
| GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), intime, |
| GST_BUFFER_SIZE (buf)); |
| |
| frameTime.recordSize = sizeof (ICMFrameTimeRecord); |
| /* *(TimeValue64 *)&frameTime.value = intime; */ |
| frameTime.value.lo = (guint32) (intime & 0xffffffff); |
| frameTime.value.hi = (guint32) (intime >> 32); |
| frameTime.base = 0; |
| frameTime.scale = 600; |
| frameTime.rate = fixed1; |
| frameTime.duration = 1; |
| frameTime.flags = icmFrameTimeDecodeImmediately; |
| /* frameTime.flags = icmFrameTimeIsNonScheduledDisplayTime; */ |
| frameTime.frameNumber = (long) (++qtwrapper->frameNumber); |
| |
| MAC_LOCK (qtwrapper); |
| |
| qtwrapper->last_ts = GST_BUFFER_TIMESTAMP (buf); |
| qtwrapper->last_duration = GST_BUFFER_DURATION (buf); |
| |
| status = ICMDecompressionSessionDecodeFrame (qtwrapper->decsession, |
| GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf), NULL, &frameTime, buf); |
| MAC_UNLOCK (qtwrapper); |
| |
| if (status) { |
| GST_WARNING_OBJECT (qtwrapper, "Error when Calling DecodeFrame() : %ld", |
| status); |
| ret = GST_FLOW_ERROR; |
| goto beach; |
| } |
| |
| beach: |
| gst_object_unref (qtwrapper); |
| return qtwrapper->lastret; |
| } |
| |
| static gboolean |
| qtwrapper_video_decoder_sink_event (GstPad * pad, GstEvent * event) |
| { |
| gboolean res; |
| QTWrapperVideoDecoder *qtwrapper; |
| |
| qtwrapper = (QTWrapperVideoDecoder *) gst_pad_get_parent (pad); |
| |
| GST_LOG_OBJECT (pad, "event : %s", GST_EVENT_TYPE_NAME (event)); |
| |
| switch (GST_EVENT_TYPE (event)) { |
| case GST_EVENT_FLUSH_START: |
| MAC_LOCK (qtwrapper); |
| qtwrapper->flushing = TRUE; |
| if (qtwrapper->prevbuf) { |
| GST_LOG ("About to unref buffer %p", qtwrapper->prevbuf); |
| gst_buffer_unref (qtwrapper->prevbuf); |
| qtwrapper->prevbuf = NULL; |
| } |
| ICMDecompressionSessionFlush (qtwrapper->decsession); |
| MAC_UNLOCK (qtwrapper); |
| break; |
| case GST_EVENT_FLUSH_STOP: |
| MAC_LOCK (qtwrapper); |
| qtwrapper->flushing = FALSE; |
| qtwrapper->prevbuf = NULL; |
| MAC_UNLOCK (qtwrapper); |
| break; |
| default: |
| break; |
| } |
| |
| res = gst_pad_push_event (qtwrapper->srcpad, event); |
| |
| gst_object_unref (qtwrapper); |
| return res; |
| } |
| |
| /* _register |
| * |
| * Scan through all available Image Decompressor components to find the ones we |
| * can handle and wrap in this plugin. |
| */ |
| |
| gboolean |
| qtwrapper_video_decoders_register (GstPlugin * plugin) |
| { |
| gboolean res = TRUE; |
| Component componentID = NULL; |
| ComponentDescription desc = { |
| 'imdc', 0, 0, 0, 0 |
| }; |
| GTypeInfo typeinfo = { |
| sizeof (QTWrapperVideoDecoderClass), |
| (GBaseInitFunc) qtwrapper_video_decoder_base_init, |
| NULL, |
| (GClassInitFunc) qtwrapper_video_decoder_class_init, |
| NULL, |
| NULL, |
| sizeof (QTWrapperVideoDecoder), |
| 0, |
| (GInstanceInitFunc) qtwrapper_video_decoder_init, |
| }; |
| |
| /* Find all ImageDecoders ! */ |
| GST_DEBUG ("There are %ld decompressors available", CountComponents (&desc)); |
| |
| /* loop over ImageDecoders */ |
| do { |
| componentID = FindNextComponent (componentID, &desc); |
| |
| GST_LOG ("componentID : %p", componentID); |
| |
| if (componentID) { |
| ComponentDescription thisdesc; |
| gchar *name = NULL, *info = NULL; |
| GstCaps *caps = NULL; |
| gchar *type_name = NULL; |
| GType type; |
| QTWrapperVideoDecoderParams *params = NULL; |
| |
| if (!(get_name_info_from_component (componentID, &thisdesc, &name, |
| &info))) |
| goto next; |
| |
| if (!get_output_info_from_component (componentID)) { |
| GST_WARNING ("Couldn't get output info from component"); |
| goto next; |
| } |
| |
| GST_LOG (" name:%s", GST_STR_NULL (name)); |
| GST_LOG (" info:%s", GST_STR_NULL (info)); |
| |
| GST_LOG (" type:%" GST_FOURCC_FORMAT, |
| QT_FOURCC_ARGS (thisdesc.componentType)); |
| GST_LOG (" subtype:%" GST_FOURCC_FORMAT, |
| QT_FOURCC_ARGS (thisdesc.componentSubType)); |
| GST_LOG (" manufacturer:%" GST_FOURCC_FORMAT, |
| QT_FOURCC_ARGS (thisdesc.componentManufacturer)); |
| |
| if (!(caps = fourcc_to_caps (thisdesc.componentSubType))) { |
| GST_LOG |
| ("We can't find caps for this component, switching to the next one !"); |
| goto next; |
| } |
| |
| type_name = g_strdup_printf ("qtwrappervideodec_%" GST_FOURCC_FORMAT, |
| QT_FOURCC_ARGS (thisdesc.componentSubType)); |
| g_strdelimit (type_name, " ", '_'); |
| |
| if (g_type_from_name (type_name)) { |
| GST_WARNING ("We already have a registered plugin for %s", type_name); |
| goto next; |
| } |
| |
| params = g_new0 (QTWrapperVideoDecoderParams, 1); |
| params->component = componentID; |
| params->sinkcaps = gst_caps_ref (caps); |
| |
| GST_INFO ("Registering g_type for type_name: %s", type_name); |
| type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0); |
| /* Store params in type qdata */ |
| g_type_set_qdata (type, QTWRAPPER_VDEC_PARAMS_QDATA, (gpointer) params); |
| |
| /* register type */ |
| if (!gst_element_register (plugin, type_name, GST_RANK_MARGINAL, type)) { |
| g_warning ("Failed to register %s", type_name);; |
| g_type_set_qdata (type, QTWRAPPER_VDEC_PARAMS_QDATA, NULL); |
| g_free (params); |
| res = FALSE; |
| goto next; |
| } else { |
| GST_LOG ("Reigstered video plugin %s", type_name); |
| } |
| |
| next: |
| if (name) |
| g_free (name); |
| if (info) |
| g_free (info); |
| if (type_name) |
| g_free (type_name); |
| if (caps) |
| gst_caps_unref (caps); |
| } |
| |
| } while (componentID && res); |
| |
| return res; |
| } |