gstreamer/sys/qtwrapper/videodecoders.c

875 lines
26 KiB
C
Raw Normal View History

configure.ac: Add QuickTime Wrapper plug-in. Original commit message from CVS: 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 13:19:48 +00:00
/*
* 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;
}