/* * GStreamer QuickTime video decoder codecs wrapper * Copyright <2006, 2007> Fluendo * Copyright <2006, 2007> Pioneers of the Inevitable * * 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 #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_details_simple (element_class, longname, "Codec/Decoder/Video", description, "Fluendo , " "Pioneers of the Inevitable "); 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_static_pad_template (element_class, &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; }