summaryrefslogtreecommitdiffstats
path: root/sys/qtwrapper/audiodecoders.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/qtwrapper/audiodecoders.c')
-rw-r--r--sys/qtwrapper/audiodecoders.c799
1 files changed, 799 insertions, 0 deletions
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;
+}