From 307925e3077da26e33b06a6c53080ebf07328847 Mon Sep 17 00:00:00 2001 From: Julien Moutte Date: Mon, 26 Nov 2007 13:19:48 +0000 Subject: configure.ac: Add QuickTime Wrapper plug-in. Original commit message from CVS: 2007-11-26 Julien Moutte * configure.ac: Add QuickTime Wrapper plug-in. * gst/speexresample/gstspeexresample.c: (gst_speex_resample_push_drain), (gst_speex_resample_process): Fix build on Mac OS X Leopard. Incorrect printf format arguments. * sys/Makefile.am: * sys/qtwrapper/Makefile.am: * sys/qtwrapper/audiodecoders.c: (qtwrapper_audio_decoder_base_init), (qtwrapper_audio_decoder_class_init), (qtwrapper_audio_decoder_init), (clear_AudioStreamBasicDescription), (fill_indesc_mp3), (fill_indesc_aac), (fill_indesc_samr), (fill_indesc_generic), (make_samr_magic_cookie), (open_decoder), (qtwrapper_audio_decoder_sink_setcaps), (process_buffer_cb), (qtwrapper_audio_decoder_chain), (qtwrapper_audio_decoder_sink_event), (qtwrapper_audio_decoders_register): * sys/qtwrapper/codecmapping.c: (audio_caps_from_string), (fourcc_to_caps): * sys/qtwrapper/codecmapping.h: * sys/qtwrapper/imagedescription.c: (image_description_for_avc1), (image_description_for_mp4v), (image_description_from_stsd_buffer), (image_description_from_codec_data): * sys/qtwrapper/imagedescription.h: * sys/qtwrapper/qtutils.c: (get_name_info_from_component), (get_output_info_from_component), (dump_avcc_atom), (dump_image_description), (dump_codec_decompress_params), (addSInt32ToDictionary), (dump_cvpixel_buffer), (DestroyAudioBufferList), (AllocateAudioBufferList): * sys/qtwrapper/qtutils.h: * sys/qtwrapper/qtwrapper.c: (plugin_init): * sys/qtwrapper/qtwrapper.h: * sys/qtwrapper/videodecoders.c: (qtwrapper_video_decoder_base_init), (qtwrapper_video_decoder_class_init), (qtwrapper_video_decoder_init), (qtwrapper_video_decoder_finalize), (fill_image_description), (new_image_description), (close_decoder), (open_decoder), (qtwrapper_video_decoder_sink_setcaps), (decompressCb), (qtwrapper_video_decoder_chain), (qtwrapper_video_decoder_sink_event), (qtwrapper_video_decoders_register): Initial import of QuickTime wrapper jointly developped by Songbird authors (Pioneers of the Inevitable) and Fluendo. --- sys/qtwrapper/videodecoders.c | 874 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 874 insertions(+) create mode 100644 sys/qtwrapper/videodecoders.c (limited to 'sys/qtwrapper/videodecoders.c') diff --git a/sys/qtwrapper/videodecoders.c b/sys/qtwrapper/videodecoders.c new file mode 100644 index 00000000..d0b54454 --- /dev/null +++ b/sys/qtwrapper/videodecoders.c @@ -0,0 +1,874 @@ +/* + * 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) +{ + GstElementDetails details; + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + gchar *name = NULL; + gchar *info = NULL; + 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 */ + details.longname = g_strdup_printf ("QTWrapper Video Decoder : %s", name); + details.klass = "Codec/Decoder/Video"; + details.description = info; + details.author = "Fluendo , " + "Pioneers of the Inevitable "; + gst_element_class_set_details (element_class, &details); + + g_free (details.longname); + 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; + OSErr oserr; + 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 */ + oserr = ICMDecompressionSessionCreate (NULL, + qtwrapper->idesc, + sessionoptions, pixelBufferAttributes, &cbrecord, &qtwrapper->decsession); + + qtwrapper->outsize = width * height * 2; + qtwrapper->width = width; + qtwrapper->height = height; + + if (oserr != noErr) { + GST_DEBUG_OBJECT (qtwrapper, + "Error when Calling ICMDecompressionSessionCreate : %d", oserr); + 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) { + gpointer addr; + GstBuffer *outbuf; + gsize size; + + size = CVPixelBufferGetDataSize (pixelBuffer); + GstClockTime 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, + 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 stride, realpixels; + + 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, 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) = 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} }; + OSErr oserr; + 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 = intime; + frameTime.value.hi = 0; + frameTime.base = 0; + frameTime.scale = 600; + frameTime.rate = fixed1; + frameTime.duration = 1; + frameTime.flags = icmFrameTimeDecodeImmediately; +/* frameTime.flags = icmFrameTimeIsNonScheduledDisplayTime; */ + frameTime.frameNumber = ++qtwrapper->frameNumber; + + MAC_LOCK (qtwrapper); + + qtwrapper->last_ts = GST_BUFFER_TIMESTAMP (buf); + qtwrapper->last_duration = GST_BUFFER_DURATION (buf); + + oserr = ICMDecompressionSessionDecodeFrame (qtwrapper->decsession, + GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf), NULL, &frameTime, buf); + MAC_UNLOCK (qtwrapper); + + if (oserr != noErr) { + GST_WARNING_OBJECT (qtwrapper, "Error when Calling DecodeFrame() : %d", + oserr); + 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; + OSErr result; + 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, + }; + + /* Initialize quicktime environment */ + result = EnterMovies (); + if (result != noErr) { + GST_ERROR ("Error initializing QuickTime environment"); + res = FALSE; + goto beach; + } + + /* 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", name); + GST_LOG (" info:%s", 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); + + 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; + } + + 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); + +beach: + return res; +} -- cgit v1.2.1