diff options
-rw-r--r-- | ChangeLog | 46 | ||||
-rw-r--r-- | configure.ac | 8 | ||||
-rw-r--r-- | gst/speexresample/gstspeexresample.c | 10 | ||||
-rw-r--r-- | sys/Makefile.am | 10 | ||||
-rw-r--r-- | sys/qtwrapper/Makefile.am | 24 | ||||
-rw-r--r-- | sys/qtwrapper/audiodecoders.c | 799 | ||||
-rw-r--r-- | sys/qtwrapper/codecmapping.c | 151 | ||||
-rw-r--r-- | sys/qtwrapper/codecmapping.h | 55 | ||||
-rw-r--r-- | sys/qtwrapper/imagedescription.c | 211 | ||||
-rw-r--r-- | sys/qtwrapper/imagedescription.h | 54 | ||||
-rw-r--r-- | sys/qtwrapper/qtutils.c | 477 | ||||
-rw-r--r-- | sys/qtwrapper/qtutils.h | 131 | ||||
-rw-r--r-- | sys/qtwrapper/qtwrapper.c | 71 | ||||
-rw-r--r-- | sys/qtwrapper/qtwrapper.h | 63 | ||||
-rw-r--r-- | sys/qtwrapper/videodecoders.c | 874 |
15 files changed, 2978 insertions, 6 deletions
@@ -1,3 +1,49 @@ +2007-11-26 Julien Moutte <julien@fluendo.com> + + * 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. + 2007-11-26 Stefan Kost <ensonic@users.sf.net> * gst/spectrum/gstspectrum.c: diff --git a/configure.ac b/configure.ac index dcf4b35d..82b17365 100644 --- a/configure.ac +++ b/configure.ac @@ -298,6 +298,12 @@ AG_GST_CHECK_FEATURE(OPENGL, [Open GL], glsink, [ CPPFLAGS="$save_CPPFLAGS" LIBS="$save_LIBS" +dnl check for QuickTime +translit(dnm, m, l) AM_CONDITIONAL(USE_QUICKTIME, true) +AG_GST_CHECK_FEATURE(QUICKTIME, [QuickTime wrapper], qtwrapper, [ + AC_CHECK_HEADER(QuickTime/Movies.h, HAVE_QUICKTIME="yes", HAVE_QUICKTIME="no") +]) + dnl check for Video CD translit(dnm, m, l) AM_CONDITIONAL(USE_VCD, true) AG_GST_CHECK_FEATURE(VCD, [Video CD], vcdsrc, [ @@ -955,6 +961,7 @@ dnl not building plugins with external dependencies, dnl but we still need to set the conditionals AM_CONDITIONAL(USE_OPENGL, false) +AM_CONDITIONAL(USE_QUICKTIME, false) AM_CONDITIONAL(USE_VCD, false) AM_CONDITIONAL(USE_X, false) AM_CONDITIONAL(USE_ALSA, false) @@ -1086,6 +1093,7 @@ gst-libs/gst/app/Makefile sys/Makefile sys/glsink/Makefile sys/dvb/Makefile +sys/qtwrapper/Makefile sys/vcd/Makefile examples/Makefile examples/app/Makefile diff --git a/gst/speexresample/gstspeexresample.c b/gst/speexresample/gstspeexresample.c index e6765a0f..77fb8e26 100644 --- a/gst/speexresample/gstspeexresample.c +++ b/gst/speexresample/gstspeexresample.c @@ -594,8 +594,9 @@ gst_speex_resample_push_drain (GstSpeexResample * resample) GST_FRAMES_TO_CLOCK_TIME (out_processed, resample->outrate); } - GST_LOG ("Pushing drain buffer of %ld bytes with timestamp %" GST_TIME_FORMAT - " duration %" GST_TIME_FORMAT " offset %lld offset_end %lld", + GST_LOG ("Pushing drain buffer of %u bytes with timestamp %" GST_TIME_FORMAT + " duration %" GST_TIME_FORMAT " offset %" G_GUINT64_FORMAT + " offset_end %" G_GUINT64_FORMAT, GST_BUFFER_SIZE (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), @@ -752,8 +753,9 @@ gst_speex_resample_process (GstSpeexResample * resample, GstBuffer * inbuf, GST_ERROR ("Failed to convert data: %s", resample_resampler_strerror (err)); return GST_FLOW_ERROR; } else { - GST_LOG ("Converted to buffer of %ld bytes with timestamp %" GST_TIME_FORMAT - ", duration %" GST_TIME_FORMAT ", offset %lld, offset_end %lld", + GST_LOG ("Converted to buffer of %u bytes with timestamp %" GST_TIME_FORMAT + ", duration %" GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT + ", offset_end %" G_GUINT64_FORMAT, GST_BUFFER_SIZE (outbuf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), diff --git a/sys/Makefile.am b/sys/Makefile.am index 106c729c..564771eb 100644 --- a/sys/Makefile.am +++ b/sys/Makefile.am @@ -34,6 +34,12 @@ else DVB_DIR= endif -SUBDIRS = $(GL_DIR) $(DVB_DIR) $(VCD_DIR) +if USE_QUICKTIME +QT_DIR=qtwrapper +else +QT_DIR= +endif + +SUBDIRS = $(GL_DIR) $(DVB_DIR) $(VCD_DIR) $(QT_DIR) -DIST_SUBDIRS = glsink dvb vcd +DIST_SUBDIRS = glsink dvb vcd qtwrapper diff --git a/sys/qtwrapper/Makefile.am b/sys/qtwrapper/Makefile.am new file mode 100644 index 00000000..db086477 --- /dev/null +++ b/sys/qtwrapper/Makefile.am @@ -0,0 +1,24 @@ +plugin_LTLIBRARIES = libgstqtwrapper.la + +# sources used to compile this plug-in +libgstqtwrapper_la_SOURCES = \ + qtwrapper.c \ + qtutils.c \ + codecmapping.c \ + audiodecoders.c \ + videodecoders.c \ + imagedescription.c + +# flags used to compile this plugin +# add other _CFLAGS and _LIBS as needed +libgstqtwrapper_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) +libgstqtwrapper_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) +libgstqtwrapper_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -Wl,-framework,CoreAudio,-framework,AudioToolbox,-framework,Carbon,-framework,QuickTime,-framework,QuartzCore + + +# headers we need but don't want installed +noinst_HEADERS = \ + codecmapping.h \ + qtwrapper.h \ + qtutils.h \ + imagedescription.h diff --git a/sys/qtwrapper/audiodecoders.c b/sys/qtwrapper/audiodecoders.c new file mode 100644 index 00000000..2efd5ae8 --- /dev/null +++ b/sys/qtwrapper/audiodecoders.c @@ -0,0 +1,799 @@ +/* + * GStreamer QuickTime audio 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 <QuickTime/Movies.h> +#include <AudioToolbox/AudioToolbox.h> + +#include <gst/base/gstadapter.h> +#include "qtwrapper.h" +#include "codecmapping.h" +#include "qtutils.h" + +#define QTWRAPPER_ADEC_PARAMS_QDATA g_quark_from_static_string("qtwrapper-adec-params") + +static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-float, " + "endianness = (int) {" G_STRINGIFY (G_BYTE_ORDER) " }, " + "signed = (boolean) { TRUE }, " + "width = (int) 32, " + "depth = (int) 32, " "rate = (int) 44100, " "channels = (int) 2") + ); + +typedef struct _QTWrapperAudioDecoder QTWrapperAudioDecoder; +typedef struct _QTWrapperAudioDecoderClass QTWrapperAudioDecoderClass; + +struct _QTWrapperAudioDecoder +{ + GstElement parent; + + GstPad *sinkpad; + GstPad *srcpad; + + /* FIXME : all following should be protected by a mutex */ + AudioConverterRef aconv; + AudioStreamBasicDescription indesc, outdesc; + + guint samplerate; + guint channels; + AudioBufferList *bufferlist; + + /* first time received after NEWSEGMENT */ + GstClockTime initial_time; + /* offset in samples from the initial time */ + guint64 cur_offset; + /* TRUE just after receiving a NEWSEGMENT */ + gboolean gotnewsegment; + + /* temporary output data */ + gpointer tmpdata; + + /* buffer previously used by the decoder */ + gpointer prevdata; + + GstAdapter *adapter; +}; + +struct _QTWrapperAudioDecoderClass +{ + GstElementClass parent_class; + + /* fourcc of the format */ + guint32 componentSubType; + + GstPadTemplate *sinktempl; +}; + +typedef struct _QTWrapperAudioDecoderParams QTWrapperAudioDecoderParams; + +struct _QTWrapperAudioDecoderParams +{ + Component component; + GstCaps *sinkcaps; +}; + +static gboolean qtwrapper_audio_decoder_sink_setcaps (GstPad * pad, + GstCaps * caps); +static GstFlowReturn qtwrapper_audio_decoder_chain (GstPad * pad, + GstBuffer * buf); +static gboolean qtwrapper_audio_decoder_sink_event (GstPad * pad, + GstEvent * event); + +static void +qtwrapper_audio_decoder_base_init (QTWrapperAudioDecoderClass * klass) +{ + GstElementDetails details; + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + gchar *name = NULL; + gchar *info = NULL; + ComponentDescription desc; + QTWrapperAudioDecoderParams *params; + + params = (QTWrapperAudioDecoderParams *) + g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass), + QTWRAPPER_ADEC_PARAMS_QDATA); + g_assert (params); + + get_name_info_from_component (params->component, &desc, &name, &info); + + /* Fill in details */ + details.longname = g_strdup_printf ("QTWrapper Audio Decoder : %s", name); + details.klass = "Codec/Decoder/Audio"; + details.description = info; + details.author = "Fluendo <gstreamer@fluendo.com>, " + "Pioneers of the Inevitable <songbird@songbirdnest.com>"; + gst_element_class_set_details (element_class, &details); + + g_free (details.longname); + g_free (name); + g_free (info); + + /* Add pad templates */ + 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->componentSubType = desc.componentSubType; +} + +static void +qtwrapper_audio_decoder_class_init (QTWrapperAudioDecoderClass * klass) +{ + /* FIXME : don't we need some vmethod implementations here ?? */ +} + +static void +qtwrapper_audio_decoder_init (QTWrapperAudioDecoder * qtwrapper) +{ + QTWrapperAudioDecoderClass *oclass; + + oclass = (QTWrapperAudioDecoderClass *) (G_OBJECT_GET_CLASS (qtwrapper)); + + /* Sink pad */ + qtwrapper->sinkpad = gst_pad_new_from_template (oclass->sinktempl, "sink"); + gst_pad_set_setcaps_function (qtwrapper->sinkpad, + GST_DEBUG_FUNCPTR (qtwrapper_audio_decoder_sink_setcaps)); + gst_pad_set_chain_function (qtwrapper->sinkpad, + GST_DEBUG_FUNCPTR (qtwrapper_audio_decoder_chain)); + gst_pad_set_event_function (qtwrapper->sinkpad, + GST_DEBUG_FUNCPTR (qtwrapper_audio_decoder_sink_event)); + gst_element_add_pad (GST_ELEMENT (qtwrapper), qtwrapper->sinkpad); + + /* Source pad */ + qtwrapper->srcpad = gst_pad_new_from_static_template (&src_templ, "src"); + gst_element_add_pad (GST_ELEMENT (qtwrapper), qtwrapper->srcpad); + + qtwrapper->adapter = gst_adapter_new (); +} + +static void +clear_AudioStreamBasicDescription (AudioStreamBasicDescription * desc) +{ + desc->mSampleRate = 0; + desc->mFormatID = 0; + desc->mFormatFlags = 0; + desc->mBytesPerPacket = 0; + desc->mFramesPerPacket = 0; + desc->mBytesPerFrame = 0; + desc->mChannelsPerFrame = 0; + desc->mBitsPerChannel = 0; + +} + +static void +fill_indesc_mp3 (QTWrapperAudioDecoder * qtwrapper, guint32 fourcc, gint rate, + gint channels) +{ + GST_LOG ("..."); + clear_AudioStreamBasicDescription (&qtwrapper->indesc); + /* only the samplerate is needed apparently */ + qtwrapper->indesc.mSampleRate = rate; + qtwrapper->indesc.mFormatID = kAudioFormatMPEGLayer3; + qtwrapper->indesc.mChannelsPerFrame = channels; +} + +static void +fill_indesc_aac (QTWrapperAudioDecoder * qtwrapper, guint32 fourcc, gint rate, + gint channels) +{ + clear_AudioStreamBasicDescription (&qtwrapper->indesc); + qtwrapper->indesc.mSampleRate = rate; + qtwrapper->indesc.mFormatID = kAudioFormatMPEG4AAC; + /* aac always has 1024 bytes per packet */ + qtwrapper->indesc.mBytesPerPacket = 1024; + qtwrapper->indesc.mChannelsPerFrame = channels; +} + +static void +fill_indesc_samr (QTWrapperAudioDecoder * qtwrapper, guint32 fourcc, + gint channels) +{ + clear_AudioStreamBasicDescription (&qtwrapper->indesc); + qtwrapper->indesc.mSampleRate = 8000; + qtwrapper->indesc.mFormatID = fourcc; + qtwrapper->indesc.mChannelsPerFrame = 1; + qtwrapper->indesc.mFramesPerPacket = 160; +} + +static void +fill_indesc_generic (QTWrapperAudioDecoder * qtwrapper, guint32 fourcc, + gint rate, gint channels) +{ + clear_AudioStreamBasicDescription (&qtwrapper->indesc); + qtwrapper->indesc.mSampleRate = rate; + qtwrapper->indesc.mFormatID = fourcc; + qtwrapper->indesc.mChannelsPerFrame = channels; +} + +static gpointer +make_samr_magic_cookie (GstBuffer * codec_data, gsize * len) +{ + gpointer res; + + *len = 48; + res = g_malloc0 (0x30); + + /* 12 first bytes are 'frma' (format) atom with 'samr' value */ + GST_WRITE_UINT32_BE (res, 0xc); + GST_WRITE_UINT32_LE (res + 4, QT_MAKE_FOURCC_BE ('f', 'r', 'm', 'a')); + GST_WRITE_UINT32_LE (res + 8, QT_MAKE_FOURCC_BE ('s', 'a', 'm', 'r')); + + /* 10 bytes for 'enda' atom with 0 */ + GST_WRITE_UINT32_BE (res + 12, 10); + GST_WRITE_UINT32_LE (res + 16, QT_MAKE_FOURCC_BE ('e', 'n', 'd', 'a')); + + /* 17(+1) bytes for the codec_data contents */ + GST_WRITE_UINT32_BE (res + 22, 18); + memcpy (res + 26, GST_BUFFER_DATA (codec_data) + 4, 17); + + /* yes... we need to replace 'damr' by 'samr'. Blame Apple ! */ + GST_WRITE_UINT8 (res + 26, 's'); + + /* padding 8 bytes */ + GST_WRITE_UINT32_BE (res + 40, 8); + +#if DEBUG_DUMP + gst_util_dump_mem (res, 48); +#endif + + return res; +} + +static gboolean +open_decoder (QTWrapperAudioDecoder * qtwrapper, GstCaps * caps, + GstCaps ** othercaps) +{ + gboolean ret = FALSE; + QTWrapperAudioDecoderClass *oclass; + gint channels = 2; + gint rate = 44100; + gint depth = 32; + OSErr oserr; + OSStatus status; + GstStructure *s; + gchar *tmp; + const GValue *value; + GstBuffer *codec_data = NULL; + + tmp = gst_caps_to_string (caps); + GST_LOG_OBJECT (qtwrapper, "caps: %s", tmp); + g_free (tmp); + + /* extract rate/channels information from the caps */ + s = gst_caps_get_structure (caps, 0); + gst_structure_get_int (s, "rate", &rate); + gst_structure_get_int (s, "channels", &channels); + + /* depth isn't compulsory */ + if (!(gst_structure_get_int (s, "depth", &depth))) + gst_structure_get_int (s, "samplesize", &depth); + + /* get codec_data */ + if ((value = gst_structure_get_value (s, "codec_data"))) { + codec_data = GST_BUFFER_CAST (gst_value_get_mini_object (value)); + } + + /* If the quicktime demuxer gives us a full esds atom, use that instead of the codec_data */ + if ((value = gst_structure_get_value (s, "quicktime_esds"))) { + codec_data = GST_BUFFER_CAST (gst_value_get_mini_object (value)); + } +#if DEBUG_DUMP + if (codec_data) + gst_util_dump_mem (GST_BUFFER_DATA (codec_data), + GST_BUFFER_SIZE (codec_data)); +#endif + + + GST_LOG ("rate:%d, channels:%d, depth:%d", rate, channels, depth); + + oclass = (QTWrapperAudioDecoderClass *) (G_OBJECT_GET_CLASS (qtwrapper)); + + /* Setup the input format description, some format require special handling */ + switch (oclass->componentSubType) { + case QT_MAKE_FOURCC_LE ('.', 'm', 'p', '3'): + fill_indesc_mp3 (qtwrapper, oclass->componentSubType, rate, channels); + break; + case QT_MAKE_FOURCC_LE ('m', 'p', '4', 'a'): + fill_indesc_aac (qtwrapper, oclass->componentSubType, rate, channels); + break; + case QT_MAKE_FOURCC_LE ('s', 'a', 'm', 'r'): + fill_indesc_samr (qtwrapper, oclass->componentSubType, channels); + rate = 8000; + break; + default: + fill_indesc_generic (qtwrapper, oclass->componentSubType, rate, channels); + break; + } + +#if DEBUG_DUMP + gst_util_dump_mem (&qtwrapper->indesc, sizeof (AudioStreamBasicDescription)); +#endif + + /* we're forcing output to stereo 44.1kHz */ + rate = 44100; + channels = 2; + + qtwrapper->samplerate = rate; + qtwrapper->channels = channels; + + /* Setup the output format description */ + qtwrapper->outdesc.mSampleRate = rate; + qtwrapper->outdesc.mFormatID = kAudioFormatLinearPCM; + qtwrapper->outdesc.mFormatFlags = kAudioFormatFlagIsFloat; +#if G_BYTE_ORDER == G_BIG_ENDIAN + qtwrapper->outdesc.mFormatFlags |= kAudioFormatFlagIsBigEndian; +#endif + qtwrapper->outdesc.mBytesPerPacket = channels * 4; /* ?? */ + qtwrapper->outdesc.mFramesPerPacket = 1; + qtwrapper->outdesc.mBytesPerFrame = channels * 4; /* channels * bytes-per-samples */ + qtwrapper->outdesc.mChannelsPerFrame = channels; + qtwrapper->outdesc.mBitsPerChannel = 32; + + /* Create an AudioConverter */ + status = AudioConverterNew (&qtwrapper->indesc, + &qtwrapper->outdesc, &qtwrapper->aconv); + if (status != noErr) { + GST_WARNING_OBJECT (qtwrapper, + "Error when calling AudioConverterNew() : %" GST_FOURCC_FORMAT, + QT_FOURCC_ARGS (status)); + goto beach; + } + + /* if we have codec_data, give it to the converter ! */ + if (codec_data) { + gsize len; + gpointer magiccookie; + + if (oclass->componentSubType == QT_MAKE_FOURCC_LE ('s', 'a', 'm', 'r')) { + magiccookie = make_samr_magic_cookie (codec_data, &len); + } else { + len = GST_BUFFER_SIZE (codec_data); + magiccookie = GST_BUFFER_DATA (codec_data); + } + GST_LOG_OBJECT (qtwrapper, "Setting magic cookie %p of size %" + G_GSIZE_FORMAT, magiccookie, len); + oserr = AudioConverterSetProperty (qtwrapper->aconv, + kAudioConverterDecompressionMagicCookie, len, magiccookie); + if (oserr != noErr) { + GST_WARNING_OBJECT (qtwrapper, "Error setting extra codec data !"); + goto beach; + } + } + + /* Create output bufferlist */ + qtwrapper->bufferlist = AllocateAudioBufferList (channels, + rate * channels * 4 / 20); + + /* Create output caps */ + *othercaps = gst_caps_new_simple ("audio/x-raw-float", + "endianness", G_TYPE_INT, G_BYTE_ORDER, + "signed", G_TYPE_BOOLEAN, TRUE, + "width", G_TYPE_INT, 32, + "depth", G_TYPE_INT, 32, + "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, channels, NULL); + + ret = TRUE; + +beach: + return ret; +} + +static gboolean +qtwrapper_audio_decoder_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + QTWrapperAudioDecoder *qtwrapper; + gboolean ret = FALSE; + GstCaps *othercaps = NULL; + + qtwrapper = (QTWrapperAudioDecoder *) gst_pad_get_parent (pad); + + GST_LOG_OBJECT (qtwrapper, "caps:%" GST_PTR_FORMAT, caps); + + /* 1. open decoder */ + if (!(open_decoder (qtwrapper, caps, &othercaps))) + goto beach; + + /* 2. set caps downstream */ + ret = gst_pad_set_caps (qtwrapper->srcpad, othercaps); + +beach: + if (othercaps) + gst_caps_unref (othercaps); + gst_object_unref (qtwrapper); + return ret; +} + +static OSStatus +process_buffer_cb (AudioConverterRef inAudioConverter, + UInt32 * ioNumberDataPackets, + AudioBufferList * ioData, + AudioStreamPacketDescription ** outDataPacketDescription, + QTWrapperAudioDecoder * qtwrapper) +{ + gint len; + AudioStreamPacketDescription aspd[200]; + + GST_LOG_OBJECT (qtwrapper, + "ioNumberDataPackets:%lu, iodata:%p, outDataPacketDescription:%p", + *ioNumberDataPackets, ioData, outDataPacketDescription); + if (outDataPacketDescription) + GST_LOG ("*outDataPacketDescription:%p", *outDataPacketDescription); + + GST_LOG ("mNumberBuffers : %u", (guint32) ioData->mNumberBuffers); + GST_LOG ("mData:%p , mDataByteSize:%u", + ioData->mBuffers[0].mData, (guint32) ioData->mBuffers[0].mDataByteSize); + + ioData->mBuffers[0].mData = NULL; + ioData->mBuffers[0].mDataByteSize = 0; + if (qtwrapper->prevdata) + g_free (qtwrapper->prevdata); + + len = gst_adapter_available (qtwrapper->adapter); + + if (len) { + ioData->mBuffers[0].mData = gst_adapter_take (qtwrapper->adapter, len); + qtwrapper->prevdata = ioData->mBuffers[0].mData; + + /* if we have a valid outDataPacketDescription, we need to fill it */ + if (outDataPacketDescription) { + /* mStartOffset : the number of bytes from the start of the buffer to the + * beginning of the packet. */ + aspd[0].mStartOffset = 0; + aspd[1].mStartOffset = 0; + /* mVariableFramesInPacket : the number of samples frames of data in the + * packet. For formats with a constant number of frames per packet, this + * field is set to 0. */ + aspd[0].mVariableFramesInPacket = 0; + aspd[1].mVariableFramesInPacket = 0; + /* mDataByteSize : The number of bytes in the packet. */ + aspd[0].mDataByteSize = len; + aspd[1].mDataByteSize = 0; + GST_LOG ("ASPD: mStartOffset:%lld, mVariableFramesInPacket:%u, " + "mDataByteSize:%u", aspd[0].mStartOffset, + (guint32) aspd[0].mVariableFramesInPacket, + (guint32) aspd[0].mDataByteSize); + *outDataPacketDescription = (AudioStreamPacketDescription *) & aspd; + } + + } else { + qtwrapper->prevdata = NULL; + } + + ioData->mBuffers[0].mDataByteSize = len; + + GST_LOG_OBJECT (qtwrapper, "returning %d bytes at %p", + len, ioData->mBuffers[0].mData); + + if (!len) + return 42; + return noErr; +} + +static GstFlowReturn +qtwrapper_audio_decoder_chain (GstPad * pad, GstBuffer * buf) +{ + GstFlowReturn ret = GST_FLOW_OK; + QTWrapperAudioDecoder *qtwrapper; + + qtwrapper = (QTWrapperAudioDecoder *) gst_pad_get_parent (pad); + + GST_LOG_OBJECT (qtwrapper, + "buffer:%p , timestamp:%" GST_TIME_FORMAT " ,size:%d", buf, + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), GST_BUFFER_SIZE (buf)); + +#if DEBUG_DUMP + gst_util_dump_mem (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); +#endif + + if (qtwrapper->gotnewsegment) { + + GST_DEBUG_OBJECT (qtwrapper, "AudioConverterReset()"); + + AudioConverterReset (qtwrapper->aconv); + + /* some formats can give us a better initial time using the buffer + * timestamp. */ + if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buf))) + qtwrapper->initial_time = GST_BUFFER_TIMESTAMP (buf); + + qtwrapper->gotnewsegment = FALSE; + } + + /* stack in adapter */ + gst_adapter_push (qtwrapper->adapter, buf); + + /* do we have enough to decode at least one frame ? */ + while (gst_adapter_available (qtwrapper->adapter)) { + GstBuffer *outbuf; + OSStatus status; + guint32 outsamples = qtwrapper->bufferlist->mBuffers[0].mDataByteSize / 8; + guint32 savedbytes = qtwrapper->bufferlist->mBuffers[0].mDataByteSize; + guint32 realbytes; + + + GST_LOG_OBJECT (qtwrapper, "Calling FillBuffer(outsamples:%d , outdata:%p)", + outsamples, qtwrapper->bufferlist->mBuffers[0].mData); + + /* Ask AudioConverter to give us data ! */ + status = AudioConverterFillComplexBuffer (qtwrapper->aconv, + (AudioConverterComplexInputDataProc) process_buffer_cb, + qtwrapper, (UInt32 *) & outsamples, qtwrapper->bufferlist, NULL); + + if ((status != noErr) && (status != 42)) { + if (status < 0) + GST_WARNING_OBJECT (qtwrapper, + "Error in AudioConverterFillComplexBuffer() : %d", (gint32) status); + else + GST_WARNING_OBJECT (qtwrapper, + "Error in AudioConverterFillComplexBuffer() : %" GST_FOURCC_FORMAT, + QT_FOURCC_ARGS (status)); + ret = GST_FLOW_ERROR; + goto beach; + } + + realbytes = qtwrapper->bufferlist->mBuffers[0].mDataByteSize; + + GST_LOG_OBJECT (qtwrapper, "We now have %d samples [%d bytes]", + outsamples, realbytes); + + qtwrapper->bufferlist->mBuffers[0].mDataByteSize = savedbytes; + + if (!outsamples) + break; + + /* 4. Create buffer and copy data in it */ + ret = gst_pad_alloc_buffer (qtwrapper->srcpad, qtwrapper->cur_offset, + realbytes, GST_PAD_CAPS (qtwrapper->srcpad), &outbuf); + if (ret != GST_FLOW_OK) + goto beach; + + /* copy data from bufferlist to output buffer */ + g_memmove (GST_BUFFER_DATA (outbuf), + qtwrapper->bufferlist->mBuffers[0].mData, realbytes); + + /* 5. calculate timestamp and duration */ + GST_BUFFER_TIMESTAMP (outbuf) = + qtwrapper->initial_time + gst_util_uint64_scale_int (GST_SECOND, + qtwrapper->cur_offset, qtwrapper->samplerate); + GST_BUFFER_SIZE (outbuf) = realbytes; + GST_BUFFER_DURATION (outbuf) = + gst_util_uint64_scale_int (GST_SECOND, + realbytes / (qtwrapper->channels * 4), qtwrapper->samplerate); + + GST_LOG_OBJECT (qtwrapper, + "timestamp:%" GST_TIME_FORMAT ", duration:%" GST_TIME_FORMAT + "offset:%lld, offset_end:%lld", + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), + GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), + GST_BUFFER_OFFSET (outbuf), GST_BUFFER_OFFSET_END (outbuf)); + + qtwrapper->cur_offset += outsamples; + + /* 6. push buffer downstream */ + + ret = gst_pad_push (qtwrapper->srcpad, outbuf); + if (ret != GST_FLOW_OK) + goto beach; + } + +beach: + gst_object_unref (qtwrapper); + return ret; +} + +static gboolean +qtwrapper_audio_decoder_sink_event (GstPad * pad, GstEvent * event) +{ + QTWrapperAudioDecoder *qtwrapper; + gboolean ret = FALSE; + + qtwrapper = (QTWrapperAudioDecoder *) gst_pad_get_parent (pad); + + GST_LOG_OBJECT (qtwrapper, "event:%s", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_NEWSEGMENT:{ + gint64 start, stop, position; + gboolean update; + gdouble rate; + GstFormat format; + + GST_LOG ("We've got a newsegment"); + gst_event_parse_new_segment (event, &update, &rate, &format, &start, + &stop, &position); + + /* if the format isn't time, we need to create a new time newsegment */ + /* FIXME : This is really bad, we should convert the values properly to time */ + if (format != GST_FORMAT_TIME) { + GstEvent *newevent; + + GST_WARNING_OBJECT (qtwrapper, + "Original event wasn't in GST_FORMAT_TIME, creating new fake one."); + + start = 0; + + newevent = + gst_event_new_new_segment (update, rate, GST_FORMAT_TIME, start, + GST_CLOCK_TIME_NONE, start); + gst_event_unref (event); + event = newevent; + } + + qtwrapper->initial_time = start; + qtwrapper->cur_offset = 0; + + gst_adapter_clear (qtwrapper->adapter); + + GST_LOG ("initial_time is now %" GST_TIME_FORMAT, GST_TIME_ARGS (start)); + + if (qtwrapper->aconv) + qtwrapper->gotnewsegment = TRUE; + + /* FIXME : reset adapter */ + break; + } + default: + break; + } + + ret = gst_pad_push_event (qtwrapper->srcpad, event); + + gst_object_unref (qtwrapper); + return TRUE; +} + +gboolean +qtwrapper_audio_decoders_register (GstPlugin * plugin) +{ + gboolean res = TRUE; + OSErr result; + Component componentID = NULL; + ComponentDescription desc = { + 'sdec', 0, 0, 0, 0 + }; + GTypeInfo typeinfo = { + sizeof (QTWrapperAudioDecoderClass), + (GBaseInitFunc) qtwrapper_audio_decoder_base_init, + NULL, + (GClassInitFunc) qtwrapper_audio_decoder_class_init, + NULL, + NULL, + sizeof (QTWrapperAudioDecoder), + 0, + (GInstanceInitFunc) qtwrapper_audio_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; + QTWrapperAudioDecoderParams *params = NULL; + + if (!(get_name_info_from_component (componentID, &thisdesc, &name, + &info))) + 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 (QT_READ_UINT32 (&thisdesc.componentSubType)))) + goto next; + + type_name = g_strdup_printf ("qtwrapperaudiodec_%" 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 (QTWrapperAudioDecoderParams, 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_ADEC_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_ADEC_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; +} diff --git a/sys/qtwrapper/codecmapping.c b/sys/qtwrapper/codecmapping.c new file mode 100644 index 00000000..6aec5f3d --- /dev/null +++ b/sys/qtwrapper/codecmapping.c @@ -0,0 +1,151 @@ +/* + * GStreamer QuickTime codec mapping + * 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. + */ + +#include "qtwrapper.h" +#include "codecmapping.h" +#include "qtutils.h" + +static GstCaps * +audio_caps_from_string (gchar * str) +{ + GstCaps *res; + + res = gst_caps_from_string (str); + gst_caps_set_simple (res, + "rate", GST_TYPE_INT_RANGE, 8000, 96000, + "channels", GST_TYPE_INT_RANGE, 1, 2, NULL); + + return res; +} + +GstCaps * +fourcc_to_caps (guint32 fourcc) +{ + GstCaps *caps = NULL; + + GST_DEBUG ("%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc)); + + switch (fourcc) { + /* VIDEO */ + case QT_MAKE_FOURCC_LE ('S', 'V', 'Q', '1'): + caps = gst_caps_new_simple ("video/x-svq", + "svqversion", G_TYPE_INT, 1, NULL); + break; + case QT_MAKE_FOURCC_LE ('S', 'V', 'Q', '3'): + caps = gst_caps_new_simple ("video/x-svq", + "svqversion", G_TYPE_INT, 3, NULL); + break; + case QT_MAKE_FOURCC_LE ('a', 'v', 'c', '1'): + caps = gst_caps_from_string ("video/x-h264"); + break; + case QT_MAKE_FOURCC_LE ('m', 'p', '4', 'v'): + caps = + gst_caps_from_string + ("video/mpeg,mpegversion=4,systemstream=(boolean)false"); + break; + case QT_MAKE_FOURCC_LE ('m', 'p', 'e', 'g'): + caps = gst_caps_from_string ("video/mpeg, " + "systemstream = (boolean) false, " "mpegversion = (int) 1"); + break; + case QT_MAKE_FOURCC_LE ('h', '2', '6', '3'): + case QT_MAKE_FOURCC_LE ('H', '2', '6', '3'): + case QT_MAKE_FOURCC_LE ('s', '2', '6', '3'): + case QT_MAKE_FOURCC_LE ('U', '2', '6', '3'): + caps = gst_caps_from_string ("video/x-h263"); + break; + case QT_MAKE_FOURCC_LE ('c', 'v', 'i', 'd'): + caps = gst_caps_from_string ("video/x-cinepak"); + break; + case QT_MAKE_FOURCC_LE ('d', 'v', 'c', 'p'): + case QT_MAKE_FOURCC_LE ('d', 'v', 'c', ' '): + case QT_MAKE_FOURCC_LE ('d', 'v', 's', 'd'): + case QT_MAKE_FOURCC_LE ('D', 'V', 'S', 'D'): + case QT_MAKE_FOURCC_LE ('d', 'v', 'c', 's'): + case QT_MAKE_FOURCC_LE ('D', 'V', 'C', 'S'): + case QT_MAKE_FOURCC_LE ('d', 'v', '2', '5'): + case QT_MAKE_FOURCC_LE ('d', 'v', 'p', 'p'): + caps = gst_caps_from_string ("video/x-dv, systemstream=(boolean)false"); + break; + + /* AUDIO */ + case QT_MAKE_FOURCC_LE ('.', 'm', 'p', '3'): + caps = + audio_caps_from_string + ("audio/mpeg,mpegversion=1,layer=3,parsed=(boolean)true"); + break; + case QT_MAKE_FOURCC_LE ('Q', 'D', 'M', '2'): + caps = audio_caps_from_string ("audio/x-qdm2"); + break; + case QT_MAKE_FOURCC_LE ('a', 'g', 's', 'm'): + caps = audio_caps_from_string ("audio/x-gsm"); + break; + case QT_MAKE_FOURCC_LE ('a', 'l', 'a', 'c'): + caps = audio_caps_from_string ("audio/x-alac"); + break; + case QT_MAKE_FOURCC_LE ('a', 'l', 'a', 'w'): + caps = audio_caps_from_string ("audio/x-alaw"); + break; + case QT_MAKE_FOURCC_LE ('m', 'p', '4', 'a'): + case QT_MAKE_FOURCC_LE ('a', 'a', 'c', ' '): + caps = audio_caps_from_string ("audio/mpeg,mpegversion=4"); + break; + case QT_MAKE_FOURCC_LE ('s', 'a', 'm', 'r'): + caps = audio_caps_from_string ("audio/AMR"); + break; + case QT_MAKE_FOURCC_LE ('u', 'l', 'a', 'w'): + caps = audio_caps_from_string ("audio/x-mulaw"); + break; + /* TO FILL !! */ + case QT_MAKE_FOURCC_LE ('M', 'A', 'C', '3'): + case QT_MAKE_FOURCC_LE ('M', 'A', 'C', '6'): + case QT_MAKE_FOURCC_LE ('Q', 'D', 'M', 'C'): + case QT_MAKE_FOURCC_LE ('Q', 'c', 'l', 'p'): + case QT_MAKE_FOURCC_LE ('Q', 'c', 'l', 'q'): + case QT_MAKE_FOURCC_LE ('d', 'v', 'c', 'a'): + default: + break; + } + + return caps; +} diff --git a/sys/qtwrapper/codecmapping.h b/sys/qtwrapper/codecmapping.h new file mode 100644 index 00000000..78600b69 --- /dev/null +++ b/sys/qtwrapper/codecmapping.h @@ -0,0 +1,55 @@ +/* + * GStreamer QuickTime codec mapping + * 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. + */ + +#include <QuickTime/Movies.h> +#include <QuickTime/ImageCodec.h> +#include <gst/gst.h> + +/* + * fourcc_to_caps: + * + * Return the caps for a given fourcc. + */ + +GstCaps *fourcc_to_caps (guint32 fourcc); diff --git a/sys/qtwrapper/imagedescription.c b/sys/qtwrapper/imagedescription.c new file mode 100644 index 00000000..7cdf3990 --- /dev/null +++ b/sys/qtwrapper/imagedescription.c @@ -0,0 +1,211 @@ +/* + * 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. + */ + +#include "imagedescription.h" + +static ImageDescription * +image_description_for_avc1 (GstBuffer * buf) +{ + ImageDescription *desc = NULL; + gpointer pos; + + desc = g_malloc0 (sizeof (ImageDescription) + GST_BUFFER_SIZE (buf) + 8); + pos = (gpointer) ((gulong) desc + (gulong) sizeof (ImageDescription)); + + desc->idSize = sizeof (ImageDescription) + GST_BUFFER_SIZE (buf) + 8; + /* write size in Big-Endian */ + GST_WRITE_UINT32_BE (pos, GST_BUFFER_SIZE (buf) + 8); + GST_WRITE_UINT32_LE (pos + 4, QT_MAKE_FOURCC_BE ('a', 'v', 'c', 'C')); + g_memmove (pos + 8, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); + + return desc; +} + +/* image_description_for_mp4v + * + * mpeg4 video has an 'esds' atom as extension for the ImageDescription. + * It is meant to contain the ES Description. + * We here create a fake one. + */ + +static ImageDescription * +image_description_for_mp4v (GstBuffer * buf) +{ + ImageDescription *desc = NULL; + guint32 offset = sizeof (ImageDescription); + gpointer location; + + GST_LOG ("buf %p , size:%d", buf, GST_BUFFER_SIZE (buf)); + + /* this image description contains: + * ImageDescription sizeof(ImageDescription) + * esds atom 34 bytes + * buffer GST_BUFFER_SIZE (buf) + * ending 3 bytes + */ + + desc = g_malloc0 (offset + 37 + GST_BUFFER_SIZE (buf)); + desc->idSize = offset + 37 + GST_BUFFER_SIZE (buf); + + location = (gpointer) ((gulong) desc + (gulong) offset); + + /* Fill in ESDS */ + /* size */ + GST_WRITE_UINT32_BE (location, 37 + GST_BUFFER_SIZE (buf)); + /* atom */ + GST_WRITE_UINT32_LE (location + 4, GST_MAKE_FOURCC ('e', 's', 'd', 's')); + /* version + flags */ + QT_WRITE_UINT32 (location + 8, 0); + /* tag */ + QT_WRITE_UINT8 (location + 12, 0x3); + /* size (buffsize + 23) */ + QT_WRITE_UINT8 (location + 13, GST_BUFFER_SIZE (buf) + 23); + /* ESID */ + QT_WRITE_UINT16 (location + 14, 0); + /* priority */ + QT_WRITE_UINT8 (location + 16, 0); + /* tag */ + QT_WRITE_UINT8 (location + 17, 0x4); + /* size (buffsize + 8) */ + QT_WRITE_UINT8 (location + 18, GST_BUFFER_SIZE (buf) + 15); + /* object type */ + QT_WRITE_UINT8 (location + 19, 0x20); + /* stream type */ + QT_WRITE_UINT8 (location + 20, 0x11); + /* buffersize db */ + QT_WRITE_UINT24 (location + 21, 13640); + /* max bitrate */ + QT_WRITE_UINT32 (location + 24, 1849648); + /* avg bitrate */ + QT_WRITE_UINT32 (location + 28, 918191); + /* tag */ + QT_WRITE_UINT8 (location + 32, 0x05); + /* size */ + QT_WRITE_UINT8 (location + 33, GST_BUFFER_SIZE (buf)); + /* codec data */ + g_memmove (location + 34, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); + /* end */ + QT_WRITE_UINT8 (location + 34 + GST_BUFFER_SIZE (buf), 0x06); + QT_WRITE_UINT8 (location + 34 + GST_BUFFER_SIZE (buf) + 1, 0x01); + QT_WRITE_UINT8 (location + 34 + GST_BUFFER_SIZE (buf) + 2, 0x02); + + return desc; +} + +static ImageDescription * +image_description_from_stsd_buffer (GstBuffer * buf) +{ + ImageDescription *desc = NULL; + gpointer content; + guint size, imds; + + GST_LOG ("buffer %p, size:%u", buf, GST_BUFFER_SIZE (buf)); + + /* The buffer contains a full atom, we only need the contents */ + /* This buffer has data in big-endian, we need to read it as such. + * except for the fourcc which are ALWAYS big-endian. */ + content = GST_BUFFER_DATA (buf) + 16; + size = GST_BUFFER_SIZE (buf) - 16; + +#if DEBUG_DUMP + GST_LOG ("incoming data in big-endian"); + gst_util_dump_mem (content, size); +#endif + + desc = g_malloc0 (size); + desc->idSize = size; + desc->cType = GST_READ_UINT32_BE (content + 4); + desc->version = QT_UINT16 (content + 16); + desc->revisionLevel = QT_UINT16 (content + 18); + desc->vendor = GST_READ_UINT32_BE (content + 20); + desc->temporalQuality = QT_UINT32 (content + 24); + desc->spatialQuality = QT_UINT32 (content + 24); + desc->dataSize = QT_UINT32 (content + 44); + desc->frameCount = QT_UINT16 (content + 48); + desc->depth = QT_UINT16 (content + 82); + desc->clutID = QT_UINT16 (content + 84); + + imds = 86; /* sizeof (ImageDescription); */ + + if (desc->idSize > imds) { + GST_LOG ("Copying %d bytes from %p to %p", + size - imds, content + imds, desc + imds); + memcpy ((gpointer) ((gulong) desc + imds), + (gpointer) ((gulong) content + imds), size - imds); + } +#if DEBUG_DUMP + GST_LOG ("outgoing data in machine-endian"); + dump_image_description (desc); +#endif + + return desc; +} + +ImageDescription * +image_description_from_codec_data (GstBuffer * buf, guint32 codectype) +{ + ImageDescription *desc = NULL; + + GST_LOG ("codectype:%" GST_FOURCC_FORMAT " buf:%p", + GST_FOURCC_ARGS (codectype), buf); + + if ((GST_BUFFER_SIZE (buf) == GST_READ_UINT32_BE (GST_BUFFER_DATA (buf))) && + (QT_MAKE_FOURCC_LE ('s', 't', 's', + 'd') == GST_READ_UINT32_BE (GST_BUFFER_DATA (buf) + 4))) { + /* We have the full stsd (ImageDescription) in our codec_data */ + desc = image_description_from_stsd_buffer (buf); + } else { + switch (codectype) { + case QT_MAKE_FOURCC_LE ('m', 'p', '4', 'v'): + desc = image_description_for_mp4v (buf); + break; + case QT_MAKE_FOURCC_LE ('a', 'v', 'c', '1'): + desc = image_description_for_avc1 (buf); + break; + default: + GST_WARNING ("Format not handled !"); + } + } + return desc; +} diff --git a/sys/qtwrapper/imagedescription.h b/sys/qtwrapper/imagedescription.h new file mode 100644 index 00000000..fe5ed9a8 --- /dev/null +++ b/sys/qtwrapper/imagedescription.h @@ -0,0 +1,54 @@ +/* + * 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 <QuickTime/Movies.h> +#include "qtwrapper.h" +#include "qtutils.h" + +ImageDescription *image_description_from_codec_data (GstBuffer * buf, + guint32 codec); diff --git a/sys/qtwrapper/qtutils.c b/sys/qtwrapper/qtutils.c new file mode 100644 index 00000000..441a87d2 --- /dev/null +++ b/sys/qtwrapper/qtutils.c @@ -0,0 +1,477 @@ +/* + * GStreamer QuickTime codec mapping + * 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. + */ + +#include "qtutils.h" + +gboolean +get_name_info_from_component (Component componentID, + ComponentDescription * desc, gchar ** name, gchar ** info) +{ + gchar *tmp1 = NULL; + gchar *tmp2 = NULL; + gchar *tmpname; + gchar *tmpinfo; + OSErr result; + + result = GetComponentInfo (componentID, desc, &tmp1, &tmp2, NULL); + if (result != noErr) + return FALSE; + +#if DEBUG_DUMP + GST_LOG ("ComponentDescription dump"); + gst_util_dump_mem ((const guchar *) desc, sizeof (ComponentDescription)); +#endif + + if (tmp1 && name) { + gsize read, written; + + tmpname = g_strndup (tmp1 + 1, (guint8) * tmp1); + *name = g_convert_with_fallback (tmpname, -1, "ASCII", "MAC", + " ", &read, &written, NULL); + if (!*name) + GST_WARNING ("read:%" G_GSIZE_FORMAT ", written:%" G_GSIZE_FORMAT, read, + written); + g_free (tmpname); + } + + if (tmp2 && info) { + tmpinfo = g_strndup (tmp2 + 1, (guint8) * tmp2); + *info = g_convert_with_fallback (tmpinfo, -1, "ASCII", "MAC", + " ", NULL, NULL, NULL); + g_free (tmpinfo); + } + + return TRUE; +} + +/* +struct CodecDecompressParams { + ImageSequence sequenceID; + ImageDescriptionHandle imageDescription; + Ptr data; + long bufferSize; + long frameNumber; + long startLine; + long stopLine; + long conditionFlags; + CodecFlags callerFlags; + CodecCapabilities * capabilities; + ICMProgressProcRecord progressProcRecord; + ICMCompletionProcRecord completionProcRecord; + ICMDataProcRecord dataProcRecord; + CGrafPtr port; + PixMap dstPixMap; + BitMapPtr maskBits; + PixMapPtr mattePixMap; + Rect srcRect; + MatrixRecord * matrix; + CodecQ accuracy; + short transferMode; + ICMFrameTimePtr frameTime; + long reserved[1]; + SInt8 matrixFlags; + SInt8 matrixType; + Rect dstRect; + UInt16 majorSourceChangeSeed; + UInt16 minorSourceChangeSeed; + CDSequenceDataSourcePtr sourceData; + RgnHandle maskRegion; + OSType ** wantedDestinationPixelTypes; + long screenFloodMethod; + long screenFloodValue; + short preferredOffscreenPixelSize; + ICMFrameTimeInfoPtr syncFrameTime; + Boolean needUpdateOnTimeChange; + Boolean enableBlackLining; + Boolean needUpdateOnSourceChange; + Boolean pad; + long unused; + CGrafPtr finalDestinationPort; + long requestedBufferWidth; + long requestedBufferHeight; + Rect displayableAreaOfRequestedBuffer; + Boolean requestedSingleField; + Boolean needUpdateOnNextIdle; + Boolean pad2[2]; + fixed bufferGammaLevel; + UInt32 taskWeight; + OSType taskName; +}; + */ + +/* struct ImageDescription { */ +/* long idSize; /\* total size of this structure *\/ */ +/* 4 CodecType cType; /\* compressor creator type *\/ */ +/* 8 long resvd1; /\* reserved--must be set to 0 *\/ */ +/* 12 short resvd2; /\* reserved--must be set to 0 *\/ */ +/* 14 short dataRefIndex; /\* reserved--must be set to 0 *\/ */ +/* 16 short version; /\* version of compressed data *\/ */ +/* 18 short revisionLevel; /\* compressor that created data *\/ */ +/* 20 long vendor; /\* compressor developer that created data *\/ */ +/* 24 CodecQ temporalQuality; */ +/* /\* degree of temporal compression *\/ */ +/* 28 CodecQ spatialQuality; */ +/* /\* degree of spatial compression *\/ */ +/* 32 short width; /\* width of source image in pixels *\/ */ +/* 34 short height; /\* height of source image in pixels *\/ */ +/* 36 Fixed hRes; /\* horizontal resolution of source image *\/ */ +/* 40 Fixed vRes; /\* vertical resolution of source image *\/ */ +/* 44 long dataSize; /\* size in bytes of compressed data *\/ */ +/* 48 short frameCount; /\* number of frames in image data *\/ */ +/* 50 Str31 name; /\* name of compression algorithm *\/ */ +/* 82 short depth; /\* pixel depth of source image *\/ */ +/* 84 short clutID; /\* ID number of the color table for image *\/ */ +/* }; */ + + +gboolean +get_output_info_from_component (Component componentID) +{ + gboolean ret = FALSE; + ComponentInstance instance; + ImageSubCodecDecompressCapabilities caps; + CodecInfo info; + + GST_LOG ("Creating an instance"); + + /* 1. Create an instance */ + if (!(instance = OpenComponent (componentID))) { + GST_WARNING ("Couldn't open component"); + return FALSE; + } + + /* 2. initialize */ + memset (&caps, 0, sizeof (ImageSubCodecDecompressCapabilities)); + if (ImageCodecInitialize (instance, &caps) != noErr) { + GST_WARNING ("ImageCodecInitialize() failed"); + goto beach; + } +#if DEBUG_DUMP + GST_LOG ("ImageSubCodecDecompressCapabilities"); + gst_util_dump_mem ((const guchar *) &caps, + sizeof (ImageSubCodecDecompressCapabilities)); +#endif + + GST_LOG ("recordSize:%ld", caps.recordSize); + GST_LOG ("decompressRecordSize:%ld", caps.decompressRecordSize); + GST_LOG ("canAsync:%d", caps.canAsync); + + /* 3. Get codec info */ + memset (&info, 0, sizeof (CodecInfo)); + if (ImageCodecGetCodecInfo (instance, &info) != noErr) { + GST_WARNING ("ImageCodecInfo() failed"); + goto beach; + }; + +#if DEBUG_DUMP + GST_LOG ("CodecInfo"); + gst_util_dump_mem ((const guchar *) &info, sizeof (CodecInfo)); +#endif + + GST_LOG ("version:%d", info.version); + GST_LOG ("revisionLevel:%d", info.revisionLevel); + GST_LOG ("vendor:%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (info.vendor)); + + /* Compression flags */ + /* Contains flags (see below) that specify the decompression capabilities of + * the component. Typically, these flags are of interest only to developers of + * image decompressors. */ + GST_LOG ("decompressFlags:%lx", info.decompressFlags); + if (info.decompressFlags & codecInfoDoes1) + GST_LOG ("Depth 1 OK"); + if (info.decompressFlags & codecInfoDoes2) + GST_LOG ("Depth 2 OK"); + if (info.decompressFlags & codecInfoDoes4) + GST_LOG ("Depth 4 OK"); + if (info.decompressFlags & codecInfoDoes8) + GST_LOG ("Depth 8 OK"); + if (info.decompressFlags & codecInfoDoes16) + GST_LOG ("Depth 16 OK"); + if (info.decompressFlags & codecInfoDoes32) + GST_LOG ("Depth 32 OK"); + GST_LOG ("compressFlags:%lx", info.compressFlags); + + /* Format FLAGS */ + /* Contains flags (see below) that describe the possible format for compressed + * data produced by this component and the format of compressed files that the + * component can handle during decompression. Typically, these flags are of + * interest only to developers of compressor components. + */ + GST_LOG ("formatFlags:%lx", info.formatFlags); + if (info.formatFlags & codecInfoDepth1) + GST_LOG ("Depth 1 OK"); + if (info.formatFlags & codecInfoDepth2) + GST_LOG ("Depth 2 OK"); + if (info.formatFlags & codecInfoDepth4) + GST_LOG ("Depth 4 OK"); + if (info.formatFlags & codecInfoDepth8) + GST_LOG ("Depth 8 OK"); + if (info.formatFlags & codecInfoDepth16) + GST_LOG ("Depth 16 OK"); + if (info.formatFlags & codecInfoDepth24) + GST_LOG ("Depth 24 OK"); + if (info.formatFlags & codecInfoDepth32) + GST_LOG ("Depth 32 OK"); + if (info.formatFlags & codecInfoDepth33) + GST_LOG ("Depth 33 OK"); + if (info.formatFlags & codecInfoDepth34) + GST_LOG ("Depth 34 OK"); + if (info.formatFlags & codecInfoDepth36) + GST_LOG ("Depth 36 OK"); + if (info.formatFlags & codecInfoDepth40) + GST_LOG ("Depth 40 OK"); + if (info.formatFlags & codecInfoStoresClut) + GST_LOG ("StoresClut OK"); + if (info.formatFlags & codecInfoDoesLossless) + GST_LOG ("Lossless OK"); + if (info.formatFlags & codecInfoSequenceSensitive) + GST_LOG ("SequenceSentitive OK"); + + + GST_LOG ("compressionAccuracy:%u", info.compressionAccuracy); + GST_LOG ("decompressionAccuracy:%u", info.decompressionAccuracy); + GST_LOG ("compressionSpeed:%d", info.compressionSpeed); + GST_LOG ("decompressionSpeed:%d", info.decompressionSpeed); + GST_LOG ("compressionLevel:%u", info.compressionLevel); + GST_LOG ("minimumHeight:%d", info.minimumHeight); + GST_LOG ("minimumWidth:%d", info.minimumWidth); + +/* /\* . Call ImageCodecPreDecompress *\/ */ +/* memset(¶ms, 0, sizeof(CodecDecompressParams)); */ +/* GST_LOG ("calling imagecodecpredecompress"); */ +/* if (ImageCodecPreDecompress (instance, ¶ms) != noErr) { */ +/* GST_WARNING ("Error in ImageCodecPreDecompress"); */ +/* goto beach; */ +/* } */ + +/* GST_INFO ("sequenceID : %d", params.sequenceID); */ + + ret = TRUE; + +beach: + /* Free instance */ + CloseComponent (instance); + return TRUE; +} + +void +dump_avcc_atom (gpointer atom) +{ + /* first 8 bytes : length + atom */ + GST_LOG ("version:0x%x", QT_UINT8 (atom + 8)); + GST_LOG ("Profile:%d", QT_UINT8 (atom + 9)); + GST_LOG ("Compatible profiles : 0x%x", QT_UINT8 (atom + 10)); + GST_LOG ("Level:%d", QT_UINT8 (atom + 11)); +} + +void +dump_image_description (ImageDescription * desc) +{ + GST_LOG ("Description %p , size:%" G_GSIZE_FORMAT, desc, desc->idSize); + +#if DEBUG_DUMP + gst_util_dump_mem ((const guchar *) desc, desc->idSize); +#endif + + GST_LOG ("cType : %" GST_FOURCC_FORMAT, QT_FOURCC_ARGS (desc->cType)); + GST_LOG ("version:%d", desc->version); + GST_LOG ("revisionLevel:%d", desc->revisionLevel); + GST_LOG ("vendor:%" GST_FOURCC_FORMAT, QT_FOURCC_ARGS (desc->vendor)); + GST_LOG ("temporalQuality:%lu", desc->temporalQuality); + GST_LOG ("spatialQuality:%lu", desc->spatialQuality); + GST_LOG ("width:%u", desc->width); + GST_LOG ("height:%u", desc->height); + GST_LOG ("hres:%f", desc->hRes / 65536.0); + GST_LOG ("vres:%f", desc->vRes / 65536.0); + GST_LOG ("dataSize:%" G_GSIZE_FORMAT, desc->dataSize); + GST_LOG ("frameCount:%d", desc->frameCount); + GST_LOG ("name:%.*s", desc->name[0], desc->name + 1); + GST_LOG ("depth:%d", desc->depth); + GST_LOG ("clutID:%d", desc->clutID); + + if (desc->idSize > sizeof (ImageDescription)) { + gpointer extradata = + (gpointer) (gulong) desc + (gulong) sizeof (ImageDescription); + guint32 type = QT_READ_UINT32 (extradata + 4); + + GST_LOG ("Extra Data size:%lu", + (gulong) desc->idSize - (gulong) sizeof (ImageDescription)); +#if DEBUG_DUMP + gst_util_dump_mem ((gulong) desc + (gulong) sizeof (ImageDescription), + (gulong) desc->idSize - (gulong) sizeof (ImageDescription)); +#endif + GST_LOG ("Extra Data Type : %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (type)); + if (type == QT_MAKE_FOURCC ('a', 'v', 'c', 'C')) + dump_avcc_atom (extradata); + } +} + +void +dump_codec_decompress_params (CodecDecompressParams * params) +{ + GST_LOG ("params %p", params); + +#if DEBUG_DUMP + gst_util_dump_mem ((const guchar *) params, sizeof (CodecDecompressParams)); +#endif + + GST_LOG ("SequenceID:%ld", params->sequenceID); + GST_LOG ("imageDescription:%p", params->imageDescription); + GST_LOG ("data:%p", params->data); + GST_LOG ("bufferSize:%ld", params->bufferSize); + GST_LOG ("frameNumber:%ld", params->frameNumber); + GST_LOG ("startLine:%ld , StopLine:%ld", params->startLine, + params->stopLine); + GST_LOG ("conditionFlags:0x%lx", params->conditionFlags); + GST_LOG ("callerFlags:0x%x", params->callerFlags); + GST_LOG ("capabilities:%p", params->capabilities); + GST_LOG ("port:%p", params->port); + GST_LOG ("dstPixMap"); + gst_util_dump_mem ((const guchar *) ¶ms->dstPixMap, sizeof (PixMap)); + + GST_LOG ("maskBits:%p", params->maskBits); + GST_LOG ("mattePixMap:%p", params->mattePixMap); + GST_LOG ("srcRect %d/%d/%d/%d", + params->srcRect.top, params->srcRect.bottom, + params->srcRect.left, params->srcRect.right); + + GST_LOG ("matrix:%p", params->matrix); + GST_LOG ("accuracy:%ld", params->accuracy); + GST_LOG ("transferMode:%d", params->transferMode); + GST_LOG ("frameTime:%p", params->frameTime); + GST_LOG ("matrixFlags:%x", params->matrixFlags); + + GST_LOG ("dstRect %d/%d/%d/%d", + params->dstRect.top, params->dstRect.bottom, + params->dstRect.left, params->dstRect.right); + + GST_LOG ("sourceData:%p", params->sourceData); + + if (params->wantedDestinationPixelTypes) { + OSType *tmp; + + for (tmp = *params->wantedDestinationPixelTypes; *tmp; tmp++) + GST_LOG ("Destination pixel %" GST_FOURCC_FORMAT, QT_FOURCC_ARGS (*tmp)); + } +} + +void +addSInt32ToDictionary (CFMutableDictionaryRef dictionary, CFStringRef key, + SInt32 numberSInt32) +{ + CFNumberRef number = + CFNumberCreate (NULL, kCFNumberSInt32Type, &numberSInt32); + if (!number) + return; + CFDictionaryAddValue (dictionary, key, number); + CFRelease (number); +} + +void +dump_cvpixel_buffer (CVPixelBufferRef pixbuf) +{ + gsize left, right, top, bottom; + + GST_LOG ("buffer %p", pixbuf); + if (CVPixelBufferLockBaseAddress (pixbuf, 0)) { + GST_WARNING ("Couldn't lock base adress on pixel buffer !"); + return; + } + GST_LOG ("Width:%" G_GSIZE_FORMAT " , Height:%" G_GSIZE_FORMAT, + CVPixelBufferGetWidth (pixbuf), CVPixelBufferGetHeight (pixbuf)); + GST_LOG ("Format:%" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (CVPixelBufferGetPixelFormatType (pixbuf))); + GST_LOG ("base address:%p", CVPixelBufferGetBaseAddress (pixbuf)); + GST_LOG ("Bytes per row:%" G_GSIZE_FORMAT, + CVPixelBufferGetBytesPerRow (pixbuf)); + GST_LOG ("Data Size:%" G_GSIZE_FORMAT, CVPixelBufferGetDataSize (pixbuf)); + GST_LOG ("Plane count:%" G_GSIZE_FORMAT, CVPixelBufferGetPlaneCount (pixbuf)); + CVPixelBufferGetExtendedPixels (pixbuf, &left, &right, &top, &bottom); + GST_LOG ("Extended pixels. left/right/top/bottom : %" G_GSIZE_FORMAT + "/%" G_GSIZE_FORMAT "/%" G_GSIZE_FORMAT "/%" G_GSIZE_FORMAT, + left, right, top, bottom); + CVPixelBufferUnlockBaseAddress (pixbuf, 0); +} + + +// Convenience function to dispose of our audio buffers +void +DestroyAudioBufferList (AudioBufferList * list) +{ + UInt32 i; + + if (list) { + for (i = 0; i < list->mNumberBuffers; i++) { + if (list->mBuffers[i].mData) + free (list->mBuffers[i].mData); + } + free (list); + } +} + +// Convenience function to allocate our audio buffers +AudioBufferList * +AllocateAudioBufferList (UInt32 numChannels, UInt32 size) +{ + AudioBufferList *list; + UInt32 i; + + list = + (AudioBufferList *) calloc (1, + sizeof (AudioBufferList) + sizeof (AudioBuffer)); + if (list == NULL) + return NULL; + + list->mNumberBuffers = 1; + for (i = 0; i < 1; ++i) { + list->mBuffers[i].mNumberChannels = numChannels; + list->mBuffers[i].mDataByteSize = size; + list->mBuffers[i].mData = malloc (size); + if (list->mBuffers[i].mData == NULL) { + DestroyAudioBufferList (list); + return NULL; + } + } + return list; +} diff --git a/sys/qtwrapper/qtutils.h b/sys/qtwrapper/qtutils.h new file mode 100644 index 00000000..cb550c85 --- /dev/null +++ b/sys/qtwrapper/qtutils.h @@ -0,0 +1,131 @@ +/* + * GStreamer QuickTime codec mapping + * 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. + */ + +#include <QuickTime/Movies.h> +#include <QuickTime/ImageCodec.h> +#include <gst/gst.h> +#include "qtwrapper.h" + +#ifndef __QTUTILS_H__ +#define __QTUTILS_H__ + +#define QT_UINT32(a) (GST_READ_UINT32_BE(a)) +#define QT_UINT24(a) (GST_READ_UINT32_BE(a) >> 8) +#define QT_UINT16(a) (GST_READ_UINT16_BE(a)) +#define QT_UINT8(a) (GST_READ_UINT8(a)) +#define QT_FP32(a) ((GST_READ_UINT32_BE(a))/65536.0) +#define QT_FP16(a) ((GST_READ_UINT16_BE(a))/256.0) +#define QT_FOURCC(a) (GST_READ_UINT32_LE(a)) +#define QT_UINT64(a) ((((guint64)QT_UINT32(a))<<32)|QT_UINT32(((guint8 *)a)+4)) +#define QT_FOURCC_ARGS(fourcc) \ + ((gchar) (((fourcc)>>24)&0xff)), \ + ((gchar) (((fourcc)>>16)&0xff)), \ + ((gchar) (((fourcc)>>8 )&0xff)), \ + ((gchar) ((fourcc) &0xff)) + +#define QT_WRITE_UINT8(data, num) GST_WRITE_UINT8(data, num) + +#define QT_MAKE_FOURCC_BE(a,b,c,d) (guint32)((a)|(b)<<8|(c)<<16|(d)<<24) +#define QT_MAKE_FOURCC_LE(a,b,c,d) QT_MAKE_FOURCC_BE(d,c,b,a) + +#define _QT_PUT(__data, __idx, __size, __shift, __num) \ + (((guint8 *) (__data))[__idx] = (((guint##__size) __num) >> __shift) & 0xff) + +/* endianness-dependent macros */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN +#define QT_MAKE_FOURCC(a,b,c,d) QT_MAKE_FOURCC_LE(a,b,c,d) +#define QT_WRITE_UINT16(data, num) GST_WRITE_UINT16_LE(data, num) +#define QT_WRITE_UINT24(data, num) do { \ + _QT_PUT (data, 0, 32, 0, num); \ + _QT_PUT (data, 1, 32, 8, num); \ + _QT_PUT (data, 2, 32, 16, num); \ + } while (0) +#define QT_WRITE_UINT32(data, num) GST_WRITE_UINT32_LE(data, num) +#define QT_READ_UINT16(data) GST_READ_UINT16_LE(data) +#define QT_READ_UINT32(data) GST_READ_UINT32_LE(data) +#else +#define QT_MAKE_FOURCC(a,b,c,d) QT_MAKE_FOURCC_BE(a,b,c,d) +#define QT_WRITE_UINT16(data, num) GST_WRITE_UINT16_BE(data, num) +#define QT_WRITE_UINT24(data, num) do { \ + _QT_PUT (data, 0, 32, 16, num); \ + _QT_PUT (data, 1, 32, 8, num); \ + _QT_PUT (data, 2, 32, 0, num); \ + } while (0) +#define QT_WRITE_UINT32(data, num) GST_WRITE_UINT32_BE(data, num) +#define QT_READ_UINT16(data) GST_READ_UINT16_BE(data) +#define QT_READ_UINT32(data) GST_READ_UINT32_BE(data) +#endif + + +/* + * get_name_info_from_component: + * + * Fills name and info with the name and description from a Component + */ + +gboolean +get_name_info_from_component (Component component, ComponentDescription * desc, + gchar ** name, gchar ** info); + + + +gboolean get_output_info_from_component (Component component); + + + +void dump_image_description (ImageDescription * desc); +void dump_codec_decompress_params (CodecDecompressParams * params); + +guint32 destination_pixel_types_to_fourcc (OSType ** types); +void +addSInt32ToDictionary (CFMutableDictionaryRef dictionary, CFStringRef key, + SInt32 numberSInt32); + +void dump_cvpixel_buffer (CVPixelBufferRef pixbuf); + +AudioBufferList *AllocateAudioBufferList(UInt32 numChannels, UInt32 size); + +void DestroyAudioBufferList(AudioBufferList* list); + +#endif /* __QTUTILS_H__ */ diff --git a/sys/qtwrapper/qtwrapper.c b/sys/qtwrapper/qtwrapper.c new file mode 100644 index 00000000..ca4793e5 --- /dev/null +++ b/sys/qtwrapper/qtwrapper.c @@ -0,0 +1,71 @@ +/* + * GStreamer QuickTime 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 "qtwrapper.h" + +GST_DEBUG_CATEGORY (qtwrapper_debug); + +static gboolean +plugin_init (GstPlugin * plugin) +{ + gboolean res; + + GST_DEBUG_CATEGORY_INIT (qtwrapper_debug, "qtwrapper", + 0, "QuickTime codecs wrappers"); + + res = qtwrapper_video_decoders_register (plugin); + res &= qtwrapper_audio_decoders_register (plugin); + + return res; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "qtwrapper", + "QuickTime codecs wrapper", + plugin_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/") diff --git a/sys/qtwrapper/qtwrapper.h b/sys/qtwrapper/qtwrapper.h new file mode 100644 index 00000000..bba744ba --- /dev/null +++ b/sys/qtwrapper/qtwrapper.h @@ -0,0 +1,63 @@ +/* + * GStreamer QuickTime 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. + */ + +#ifndef __GST_QTWRAPPER_H__ +#define __GST_QTWRAPPER_H__ + +#include <gst/gst.h> + +/* Set following to 1 if you want to have extra debug in form of + * memory dumps */ +#define DEBUG_DUMP 0 + +GST_DEBUG_CATEGORY_EXTERN (qtwrapper_debug); +#define GST_CAT_DEFAULT qtwrapper_debug + +G_BEGIN_DECLS + +extern gboolean qtwrapper_video_decoders_register (GstPlugin *); +extern gboolean qtwrapper_audio_decoders_register (GstPlugin *); + +G_END_DECLS +#endif /* __GST_QTWRAPPER_H__ */ 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 <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 <QuickTime/Movies.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) +{ + 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 <gstreamer@fluendo.com>, " + "Pioneers of the Inevitable <songbird@songbirdnest.com>"; + 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; +} |