diff options
author | Dave Robillard <dave@drobilla.net> | 2009-05-26 19:10:44 -0400 |
---|---|---|
committer | Dave Robillard <dave@drobilla.net> | 2009-05-26 19:10:44 -0400 |
commit | b75a26657febaf86c4137b4d41c068926325e316 (patch) | |
tree | 65c161824169ac09bf8418244937aec6ab77a270 /gst | |
parent | 4e1d3bba9c21cb8bbfe70ffed953a8385fb7314d (diff) | |
parent | 8f70498c898a65d0938e3e104e91662ff5b693c3 (diff) | |
download | gst-plugins-bad-b75a26657febaf86c4137b4d41c068926325e316.tar.gz gst-plugins-bad-b75a26657febaf86c4137b4d41c068926325e316.tar.bz2 gst-plugins-bad-b75a26657febaf86c4137b4d41c068926325e316.zip |
Merge branch 'master' of git://anongit.freedesktop.org/gstreamer/gst-plugins-bad into fdo
Diffstat (limited to 'gst')
103 files changed, 6218 insertions, 14730 deletions
diff --git a/gst/adpcmdec/Makefile.am b/gst/adpcmdec/Makefile.am new file mode 100644 index 00000000..5c60ad4f --- /dev/null +++ b/gst/adpcmdec/Makefile.am @@ -0,0 +1,12 @@ +plugin_LTLIBRARIES = libgstadpcmdec.la + +# sources used to compile this plug-in +libgstadpcmdec_la_SOURCES = adpcmdec.c + +# flags used to compile this plugin +# add other _CFLAGS and _LIBS as needed +libgstadpcmdec_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) +libgstadpcmdec_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) +libgstadpcmdec_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstadpcmdec_la_LIBTOOLFLAGS = --tag=disable-static + diff --git a/gst/adpcmdec/adpcmdec.c b/gst/adpcmdec/adpcmdec.c new file mode 100644 index 00000000..c58dc5d8 --- /dev/null +++ b/gst/adpcmdec/adpcmdec.c @@ -0,0 +1,452 @@ +/* GStreamer + * Copyright (C) 2009 Pioneers of the Inevitable <songbird@songbirdnest.com> + * + * Authors: Michael Smith <msmith@songbirdnest.com> + * + * 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. + */ + +/* Based on MS-ADPCM decoder in libsndfile, + Copyright (C) 1999-2002 Erik de Castro Lopo <erikd@zip.com.au + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> + +#define GST_TYPE_ADPCM_DEC \ + (adpcmdec_get_type ()) + +#define GST_ADPCM_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_ADPCM_DEC, ADPCMDec)) + +#define GST_CAT_DEFAULT adpcmdec_debug +GST_DEBUG_CATEGORY_STATIC (adpcmdec_debug); + +static const GstElementDetails adpcmdec_details = +GST_ELEMENT_DETAILS ("MS-ADPCM decoder", + "Codec/Decoder/Audio", + "Decode MS AD-PCM audio", + "Pioneers of the Inevitable <songbird@songbirdnest.com"); + +static GstStaticPadTemplate adpcmdec_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-adpcm, " + "layout=(string)microsoft, " + "block_align = (int) [64, 8096], " + "rate = (int)[ 1, MAX ], " "channels = (int)[1,2]") + ); + +static GstStaticPadTemplate adpcmdec_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-int, " + "depth = (int)16, " + "width = (int)16, " + "endianness = (int)" G_STRINGIFY (G_BYTE_ORDER) ", " + "signed = (boolean)TRUE, " + "channels = (int) [1,2], " "rate = (int)[1, MAX]") + ); + +typedef struct _ADPCMDecClass +{ + GstElementClass parent_class; +} ADPCMDecClass; + +typedef struct _ADPCMDec +{ + GstElement parent; + + GstPad *sinkpad; + GstPad *srcpad; + + GstCaps *output_caps; + + int rate; + int channels; + int blocksize; + + gboolean is_setup; + + GstClockTime timestamp; + + guint64 out_samples; + + GstAdapter *adapter; + +} ADPCMDec; + +GST_BOILERPLATE (ADPCMDec, adpcmdec, GstElement, GST_TYPE_ELEMENT); +static gboolean +adpcmdec_setup (ADPCMDec * dec) +{ + dec->output_caps = gst_caps_new_simple ("audio/x-raw-int", + "rate", G_TYPE_INT, dec->rate, + "channels", G_TYPE_INT, dec->channels, + "width", G_TYPE_INT, 16, + "depth", G_TYPE_INT, 16, + "endianness", G_TYPE_INT, G_BYTE_ORDER, + "signed", G_TYPE_BOOLEAN, TRUE, NULL); + + if (dec->output_caps) { + gst_pad_set_caps (dec->srcpad, dec->output_caps); + } + + dec->is_setup = TRUE; + dec->timestamp = GST_CLOCK_TIME_NONE; + dec->adapter = gst_adapter_new (); + dec->out_samples = 0; + + return gst_pad_push_event (dec->srcpad, gst_event_new_new_segment (FALSE, + 1.0, GST_FORMAT_TIME, 0, -1, 0)); +} + +static void +adpcmdec_teardown (ADPCMDec * dec) +{ + if (dec->output_caps) { + gst_caps_unref (dec->output_caps); + dec->output_caps = NULL; + } + if (dec->adapter) { + g_object_unref (dec->adapter); + dec->adapter = NULL; + } + dec->is_setup = FALSE; +} + +static gboolean +adpcmdec_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + ADPCMDec *dec = (ADPCMDec *) gst_pad_get_parent (pad); + GstStructure *structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "block_align", &dec->blocksize)) + return FALSE; + if (!gst_structure_get_int (structure, "rate", &dec->rate)) + return FALSE; + if (!gst_structure_get_int (structure, "channels", &dec->channels)) + return FALSE; + + if (dec->is_setup) + adpcmdec_teardown (dec); + gst_object_unref (dec); + + return TRUE; +} + + +/*===================================================================== + * From libsndfile: + * + * MS ADPCM Block Layout. + * ====================== + * Block is usually 256, 512 or 1024 bytes depending on sample rate. + * For a mono file, the block is laid out as follows: + * byte purpose + * 0 block predictor [0..6] + * 1,2 initial idelta (positive) + * 3,4 sample 1 + * 5,6 sample 0 + * 7..n packed bytecodes + * + * For a stereo file, the block is laid out as follows: + * byte purpose + * 0 block predictor [0..6] for left channel + * 1 block predictor [0..6] for right channel + * 2,3 initial idelta (positive) for left channel + * 4,5 initial idelta (positive) for right channel + * 6,7 sample 1 for left channel + * 8,9 sample 1 for right channel + * 10,11 sample 0 for left channel + * 12,13 sample 0 for right channel + * 14..n packed bytecodes + * + *===================================================================== +*/ +static int AdaptationTable[] = { + 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 +}; + +static int AdaptCoeff1[] = { + 256, 512, 0, 192, 240, 460, 392 +}; + +static int AdaptCoeff2[] = { + 0, -256, 0, 64, 0, -208, -232 +}; + +static gint16 +read_sample (guint8 * data) +{ + guint16 val = data[0] | (data[1] << 8); + return *((gint16 *) & val); +} + +/* Decode a single block of data from 'data', storing 'n_samples' decoded 16 bit + samples in 'samples'. + + All buffer lengths have been verified by the caller + */ +static gboolean +adpcmdec_decode_ms_block (ADPCMDec * dec, int n_samples, guint8 * data, + gint16 * samples) +{ + gint16 pred[2]; + gint16 idelta[2]; + int idx; /* Current byte offset in 'data' */ + int i; /* Current sample index in 'samples' */ + + /* Read the block header, verify for sanity */ + if (dec->channels == 1) { + pred[0] = data[0]; + idelta[0] = read_sample (data + 1); + samples[1] = read_sample (data + 3); + samples[0] = read_sample (data + 5); + idx = 7; + i = 2; + if (pred[0] < 0 || pred[0] > 6) { + GST_WARNING_OBJECT (dec, "Invalid block predictor"); + return FALSE; + } + } + + else { + pred[0] = data[0]; + pred[1] = data[1]; + idelta[0] = read_sample (data + 2); + idelta[1] = read_sample (data + 4); + samples[2] = read_sample (data + 6); + samples[3] = read_sample (data + 8); + samples[0] = read_sample (data + 10); + samples[1] = read_sample (data + 12); + idx = 14; + i = 4; + if (pred[0] < 0 || pred[0] > 6 || pred[1] < 0 || pred[1] > 6) { + GST_WARNING_OBJECT (dec, "Invalid block predictor"); + return FALSE; + } + } + for (; i < n_samples; i++) { + int chan = i % dec->channels; + int bytecode; + int delta; + int current; + int predict; + if (i % 2 == 0) { + bytecode = (data[idx] >> 4) & 0x0F; + } else { + bytecode = data[idx] & 0x0F; + idx++; + } + + delta = idelta[chan]; + idelta[chan] = (AdaptationTable[bytecode] * delta) >> 8; + if (idelta[chan] < 16) + idelta[chan] = 16; + + /* Bytecode is used above as an index into the table. Below, it's used + as a signed 4-bit value; convert appropriately */ + if (bytecode & 0x8) + bytecode -= 0x10; + + predict = ((samples[i - dec->channels] * AdaptCoeff1[pred[chan]]) + + (samples[i - 2 * dec->channels] * AdaptCoeff2[pred[chan]]) + ) >> 8; + + current = (bytecode * delta) + predict; + + /* Clamp to 16 bits, store decoded sample */ + samples[i] = CLAMP (current, G_MININT16, G_MAXINT16); + } + return TRUE; +} + +static GstFlowReturn +adpcmdec_chain (GstPad * pad, GstBuffer * buf) +{ + ADPCMDec *dec = (ADPCMDec *) gst_pad_get_parent (pad); + GstFlowReturn ret = GST_FLOW_OK; + guint8 *data; + GstBuffer *outbuf = NULL; + GstBuffer *databuf = NULL; + int outsize; + int samples; + gboolean res; + + if (!dec->is_setup) + adpcmdec_setup (dec); + + if (dec->timestamp == GST_CLOCK_TIME_NONE) + dec->timestamp = GST_BUFFER_TIMESTAMP (buf); + + gst_adapter_push (dec->adapter, buf); + + while (gst_adapter_available (dec->adapter) >= dec->blocksize) { + databuf = gst_adapter_take_buffer (dec->adapter, dec->blocksize); + data = GST_BUFFER_DATA (databuf); + + /* Each block has a 3 byte header per channel, plus 4 bytes per channel to + give two initial sample values per channel. Then the remainder gives + two samples per byte */ + samples = (dec->blocksize - 7 * dec->channels) * 2 + 2 * dec->channels; + outsize = 2 * samples; + outbuf = gst_buffer_new_and_alloc (outsize); + + res = adpcmdec_decode_ms_block (dec, samples, data, + (gint16 *) (GST_BUFFER_DATA (outbuf))); + + /* Done with input data, free it */ + gst_buffer_unref (databuf); + + if (!res) { + gst_buffer_unref (outbuf); + GST_WARNING_OBJECT (dec, "Decode of block failed"); + ret = GST_FLOW_ERROR; + goto done; + } + + gst_buffer_set_caps (outbuf, dec->output_caps); + GST_BUFFER_TIMESTAMP (outbuf) = dec->timestamp; + dec->out_samples += samples / dec->channels; + dec->timestamp = + gst_util_uint64_scale_int (dec->out_samples, GST_SECOND, dec->rate); + GST_BUFFER_DURATION (outbuf) = + dec->timestamp - GST_BUFFER_TIMESTAMP (outbuf); + + ret = gst_pad_push (dec->srcpad, outbuf); + if (ret != GST_FLOW_OK) + goto done; + } + +done: + gst_object_unref (dec); + + return ret; +} + +static gboolean +adpcmdec_sink_event (GstPad * pad, GstEvent * event) +{ + ADPCMDec *dec = (ADPCMDec *) gst_pad_get_parent (pad); + gboolean res; + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + gst_adapter_clear (dec->adapter); + /* Fall through */ + default: + res = gst_pad_push_event (dec->srcpad, event); + break; + } + gst_object_unref (dec); + return res; +} + +static GstStateChangeReturn +adpcmdec_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret; + ADPCMDec *dec = (ADPCMDec *) element; + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + adpcmdec_teardown (dec); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + return ret; +} + +static void +adpcmdec_dispose (GObject * obj) +{ + G_OBJECT_CLASS (parent_class)->dispose (obj); +} + +static void +adpcmdec_init (ADPCMDec * dec, ADPCMDecClass * klass) +{ + dec->sinkpad = + gst_pad_new_from_static_template (&adpcmdec_sink_template, "sink"); + gst_pad_set_setcaps_function (dec->sinkpad, + GST_DEBUG_FUNCPTR (adpcmdec_sink_setcaps)); + gst_pad_set_chain_function (dec->sinkpad, GST_DEBUG_FUNCPTR (adpcmdec_chain)); + gst_pad_set_event_function (dec->sinkpad, + GST_DEBUG_FUNCPTR (adpcmdec_sink_event)); + gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad); + dec->srcpad = + gst_pad_new_from_static_template (&adpcmdec_src_template, "src"); + gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad); +} + +static void +adpcmdec_class_init (ADPCMDecClass * klass) +{ + GObjectClass *gobjectclass = (GObjectClass *) klass; + GstElementClass *gstelement_class = (GstElementClass *) klass; + gobjectclass->dispose = adpcmdec_dispose; + gstelement_class->change_state = adpcmdec_change_state; +} static void + +adpcmdec_base_init (gpointer klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&adpcmdec_sink_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&adpcmdec_src_template)); + gst_element_class_set_details (element_class, &adpcmdec_details); +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (adpcmdec_debug, "adpcmdec", 0, "ADPCM Decoders"); + if (!gst_element_register (plugin, "msadpcmdec", GST_RANK_PRIMARY, + GST_TYPE_ADPCM_DEC)) { + return FALSE; + } + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, "adpcmdec", + "ADPCM decoder", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, + GST_PACKAGE_ORIGIN); diff --git a/gst/debugutils/fpsdisplaysink.c b/gst/debugutils/fpsdisplaysink.c index b37ab112..54f5cc54 100644 --- a/gst/debugutils/fpsdisplaysink.c +++ b/gst/debugutils/fpsdisplaysink.c @@ -267,11 +267,11 @@ fps_display_sink_start (GstFPSDisplaySink * self) gst_object_ref (self->text_overlay); g_object_set (self->text_overlay, "font-desc", DEFAULT_FONT, "silent", FALSE, NULL); - } - gst_bin_add (GST_BIN (self), self->text_overlay); + gst_bin_add (GST_BIN (self), self->text_overlay); - if (!gst_element_link (self->text_overlay, self->video_sink)) { - GST_ERROR_OBJECT (self, "Could not link elements"); + if (!gst_element_link (self->text_overlay, self->video_sink)) { + GST_ERROR_OBJECT (self, "Could not link elements"); + } } target_pad = gst_element_get_static_pad (self->text_overlay, "video_sink"); } @@ -280,6 +280,7 @@ no_text_overlay: if (self->text_overlay) { gst_element_unlink (self->text_overlay, self->video_sink); gst_bin_remove (GST_BIN (self), self->text_overlay); + self->text_overlay = NULL; } target_pad = gst_element_get_static_pad (self->video_sink, "sink"); } @@ -300,6 +301,13 @@ fps_display_sink_stop (GstFPSDisplaySink * self) g_source_remove (self->timeout_id); self->timeout_id = 0; } + + if (self->text_overlay) { + gst_element_unlink (self->text_overlay, self->video_sink); + gst_bin_remove (GST_BIN (self), self->text_overlay); + gst_object_unref (self->text_overlay); + self->text_overlay = NULL; + } } static void diff --git a/gst/deinterlace/.gitignore b/gst/deinterlace/.gitignore deleted file mode 100644 index 08f5ed37..00000000 --- a/gst/deinterlace/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -Makefile -Makefile.in -*.o -*.lo -*.la -.deps -.libs diff --git a/gst/deinterlace/Makefile.am b/gst/deinterlace/Makefile.am deleted file mode 100644 index 3d842d01..00000000 --- a/gst/deinterlace/Makefile.am +++ /dev/null @@ -1,12 +0,0 @@ -plugin_LTLIBRARIES = libgstdeinterlace.la - -libgstdeinterlace_la_SOURCES = gstdeinterlace.c -libgstdeinterlace_la_CFLAGS = \ - $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) -libgstdeinterlace_la_LIBADD = \ - $(GST_PLUGINS_BASE_LIBS) -lgstvideo-$(GST_MAJORMINOR) $(GST_BASE_LIBS) -libgstdeinterlace_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgstdeinterlace_la_LIBTOOLFLAGS = --tag=disable-static - -noinst_HEADERS = gstdeinterlace.h - diff --git a/gst/deinterlace/deinterlace.vcproj b/gst/deinterlace/deinterlace.vcproj deleted file mode 100644 index 200d88ee..00000000 --- a/gst/deinterlace/deinterlace.vcproj +++ /dev/null @@ -1,148 +0,0 @@ -<?xml version="1.0" encoding="Windows-1252"?> -<VisualStudioProject - ProjectType="Visual C++" - Version="7.10" - Name="deinterlace" - ProjectGUID="{979C216F-0ACF-4956-AE00-055A42D678B0}" - Keyword="Win32Proj"> - <Platforms> - <Platform - Name="Win32"/> - </Platforms> - <Configurations> - <Configuration - Name="Debug|Win32" - OutputDirectory="../../win32/Debug" - IntermediateDirectory="../../win32/Debug" - ConfigurationType="2" - CharacterSet="2"> - <Tool - Name="VCCLCompilerTool" - Optimization="0" - AdditionalIncludeDirectories="../../../gstreamer/win32;../../../gstreamer;../../../gstreamer/libs;../../../glib;../../../glib/glib;../../../glib/gmodule;"../../gst-libs";../../../popt/include;../../../libxml2/include/libxml2" - PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;deinterlace_EXPORTS;HAVE_CONFIG_H;_USE_MATH_DEFINES" - MinimalRebuild="TRUE" - BasicRuntimeChecks="3" - RuntimeLibrary="3" - UsePrecompiledHeader="0" - WarningLevel="3" - Detect64BitPortabilityProblems="TRUE" - DebugInformationFormat="4"/> - <Tool - Name="VCCustomBuildTool"/> - <Tool - Name="VCLinkerTool" - AdditionalDependencies="glib-2.0.lib gmodule-2.0.lib gthread-2.0.lib gobject-2.0.lib libgstreamer.lib gstbytestream.lib iconv.lib intl.lib" - OutputFile="$(OutDir)/gstdeinterlace.dll" - LinkIncremental="2" - AdditionalLibraryDirectories="../../../gstreamer/win32/Debug;../../../glib/glib;../../../glib/gmodule;../../../glib/gthread;../../../glib/gobject;../../../gettext/lib;../../../libiconv/lib" - ModuleDefinitionFile="" - GenerateDebugInformation="TRUE" - ProgramDatabaseFile="$(OutDir)/deinterlace.pdb" - SubSystem="2" - OptimizeReferences="2" - ImportLibrary="$(OutDir)/gstdeinterlace.lib" - TargetMachine="1"/> - <Tool - Name="VCMIDLTool"/> - <Tool - Name="VCPostBuildEventTool" - CommandLine="copy /Y $(TargetPath) c:\gstreamer\plugins"/> - <Tool - Name="VCPreBuildEventTool"/> - <Tool - Name="VCPreLinkEventTool"/> - <Tool - Name="VCResourceCompilerTool"/> - <Tool - Name="VCWebServiceProxyGeneratorTool"/> - <Tool - Name="VCXMLDataGeneratorTool"/> - <Tool - Name="VCWebDeploymentTool"/> - <Tool - Name="VCManagedWrapperGeneratorTool"/> - <Tool - Name="VCAuxiliaryManagedWrapperGeneratorTool"/> - </Configuration> - <Configuration - Name="Release|Win32" - OutputDirectory="../../win32/Release" - IntermediateDirectory="../../win32/Release" - ConfigurationType="2" - CharacterSet="2"> - <Tool - Name="VCCLCompilerTool" - AdditionalIncludeDirectories="../../../gstreamer/win32;../../../gstreamer;../../../gstreamer/libs;../../../glib;../../../glib/glib;../../../glib/gmodule;"../../gst-libs";../../../popt/include;../../../libxml2/include/libxml2" - PreprocessorDefinitions="WIN32;NDEBUG;GST_DISABLE_GST_DEBUG;_WINDOWS;_USRDLL;deinterlace_EXPORTS;HAVE_CONFIG_H;_USE_MATH_DEFINES" - RuntimeLibrary="2" - UsePrecompiledHeader="0" - WarningLevel="3" - Detect64BitPortabilityProblems="TRUE" - DebugInformationFormat="3"/> - <Tool - Name="VCCustomBuildTool"/> - <Tool - Name="VCLinkerTool" - AdditionalDependencies="glib-2.0.lib gmodule-2.0.lib gthread-2.0.lib gobject-2.0.lib libgstreamer.lib gstbytestream.lib iconv.lib intl.lib" - OutputFile="$(OutDir)/gstdeinterlace.dll" - LinkIncremental="1" - AdditionalLibraryDirectories="../../../gstreamer/win32/Release;../../../glib/glib;../../../glib/gmodule;../../../glib/gthread;../../../glib/gobject;../../../gettext/lib;../../../libiconv/lib" - ModuleDefinitionFile="" - GenerateDebugInformation="TRUE" - SubSystem="2" - OptimizeReferences="2" - EnableCOMDATFolding="2" - ImportLibrary="$(OutDir)/gstdeinterlace.lib" - TargetMachine="1"/> - <Tool - Name="VCMIDLTool"/> - <Tool - Name="VCPostBuildEventTool" - CommandLine="copy /Y $(TargetPath) c:\gstreamer\plugins"/> - <Tool - Name="VCPreBuildEventTool"/> - <Tool - Name="VCPreLinkEventTool"/> - <Tool - Name="VCResourceCompilerTool"/> - <Tool - Name="VCWebServiceProxyGeneratorTool"/> - <Tool - Name="VCXMLDataGeneratorTool"/> - <Tool - Name="VCWebDeploymentTool"/> - <Tool - Name="VCManagedWrapperGeneratorTool"/> - <Tool - Name="VCAuxiliaryManagedWrapperGeneratorTool"/> - </Configuration> - </Configurations> - <References> - </References> - <Files> - <Filter - Name="Source Files" - Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx" - UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"> - <File - RelativePath=".\gstdeinterlace.c"> - </File> - </Filter> - <Filter - Name="Header Files" - Filter="h;hpp;hxx;hm;inl;inc;xsd" - UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"> - <File - RelativePath=".\gstdeinterlace.h"> - </File> - </Filter> - <Filter - Name="Resource Files" - Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx" - UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"> - </Filter> - </Files> - <Globals> - </Globals> -</VisualStudioProject> diff --git a/gst/deinterlace/gstdeinterlace.c b/gst/deinterlace/gstdeinterlace.c deleted file mode 100644 index b2a25afe..00000000 --- a/gst/deinterlace/gstdeinterlace.c +++ /dev/null @@ -1,514 +0,0 @@ -/* GStreamer simple deinterlacing plugin - * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu> - * Copyright (C) 2006-2008 Tim-Philipp Müller <tim centricular net> - * - * 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. - */ -/* based on the Area Based Deinterlacer (for RGB frames) */ -/* (a VirtualDub filter) from Gunnar Thalin <guth@home.se> */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <string.h> -#include "gstdeinterlace.h" -#include <gst/video/video.h> - -/** - * SECTION:element-deinterlace - * - * Adaptively deinterlaces video frames by detecting interlacing artifacts. - * An edge detection matrix is used, with a threshold value. Pixels detected - * as 'interlaced' are replaced with pixels blended from the pixels above and - * below. - * - * <refsect2> - * <title>Example launch line</title> - * |[ - * gst-launch -v videotestsrc ! deinterlace ! ffmpegcolorspace ! xvimagesink - * ]| - * </refsect2> - */ - -GST_DEBUG_CATEGORY_STATIC (deinterlace_debug); -#define GST_CAT_DEFAULT deinterlace_debug - -#define DEFAULT_DI_AREA_ONLY FALSE -#define DEFAULT_NI_AREA_ONLY FALSE -#define DEFAULT_BLEND FALSE -#define DEFAULT_DEINTERLACE TRUE -#define DEFAULT_THRESHOLD 20 -#define DEFAULT_EDGE_DETECT 25 - -enum -{ - ARG_0, - ARG_DI_ONLY, - ARG_NI_ONLY, - ARG_BLEND, - ARG_THRESHOLD, - ARG_EDGE_DETECT, - ARG_DEINTERLACE -}; - -static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ I420, Y42B }")) - ); - -static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ I420, Y42B }")) - ); - -GST_BOILERPLATE (GstDeinterlace, gst_deinterlace, GstBaseTransform, - GST_TYPE_BASE_TRANSFORM); - -static void gst_deinterlace_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_deinterlace_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); -static GstFlowReturn gst_deinterlace_transform_ip (GstBaseTransform * trans, - GstBuffer * buf); -static gboolean gst_deinterlace_stop (GstBaseTransform * trans); -static gboolean gst_deinterlace_set_caps (GstBaseTransform * trans, - GstCaps * incaps, GstCaps * outcaps); -static GstCaps *gst_deinterlace_transform_caps (GstBaseTransform * trans, - GstPadDirection direction, GstCaps * incaps); - -static void -gst_deinterlace_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&src_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&sink_factory)); - - gst_element_class_set_details_simple (element_class, "Deinterlace", - "Filter/Effect/Video", "Deinterlace video", - "Wim Taymans <wim.taymans@gmail.com>"); -} - -static void -gst_deinterlace_class_init (GstDeinterlaceClass * klass) -{ - GObjectClass *gobject_class; - GstBaseTransformClass *basetransform_class; - - gobject_class = (GObjectClass *) klass; - basetransform_class = (GstBaseTransformClass *) klass; - - gobject_class->set_property = gst_deinterlace_set_property; - gobject_class->get_property = gst_deinterlace_get_property; - - /** - * GstDeinterlace:deinterlace: - * - * Turn processing on/off. When false, no modification of the - * video frames occurs and they pass through intact. - */ - g_object_class_install_property (gobject_class, ARG_DEINTERLACE, - g_param_spec_boolean ("deinterlace", "deinterlace", - "turn deinterlacing on/off", DEFAULT_DEINTERLACE, G_PARAM_READWRITE)); - /** - * GstDeinterlace:di-area-only: - * - * When set to true, only areas affected by the deinterlacing are output, - * making it easy to see which regions are being modified. - * - * See Also: #GstDeinterlace:ni-area-only - */ - g_object_class_install_property (gobject_class, ARG_DI_ONLY, - g_param_spec_boolean ("di-area-only", "di-area-only", - "displays deinterlaced areas only", DEFAULT_DI_AREA_ONLY, - G_PARAM_READWRITE)); - /** - * GstDeinterlace:ni-area-only: - * - * When set to true, only areas unaffected by the deinterlacing are output, - * making it easy to see which regions are being preserved intact. - * - * See Also: #GstDeinterlace:di-area-only - */ - g_object_class_install_property (gobject_class, ARG_NI_ONLY, - g_param_spec_boolean ("ni-area-only", "ni-area-only", - "displays non-interlaced areas only", DEFAULT_DI_AREA_ONLY, - G_PARAM_READWRITE)); - /** - * GstDeinterlace:blend: - * - * Change the blending for pixels which are detected as - * 'interlacing artifacts'. When true, the output pixel is a weighted - * average (1,2,1) of the pixel and the pixels above and below it. - * When false, the odd field lines are preserved, and the even field lines - * are averaged from the surrounding pixels above and below (the odd field). - */ - g_object_class_install_property (gobject_class, ARG_BLEND, - g_param_spec_boolean ("blend", "blend", "blend", DEFAULT_BLEND, - G_PARAM_READWRITE)); - /** - * GstDeinterlace:threshold: - * - * Affects the threshold of the edge-detection function used for detecting - * interlacing artifacts. - */ - g_object_class_install_property (gobject_class, ARG_THRESHOLD, - g_param_spec_int ("threshold", "Edge-detection threshold", - "Threshold value for the interlacing artifacts in the output " - "of the edge detection", G_MININT, G_MAXINT, 0, G_PARAM_READWRITE)); - /** - * GstDeinterlace:edge-detect: - * - * Affects the weighting of the edge-detection function used for detecting - * interlacing artifacts. - */ - g_object_class_install_property (gobject_class, ARG_EDGE_DETECT, - g_param_spec_int ("edge-detect", "edge detection weighting", - "Weighting value used for calculating the edge detection matrix", - G_MININT, G_MAXINT, 0, G_PARAM_READWRITE)); - - basetransform_class->transform_ip = - GST_DEBUG_FUNCPTR (gst_deinterlace_transform_ip); - basetransform_class->transform_caps = - GST_DEBUG_FUNCPTR (gst_deinterlace_transform_caps); - basetransform_class->stop = GST_DEBUG_FUNCPTR (gst_deinterlace_stop); - basetransform_class->set_caps = GST_DEBUG_FUNCPTR (gst_deinterlace_set_caps); -} - -static void -gst_deinterlace_init (GstDeinterlace * filter, GstDeinterlaceClass * klass) -{ - filter->show_deinterlaced_area_only = DEFAULT_DI_AREA_ONLY; - filter->show_noninterlaced_area_only = DEFAULT_NI_AREA_ONLY; - filter->blend = DEFAULT_BLEND; - filter->deinterlace = DEFAULT_DEINTERLACE; - filter->threshold = DEFAULT_THRESHOLD; - filter->edge_detect = DEFAULT_EDGE_DETECT; - /*filter->threshold_blend = 0; */ - - filter->src = NULL; - filter->picsize = 0; -} - -static gboolean -gst_deinterlace_stop (GstBaseTransform * trans) -{ - GstDeinterlace *filter; - - filter = GST_DEINTERLACE (trans); - - g_free (filter->src); - filter->src = NULL; - filter->picsize = 0; - filter->width = 0; - filter->height = 0; - - return TRUE; -} - -static GstCaps * -gst_deinterlace_transform_caps (GstBaseTransform * trans, - GstPadDirection direction, GstCaps * incaps) -{ - return gst_caps_ref (incaps); -} - -static gboolean -gst_deinterlace_set_caps (GstBaseTransform * trans, GstCaps * incaps, - GstCaps * outcaps) -{ - GstDeinterlace *filter; - GstVideoFormat fmt; - GstStructure *s; - guint32 fourcc; - gint picsize, w, h; - - filter = GST_DEINTERLACE (trans); - - g_assert (gst_caps_is_equal_fixed (incaps, outcaps)); - - s = gst_caps_get_structure (incaps, 0); - if (!gst_structure_get_int (s, "width", &w) || - !gst_structure_get_int (s, "height", &h) || - !gst_structure_get_fourcc (s, "format", &fourcc)) { - return FALSE; - } - - filter->width = w; - filter->height = h; - filter->fourcc = fourcc; - - GST_DEBUG_OBJECT (filter, "width x height = %d x %d, fourcc: %" - GST_FOURCC_FORMAT, w, h, GST_FOURCC_ARGS (fourcc)); - - fmt = gst_video_format_from_fourcc (fourcc); - - filter->y_stride = gst_video_format_get_row_stride (fmt, 0, w); - filter->u_stride = gst_video_format_get_row_stride (fmt, 1, w); - filter->v_stride = gst_video_format_get_row_stride (fmt, 2, w); - - filter->uv_height = gst_video_format_get_component_height (fmt, 1, h); - - filter->y_off = gst_video_format_get_component_offset (fmt, 0, w, h); - filter->u_off = gst_video_format_get_component_offset (fmt, 1, w, h); - filter->v_off = gst_video_format_get_component_offset (fmt, 2, w, h); - - picsize = gst_video_format_get_size (fmt, w, h); - - if (filter->picsize != picsize) { - filter->picsize = picsize; - g_free (filter->src); /* free + alloc avoids memcpy */ - filter->src = g_malloc0 (filter->picsize); - GST_LOG_OBJECT (filter, "temp buffer size %d", filter->picsize); - } - - return TRUE; -} - -static GstFlowReturn -gst_deinterlace_transform_ip (GstBaseTransform * trans, GstBuffer * buf) -{ - GstDeinterlace *filter; - gboolean bShowDeinterlacedAreaOnly; - gboolean bShowNoninterlacedAreaOnly; - gint y0, y1, y2, y3; - guchar *psrc1, *pdst1, *yuvptr, *src; - gint iInterlaceValue0, iInterlaceValue1, iInterlaceValue2; - gint x, y, p; - gint y_line; - guchar *y_dst, *y_src; - guchar fill_value; - gboolean bBlend; - gboolean bDeinterlace; - gint iThreshold; - gint iEdgeDetect; - gint width, height; - - /* g_assert (gst_buffer_is_writable (buf)); */ - - filter = GST_DEINTERLACE (trans); - - GST_OBJECT_LOCK (filter); - bBlend = filter->blend; - bDeinterlace = filter->deinterlace; - iThreshold = filter->threshold; - iEdgeDetect = filter->edge_detect; - bShowDeinterlacedAreaOnly = filter->show_deinterlaced_area_only; - bShowNoninterlacedAreaOnly = filter->show_noninterlaced_area_only; - GST_OBJECT_UNLOCK (filter); - - src = filter->src; - yuvptr = GST_BUFFER_DATA (buf); - - memcpy (filter->src, yuvptr, filter->picsize); - - - iThreshold = iThreshold * iThreshold * 4; - /* We don't want an integer overflow in the interlace calculation. */ - if (iEdgeDetect > 180) - iEdgeDetect = 180; - iEdgeDetect = iEdgeDetect * iEdgeDetect; - - for (p = 0; p < 3; p++) { - switch (p) { - case 0: - y_dst = yuvptr + filter->y_off; /* dst y pointer */ - y_line = filter->y_stride; - y_src = src + filter->y_off; - width = filter->width; - height = filter->height; - fill_value = 0; - break; - case 1: - y_dst = yuvptr + filter->u_off; /* dst U pointer */ - y_line = filter->u_stride; - y_src = src + filter->u_off; - width = filter->width / 2; - height = filter->uv_height; - fill_value = 128; - break; - case 2: - y_dst = yuvptr + filter->v_off; /* dst V pointer */ - y_line = filter->v_stride; - y_src = src + filter->v_off; - width = filter->width / 2; - height = filter->uv_height; - fill_value = 128; - break; - default: - g_assert_not_reached (); - break; - } - - for (x = 0; x < width; x++) { - pdst1 = y_dst + x; - psrc1 = y_src + x; - iInterlaceValue1 = iInterlaceValue2 = 0; - - for (y = 0; y < height; y++, psrc1 += y_line, pdst1 += y_line) { - /* current line is 1 */ - y0 = y1 = y2 = y3 = *psrc1; - if (y > 0) - y0 = *(psrc1 - y_line); - if (y < (height - 1)) - y2 = *(psrc1 + y_line); - if (y < (height - 2)) - y3 = *(psrc1 + 2 * y_line); - - iInterlaceValue0 = iInterlaceValue1; - iInterlaceValue1 = iInterlaceValue2; - - if (y < height - 1) - iInterlaceValue2 = - (ABS (y1 - y2) * ABS (y3 - y2) - ((iEdgeDetect * (y1 - y3) * (y1 - - y3)) >> 12)) * 10; - else - iInterlaceValue2 = 0; - - if ((iInterlaceValue0 + 2 * iInterlaceValue1 + iInterlaceValue2 > - iThreshold) && (y > 0)) { - if (bShowNoninterlacedAreaOnly) { - *pdst1 = fill_value; /* blank the point and so the interlac area */ - } else { - if (bDeinterlace) { - if (bBlend) { - *pdst1 = (unsigned char) ((y0 + 2 * y1 + y2) >> 2); - } else { - /* this method seems to work better than blending if the */ - /* quality is pretty bad and the half pics don't fit together */ - if ((y % 2) == 1) { /* if odd simply copy the value */ - *pdst1 = *psrc1; - } else { /* if even interpolate the line (upper + lower)/2 */ - *pdst1 = (unsigned char) ((y0 + y2) >> 1); - } - } - } else { - *pdst1 = *psrc1; - } - } - - } else { - /* so we went below the treshold and therefore we don't have to */ - /* change anything */ - if (bShowDeinterlacedAreaOnly) { - /* this is for testing to see how we should tune the treshhold */ - /* and shows as the things that haven't change because the */ - /* threshold was to low?? (or shows that everything is ok :-) */ - *pdst1 = fill_value; /* blank the point and so the non-interlac area */ - } else { - *pdst1 = *psrc1; - } - } - } - } - } - - return GST_FLOW_OK; -} - -static void -gst_deinterlace_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstDeinterlace *filter; - - filter = GST_DEINTERLACE (object); - - GST_OBJECT_LOCK (filter); - switch (prop_id) { - case ARG_DEINTERLACE: - filter->deinterlace = g_value_get_boolean (value); - break; - case ARG_DI_ONLY: - filter->show_deinterlaced_area_only = g_value_get_boolean (value); - break; - case ARG_NI_ONLY: - filter->show_noninterlaced_area_only = g_value_get_boolean (value); - break; - case ARG_BLEND: - filter->blend = g_value_get_boolean (value); - break; - case ARG_THRESHOLD: - filter->threshold = g_value_get_int (value); - break; - case ARG_EDGE_DETECT: - filter->edge_detect = g_value_get_int (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - GST_OBJECT_UNLOCK (filter); -} - -static void -gst_deinterlace_get_property (GObject * object, guint prop_id, GValue * value, - GParamSpec * pspec) -{ - GstDeinterlace *filter; - - filter = GST_DEINTERLACE (object); - - GST_OBJECT_LOCK (filter); - switch (prop_id) { - case ARG_DEINTERLACE: - g_value_set_boolean (value, filter->deinterlace); - break; - case ARG_DI_ONLY: - g_value_set_boolean (value, filter->show_deinterlaced_area_only); - break; - case ARG_NI_ONLY: - g_value_set_boolean (value, filter->show_noninterlaced_area_only); - break; - case ARG_BLEND: - g_value_set_boolean (value, filter->blend); - break; - case ARG_THRESHOLD: - g_value_set_int (value, filter->threshold); - break; - case ARG_EDGE_DETECT: - g_value_set_int (value, filter->edge_detect); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - GST_OBJECT_UNLOCK (filter); -} - -static gboolean -plugin_init (GstPlugin * plugin) -{ - GST_DEBUG_CATEGORY_INIT (deinterlace_debug, "deinterlace", 0, - "deinterlace element"); - - if (!gst_element_register (plugin, "deinterlace", GST_RANK_NONE, - gst_deinterlace_get_type ())) - return FALSE; - - return TRUE; -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "gstinterlace", - "Deinterlace video", plugin_init, PACKAGE_VERSION, "LGPL", GST_PACKAGE_NAME, - GST_PACKAGE_ORIGIN); diff --git a/gst/deinterlace/gstdeinterlace.h b/gst/deinterlace/gstdeinterlace.h deleted file mode 100644 index faa8e811..00000000 --- a/gst/deinterlace/gstdeinterlace.h +++ /dev/null @@ -1,74 +0,0 @@ -/* GStreamer simple deinterlacing plugin - * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu> - * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net> - * - * 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_DEINTERLACE_H__ -#define __GST_DEINTERLACE_H__ - -#include <gst/gst.h> -#include <gst/base/gstbasetransform.h> - -G_BEGIN_DECLS - -#define GST_TYPE_DEINTERLACE (gst_deinterlace_get_type()) -#define GST_DEINTERLACE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DEINTERLACE,GstDeinterlace)) -#define GST_DEINTERLACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DEINTERLACE,GstDeinterlaceClass)) -#define GST_IS_DEINTERLACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DEINTERLACE)) -#define GST_IS_DEINTERLACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DEINTERLACE)) - -typedef struct _GstDeinterlace GstDeinterlace; -typedef struct _GstDeinterlaceClass GstDeinterlaceClass; - -struct _GstDeinterlace { - GstBaseTransform basetransform; - - /*< private >*/ - gint width; - gint height; - gint uv_height; - guint32 fourcc; - - gboolean show_deinterlaced_area_only; - gboolean show_noninterlaced_area_only; - gboolean blend; - gboolean deinterlace; - gint threshold_blend; /* here we start blending */ - gint threshold; /* here we start interpolating TODO FIXME */ - gint edge_detect; - - gint picsize; - gint y_stride; - gint u_stride; - gint v_stride; - gint y_off; - gint u_off; - gint v_off; - - guchar *src; -}; - -struct _GstDeinterlaceClass { - GstBaseTransformClass basetransformclass; -}; - -GType gst_deinterlace_get_type (void); - -G_END_DECLS - -#endif /* __GST_DEINTERLACE_H__ */ diff --git a/gst/deinterlace2/Makefile.am b/gst/deinterlace2/Makefile.am deleted file mode 100644 index 1de59919..00000000 --- a/gst/deinterlace2/Makefile.am +++ /dev/null @@ -1,48 +0,0 @@ -plugin_LTLIBRARIES = libgstdeinterlace2.la - -libgstdeinterlace2_la_SOURCES = \ - gstdeinterlace2.c \ - tvtime/greedy.c \ - tvtime/greedyh.c \ - tvtime/vfir.c \ - tvtime/tomsmocomp.c \ - tvtime/weavetff.c \ - tvtime/weavebff.c \ - tvtime/weave.c \ - tvtime/linear.c \ - tvtime/linearblend.c \ - tvtime/scalerbob.c - -libgstdeinterlace2_la_CFLAGS = $(GST_CFLAGS) \ - $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(LIBOIL_CFLAGS) -libgstdeinterlace2_la_LIBADD = $(GST_LIBS) \ - $(GST_PLUGINS_BASE_LIBS) -lgstvideo-$(GST_MAJORMINOR) $(GST_BASE_LIBS) $(LIBOIL_LIBS) -libgstdeinterlace2_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgstdeinterlace2_la_LIBTOOLFLAGS = --tag=disable-static - -noinst_HEADERS = \ - gstdeinterlace2.h \ - tvtime/mmx.h \ - tvtime/sse.h \ - tvtime/greedyh.asm \ - tvtime/greedyhmacros.h \ - tvtime/plugins.h \ - tvtime/x86-64_macros.inc \ - tvtime/tomsmocomp/SearchLoop0A.inc \ - tvtime/tomsmocomp/SearchLoopBottom.inc \ - tvtime/tomsmocomp/SearchLoopEdgeA8.inc \ - tvtime/tomsmocomp/SearchLoopEdgeA.inc \ - tvtime/tomsmocomp/SearchLoopOddA2.inc \ - tvtime/tomsmocomp/SearchLoopOddA6.inc \ - tvtime/tomsmocomp/SearchLoopOddAH2.inc \ - tvtime/tomsmocomp/SearchLoopOddAH.inc \ - tvtime/tomsmocomp/SearchLoopOddA.inc \ - tvtime/tomsmocomp/SearchLoopTop.inc \ - tvtime/tomsmocomp/SearchLoopVAH.inc \ - tvtime/tomsmocomp/SearchLoopVA.inc \ - tvtime/tomsmocomp/StrangeBob.inc \ - tvtime/tomsmocomp/TomsMoCompAll2.inc \ - tvtime/tomsmocomp/TomsMoCompAll.inc \ - tvtime/tomsmocomp/tomsmocompmacros.h \ - tvtime/tomsmocomp/WierdBob.inc - diff --git a/gst/deinterlace2/gstdeinterlace2.c b/gst/deinterlace2/gstdeinterlace2.c deleted file mode 100644 index b293b43e..00000000 --- a/gst/deinterlace2/gstdeinterlace2.c +++ /dev/null @@ -1,1509 +0,0 @@ -/* - * GStreamer - * Copyright (C) 2005 Martin Eikermann <meiker@upb.de> - * Copyright (C) 2008-2009 Sebastian Dröge <slomo@collabora.co.uk> - * - * 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. - */ - -/** - * SECTION:element-deinterlace2 - * - * deinterlace2 deinterlaces interlaced video frames to progressive video frames. - * For this different algorithms can be selected which will be described later. - * - * <refsect2> - * <title>Example launch line</title> - * |[ - * gst-launch -v filesrc location=/path/to/file ! decodebin2 ! ffmpegcolorspace ! deinterlace2 ! ffmpegcolorspace ! autovideosink - * ]| This pipeline deinterlaces a video file with the default deinterlacing options. - * </refsect2> - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <gst/gst.h> -#include <gst/video/video.h> -#include <liboil/liboil.h> - -#include "gstdeinterlace2.h" -#include "tvtime/plugins.h" - -#include <string.h> - -GST_DEBUG_CATEGORY_STATIC (deinterlace2_debug); -#define GST_CAT_DEFAULT (deinterlace2_debug) - -/* Object signals and args */ -enum -{ - LAST_SIGNAL -}; - -/* Properties */ - -#define DEFAULT_MODE GST_DEINTERLACE2_MODE_INTERLACED -#define DEFAULT_METHOD GST_DEINTERLACE2_GREEDY_H -#define DEFAULT_FIELDS GST_DEINTERLACE2_ALL -#define DEFAULT_FIELD_LAYOUT GST_DEINTERLACE2_LAYOUT_AUTO - -enum -{ - PROP_0, - PROP_MODE, - PROP_METHOD, - PROP_FIELDS, - PROP_FIELD_LAYOUT, - PROP_LAST -}; - -G_DEFINE_TYPE (GstDeinterlaceMethod, gst_deinterlace_method, GST_TYPE_OBJECT); - -static void -gst_deinterlace_method_class_init (GstDeinterlaceMethodClass * klass) -{ - -} - -static void -gst_deinterlace_method_init (GstDeinterlaceMethod * self) -{ - -} - -static void -gst_deinterlace_method_deinterlace_frame (GstDeinterlaceMethod * self, - GstDeinterlace2 * parent, GstBuffer * outbuf) -{ - GstDeinterlaceMethodClass *klass = GST_DEINTERLACE_METHOD_GET_CLASS (self); - - klass->deinterlace_frame (self, parent, outbuf); -} - -static gint -gst_deinterlace_method_get_fields_required (GstDeinterlaceMethod * self) -{ - GstDeinterlaceMethodClass *klass = GST_DEINTERLACE_METHOD_GET_CLASS (self); - - return klass->fields_required; -} - -static gint -gst_deinterlace_method_get_latency (GstDeinterlaceMethod * self) -{ - GstDeinterlaceMethodClass *klass = GST_DEINTERLACE_METHOD_GET_CLASS (self); - - return klass->latency; -} - - -G_DEFINE_TYPE (GstDeinterlaceSimpleMethod, gst_deinterlace_simple_method, - GST_TYPE_DEINTERLACE_METHOD); - -static void -gst_deinterlace_simple_method_interpolate_scanline (GstDeinterlaceMethod * self, - GstDeinterlace2 * parent, guint8 * out, - GstDeinterlaceScanlineData * scanlines, gint width) -{ - oil_memcpy (out, scanlines->m1, parent->row_stride); -} - -static void -gst_deinterlace_simple_method_copy_scanline (GstDeinterlaceMethod * self, - GstDeinterlace2 * parent, guint8 * out, - GstDeinterlaceScanlineData * scanlines, gint width) -{ - oil_memcpy (out, scanlines->m0, parent->row_stride); -} - -static void -gst_deinterlace_simple_method_deinterlace_frame (GstDeinterlaceMethod * self, - GstDeinterlace2 * parent, GstBuffer * outbuf) -{ - GstDeinterlaceSimpleMethodClass *dsm_class = - GST_DEINTERLACE_SIMPLE_METHOD_GET_CLASS (self); - GstDeinterlaceMethodClass *dm_class = GST_DEINTERLACE_METHOD_GET_CLASS (self); - GstDeinterlaceScanlineData scanlines; - guint8 *out = GST_BUFFER_DATA (outbuf); - guint8 *field0 = NULL, *field1 = NULL, *field2 = NULL, *field3 = NULL; - gint cur_field_idx = parent->history_count - dm_class->fields_required; - guint cur_field_flags = parent->field_history[cur_field_idx].flags; - gint line; - - field0 = GST_BUFFER_DATA (parent->field_history[cur_field_idx].buf); - - g_assert (dm_class->fields_required <= 4); - - if (dm_class->fields_required >= 2) - field1 = GST_BUFFER_DATA (parent->field_history[cur_field_idx + 1].buf); - if (dm_class->fields_required >= 3) - field2 = GST_BUFFER_DATA (parent->field_history[cur_field_idx + 2].buf); - if (dm_class->fields_required >= 4) - field3 = GST_BUFFER_DATA (parent->field_history[cur_field_idx + 3].buf); - - - if (cur_field_flags == PICTURE_INTERLACED_BOTTOM) { - /* double the first scanline of the bottom field */ - oil_memcpy (out, field0, parent->row_stride); - out += parent->row_stride; - } - - oil_memcpy (out, field0, parent->row_stride); - out += parent->row_stride; - - for (line = 2; line <= parent->field_height; line++) { - - memset (&scanlines, 0, sizeof (scanlines)); - scanlines.bottom_field = (cur_field_flags == PICTURE_INTERLACED_BOTTOM); - - /* interp. scanline */ - scanlines.t0 = field0; - scanlines.b0 = field0 + parent->field_stride; - - if (field1 != NULL) { - scanlines.tt1 = field1; - scanlines.m1 = field1 + parent->field_stride; - scanlines.bb1 = field1 + parent->field_stride * 2; - field1 += parent->field_stride; - } - - if (field2 != NULL) { - scanlines.t2 = field2; - scanlines.b2 = field2 + parent->field_stride; - } - - if (field3 != NULL) { - scanlines.tt3 = field3; - scanlines.m3 = field3 + parent->field_stride; - scanlines.bb3 = field3 + parent->field_stride * 2; - field3 += parent->field_stride; - } - - /* set valid data for corner cases */ - if (line == 2) { - scanlines.tt1 = scanlines.bb1; - scanlines.tt3 = scanlines.bb3; - } else if (line == parent->field_height) { - scanlines.bb1 = scanlines.tt1; - scanlines.bb3 = scanlines.tt3; - } - - dsm_class->interpolate_scanline (self, parent, out, &scanlines, - parent->frame_width); - out += parent->row_stride; - - memset (&scanlines, 0, sizeof (scanlines)); - scanlines.bottom_field = (cur_field_flags == PICTURE_INTERLACED_BOTTOM); - - /* copy a scanline */ - scanlines.tt0 = field0; - scanlines.m0 = field0 + parent->field_stride; - scanlines.bb0 = field0 + parent->field_stride * 2; - field0 += parent->field_stride; - - if (field1 != NULL) { - scanlines.t1 = field1; - scanlines.b1 = field1 + parent->field_stride; - } - - if (field2 != NULL) { - scanlines.tt2 = field2; - scanlines.m2 = field2 + parent->field_stride; - scanlines.bb2 = field2 + parent->field_stride * 2; - field2 += parent->field_stride; - } - - if (field3 != NULL) { - scanlines.t3 = field3; - scanlines.b3 = field3 + parent->field_stride; - } - - /* set valid data for corner cases */ - if (line == parent->field_height) { - scanlines.bb0 = scanlines.tt0; - scanlines.b1 = scanlines.t1; - scanlines.bb2 = scanlines.tt2; - scanlines.b3 = scanlines.t3; - } - - dsm_class->copy_scanline (self, parent, out, &scanlines, - parent->frame_width); - out += parent->row_stride; - } - - if (cur_field_flags == PICTURE_INTERLACED_TOP) { - /* double the last scanline of the top field */ - oil_memcpy (out, field0, parent->row_stride); - } -} - -static void -gst_deinterlace_simple_method_class_init (GstDeinterlaceSimpleMethodClass * - klass) -{ - GstDeinterlaceMethodClass *dm_class = (GstDeinterlaceMethodClass *) klass; - - dm_class->deinterlace_frame = gst_deinterlace_simple_method_deinterlace_frame; - dm_class->fields_required = 2; - - klass->interpolate_scanline = - gst_deinterlace_simple_method_interpolate_scanline; - klass->copy_scanline = gst_deinterlace_simple_method_copy_scanline; -} - -static void -gst_deinterlace_simple_method_init (GstDeinterlaceSimpleMethod * self) -{ -} - -#define GST_TYPE_DEINTERLACE2_METHODS (gst_deinterlace2_methods_get_type ()) -static GType -gst_deinterlace2_methods_get_type (void) -{ - static GType deinterlace2_methods_type = 0; - - static const GEnumValue methods_types[] = { - {GST_DEINTERLACE2_TOMSMOCOMP, "Motion Adaptive: Motion Search", - "tomsmocomp"}, - {GST_DEINTERLACE2_GREEDY_H, "Motion Adaptive: Advanced Detection", - "greedyh"}, - {GST_DEINTERLACE2_GREEDY_L, "Motion Adaptive: Simple Detection", "greedyl"}, - {GST_DEINTERLACE2_VFIR, "Blur Vertical", "vfir"}, - {GST_DEINTERLACE2_LINEAR, "Television: Full resolution", "linear"}, - {GST_DEINTERLACE2_LINEAR_BLEND, "Blur: Temporal", "linearblend"}, - {GST_DEINTERLACE2_SCALER_BOB, "Double lines", "scalerbob"}, - {GST_DEINTERLACE2_WEAVE, "Weave", "weave"}, - {GST_DEINTERLACE2_WEAVE_TFF, "Progressive: Top Field First", "weavetff"}, - {GST_DEINTERLACE2_WEAVE_BFF, "Progressive: Bottom Field First", "weavebff"}, - {0, NULL, NULL}, - }; - - if (!deinterlace2_methods_type) { - deinterlace2_methods_type = - g_enum_register_static ("GstDeinterlace2Methods", methods_types); - } - return deinterlace2_methods_type; -} - -#define GST_TYPE_DEINTERLACE2_FIELDS (gst_deinterlace2_fields_get_type ()) -static GType -gst_deinterlace2_fields_get_type (void) -{ - static GType deinterlace2_fields_type = 0; - - static const GEnumValue fields_types[] = { - {GST_DEINTERLACE2_ALL, "All fields", "all"}, - {GST_DEINTERLACE2_TF, "Top fields only", "top"}, - {GST_DEINTERLACE2_BF, "Bottom fields only", "bottom"}, - {0, NULL, NULL}, - }; - - if (!deinterlace2_fields_type) { - deinterlace2_fields_type = - g_enum_register_static ("GstDeinterlace2Fields", fields_types); - } - return deinterlace2_fields_type; -} - -#define GST_TYPE_DEINTERLACE2_FIELD_LAYOUT (gst_deinterlace2_field_layout_get_type ()) -static GType -gst_deinterlace2_field_layout_get_type (void) -{ - static GType deinterlace2_field_layout_type = 0; - - static const GEnumValue field_layout_types[] = { - {GST_DEINTERLACE2_LAYOUT_AUTO, "Auto detection", "auto"}, - {GST_DEINTERLACE2_LAYOUT_TFF, "Top field first", "tff"}, - {GST_DEINTERLACE2_LAYOUT_BFF, "Bottom field first", "bff"}, - {0, NULL, NULL}, - }; - - if (!deinterlace2_field_layout_type) { - deinterlace2_field_layout_type = - g_enum_register_static ("GstDeinterlace2FieldLayout", - field_layout_types); - } - return deinterlace2_field_layout_type; -} - -#define GST_TYPE_DEINTERLACE2_MODES (gst_deinterlace2_modes_get_type ()) -static GType -gst_deinterlace2_modes_get_type (void) -{ - static GType deinterlace2_modes_type = 0; - - static const GEnumValue modes_types[] = { - {GST_DEINTERLACE2_MODE_AUTO, "Auto detection", "auto"}, - {GST_DEINTERLACE2_MODE_INTERLACED, "Enfore deinterlacing", "interlaced"}, - {0, NULL, NULL}, - }; - - if (!deinterlace2_modes_type) { - deinterlace2_modes_type = - g_enum_register_static ("GstDeinterlace2Modes", modes_types); - } - return deinterlace2_modes_type; -} - -static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("YUY2") ";" - GST_VIDEO_CAPS_YUV ("YVYU")) - ); - -static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("YUY2") ";" - GST_VIDEO_CAPS_YUV ("YVYU")) - ); - -static void gst_deinterlace2_finalize (GObject * self); -static void gst_deinterlace2_set_property (GObject * self, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_deinterlace2_get_property (GObject * self, guint prop_id, - GValue * value, GParamSpec * pspec); - -static GstCaps *gst_deinterlace2_getcaps (GstPad * pad); -static gboolean gst_deinterlace2_setcaps (GstPad * pad, GstCaps * caps); -static gboolean gst_deinterlace2_sink_event (GstPad * pad, GstEvent * event); -static GstFlowReturn gst_deinterlace2_chain (GstPad * pad, GstBuffer * buffer); -static GstStateChangeReturn gst_deinterlace2_change_state (GstElement * element, - GstStateChange transition); - -static gboolean gst_deinterlace2_src_event (GstPad * pad, GstEvent * event); -static gboolean gst_deinterlace2_src_query (GstPad * pad, GstQuery * query); -static const GstQueryType *gst_deinterlace2_src_query_types (GstPad * pad); - -static void gst_deinterlace2_reset (GstDeinterlace2 * self); - -static void gst_deinterlace2_child_proxy_interface_init (gpointer g_iface, - gpointer iface_data); - -static void -_do_init (GType object_type) -{ - const GInterfaceInfo child_proxy_interface_info = { - (GInterfaceInitFunc) gst_deinterlace2_child_proxy_interface_init, - NULL, /* interface_finalize */ - NULL /* interface_data */ - }; - - g_type_add_interface_static (object_type, GST_TYPE_CHILD_PROXY, - &child_proxy_interface_info); -} - -GST_BOILERPLATE_FULL (GstDeinterlace2, gst_deinterlace2, GstElement, - GST_TYPE_ELEMENT, _do_init); - -static void -gst_deinterlace2_set_method (GstDeinterlace2 * self, - GstDeinterlace2Methods method) -{ - - if (self->method) { - gst_child_proxy_child_removed (GST_OBJECT (self), - GST_OBJECT (self->method)); - gst_object_unparent (GST_OBJECT (self->method)); - self->method = NULL; - } - - switch (method) { - case GST_DEINTERLACE2_TOMSMOCOMP: - self->method = g_object_new (GST_TYPE_DEINTERLACE_TOMSMOCOMP, NULL); - break; - case GST_DEINTERLACE2_GREEDY_H: - self->method = g_object_new (GST_TYPE_DEINTERLACE_GREEDY_H, NULL); - break; - case GST_DEINTERLACE2_GREEDY_L: - self->method = g_object_new (GST_TYPE_DEINTERLACE_GREEDY_L, NULL); - break; - case GST_DEINTERLACE2_VFIR: - self->method = g_object_new (GST_TYPE_DEINTERLACE_VFIR, NULL); - break; - case GST_DEINTERLACE2_LINEAR: - self->method = g_object_new (GST_TYPE_DEINTERLACE_LINEAR, NULL); - break; - case GST_DEINTERLACE2_LINEAR_BLEND: - self->method = g_object_new (GST_TYPE_DEINTERLACE_LINEAR_BLEND, NULL); - break; - case GST_DEINTERLACE2_SCALER_BOB: - self->method = g_object_new (GST_TYPE_DEINTERLACE_SCALER_BOB, NULL); - break; - case GST_DEINTERLACE2_WEAVE: - self->method = g_object_new (GST_TYPE_DEINTERLACE_WEAVE, NULL); - break; - case GST_DEINTERLACE2_WEAVE_TFF: - self->method = g_object_new (GST_TYPE_DEINTERLACE_WEAVE_TFF, NULL); - break; - case GST_DEINTERLACE2_WEAVE_BFF: - self->method = g_object_new (GST_TYPE_DEINTERLACE_WEAVE_BFF, NULL); - break; - default: - GST_WARNING_OBJECT (self, "Invalid Deinterlacer Method"); - return; - } - - self->method_id = method; - - gst_object_set_name (GST_OBJECT (self->method), "method"); - gst_object_set_parent (GST_OBJECT (self->method), GST_OBJECT (self)); - gst_child_proxy_child_added (GST_OBJECT (self), GST_OBJECT (self->method)); -} - -static void -gst_deinterlace2_base_init (gpointer klass) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&src_templ)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&sink_templ)); - - gst_element_class_set_details_simple (element_class, - "Deinterlacer", - "Filter/Video", - "Deinterlace Methods ported from DScaler/TvTime", - "Martin Eikermann <meiker@upb.de>, " - "Sebastian Dröge <slomo@circular-chaos.org>"); -} - -static void -gst_deinterlace2_class_init (GstDeinterlace2Class * klass) -{ - GObjectClass *gobject_class = (GObjectClass *) klass; - - GstElementClass *element_class = (GstElementClass *) klass; - - gobject_class->set_property = gst_deinterlace2_set_property; - gobject_class->get_property = gst_deinterlace2_get_property; - gobject_class->finalize = gst_deinterlace2_finalize; - - /** - * GstDeinterlace2:mode - * - * This selects whether the deinterlacing methods should - * always be applied or if they should only be applied - * on content that has the "interlaced" flag on the caps. - * - */ - g_object_class_install_property (gobject_class, PROP_MODE, - g_param_spec_enum ("mode", - "Mode", - "Deinterlace Mode", - GST_TYPE_DEINTERLACE2_MODES, - DEFAULT_MODE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) - ); - - /** - * GstDeinterlace2:method - * - * Selects the different deinterlacing algorithms that can be used. - * These provide different quality and CPU usage. - * - * Some methods provide parameters which can be set by getting - * the "method" child via the #GstChildProxy interface and - * setting the appropiate properties on it. - * - * <itemizedlist> - * <listitem> - * <para> - * tomsmocomp - * Motion Adaptive: Motion Search - * </para> - * </listitem> - * <listitem> - * <para> - * greedyh - * Motion Adaptive: Advanced Detection - * </para> - * </listitem> - * <listitem> - * <para> - * greedyl - * Motion Adaptive: Simple Detection - * </para> - * </listitem> - * <listitem> - * <para> - * vfir - * Blur vertical - * </para> - * </listitem> - * <listitem> - * <para> - * linear - * Linear interpolation - * </para> - * </listitem> - * <listitem> - * <para> - * linearblend - * Linear interpolation in time domain - * </para> - * </listitem> - * <listitem> - * <para> - * scalerbob - * Double lines - * </para> - * </listitem> - * <listitem> - * <para> - * weave - * Weave - * </para> - * </listitem> - * <listitem> - * <para> - * weavetff - * Progressive: Top Field First - * </para> - * </listitem> - * <listitem> - * <para> - * weavebff - * Progressive: Bottom Field First - * </para> - * </listitem> - * </itemizedlist> - */ - g_object_class_install_property (gobject_class, PROP_METHOD, - g_param_spec_enum ("method", - "Method", - "Deinterlace Method", - GST_TYPE_DEINTERLACE2_METHODS, - DEFAULT_METHOD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) - ); - - /** - * GstDeinterlace2:fields - * - * This selects which fields should be output. If "all" is selected - * the output framerate will be double. - * - */ - g_object_class_install_property (gobject_class, PROP_FIELDS, - g_param_spec_enum ("fields", - "fields", - "Fields to use for deinterlacing", - GST_TYPE_DEINTERLACE2_FIELDS, - DEFAULT_FIELDS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) - ); - - /** - * GstDeinterlace2:layout - * - * This selects which fields is the first in time. - * - */ - g_object_class_install_property (gobject_class, PROP_FIELD_LAYOUT, - g_param_spec_enum ("tff", - "tff", - "Deinterlace top field first", - GST_TYPE_DEINTERLACE2_FIELD_LAYOUT, - DEFAULT_FIELD_LAYOUT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) - ); - - element_class->change_state = - GST_DEBUG_FUNCPTR (gst_deinterlace2_change_state); -} - -static GstObject * -gst_deinterlace2_child_proxy_get_child_by_index (GstChildProxy * child_proxy, - guint index) -{ - GstDeinterlace2 *self = GST_DEINTERLACE2 (child_proxy); - - g_return_val_if_fail (index == 0, NULL); - - return gst_object_ref (self->method); -} - -static guint -gst_deinterlace2_child_proxy_get_children_count (GstChildProxy * child_proxy) -{ - return 1; -} - -static void -gst_deinterlace2_child_proxy_interface_init (gpointer g_iface, - gpointer iface_data) -{ - GstChildProxyInterface *iface = g_iface; - - iface->get_child_by_index = gst_deinterlace2_child_proxy_get_child_by_index; - iface->get_children_count = gst_deinterlace2_child_proxy_get_children_count; -} - -static void -gst_deinterlace2_init (GstDeinterlace2 * self, GstDeinterlace2Class * klass) -{ - self->sinkpad = gst_pad_new_from_static_template (&sink_templ, "sink"); - gst_pad_set_chain_function (self->sinkpad, - GST_DEBUG_FUNCPTR (gst_deinterlace2_chain)); - gst_pad_set_event_function (self->sinkpad, - GST_DEBUG_FUNCPTR (gst_deinterlace2_sink_event)); - gst_pad_set_setcaps_function (self->sinkpad, - GST_DEBUG_FUNCPTR (gst_deinterlace2_setcaps)); - gst_pad_set_getcaps_function (self->sinkpad, - GST_DEBUG_FUNCPTR (gst_deinterlace2_getcaps)); - gst_element_add_pad (GST_ELEMENT (self), self->sinkpad); - - self->srcpad = gst_pad_new_from_static_template (&src_templ, "src"); - gst_pad_set_event_function (self->srcpad, - GST_DEBUG_FUNCPTR (gst_deinterlace2_src_event)); - gst_pad_set_query_type_function (self->srcpad, - GST_DEBUG_FUNCPTR (gst_deinterlace2_src_query_types)); - gst_pad_set_query_function (self->srcpad, - GST_DEBUG_FUNCPTR (gst_deinterlace2_src_query)); - gst_pad_set_setcaps_function (self->srcpad, - GST_DEBUG_FUNCPTR (gst_deinterlace2_setcaps)); - gst_pad_set_getcaps_function (self->srcpad, - GST_DEBUG_FUNCPTR (gst_deinterlace2_getcaps)); - gst_element_add_pad (GST_ELEMENT (self), self->srcpad); - - gst_element_no_more_pads (GST_ELEMENT (self)); - - self->mode = DEFAULT_MODE; - gst_deinterlace2_set_method (self, DEFAULT_METHOD); - self->fields = DEFAULT_FIELDS; - self->field_layout = DEFAULT_FIELD_LAYOUT; - - gst_deinterlace2_reset (self); -} - -static void -gst_deinterlace2_reset_history (GstDeinterlace2 * self) -{ - gint i; - - for (i = 0; i < self->history_count; i++) { - if (self->field_history[i].buf) { - gst_buffer_unref (self->field_history[i].buf); - self->field_history[i].buf = NULL; - } - } - memset (self->field_history, 0, MAX_FIELD_HISTORY * sizeof (GstPicture)); - self->history_count = 0; -} - -static void -gst_deinterlace2_reset (GstDeinterlace2 * self) -{ - self->row_stride = 0; - self->frame_width = 0; - self->frame_height = 0; - self->frame_rate_n = 0; - self->frame_rate_d = 0; - self->field_height = 0; - self->field_stride = 0; - - gst_deinterlace2_reset_history (self); -} - -static void -gst_deinterlace2_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstDeinterlace2 *self; - - g_return_if_fail (GST_IS_DEINTERLACE2 (object)); - self = GST_DEINTERLACE2 (object); - - switch (prop_id) { - case PROP_MODE: - if (GST_STATE (self) >= GST_STATE_PAUSED) { - g_warning ("Setting the 'mode' property is only allowed in " - "states other than PAUSED and PLAYING"); - } else { - self->mode = g_value_get_enum (value); - } - break; - case PROP_METHOD: - gst_deinterlace2_set_method (self, g_value_get_enum (value)); - break; - case PROP_FIELDS:{ - gint oldfields; - - GST_OBJECT_LOCK (self); - oldfields = self->fields; - self->fields = g_value_get_enum (value); - if (self->fields != oldfields && GST_PAD_CAPS (self->srcpad)) - gst_deinterlace2_setcaps (self->sinkpad, GST_PAD_CAPS (self->sinkpad)); - GST_OBJECT_UNLOCK (self); - break; - } - case PROP_FIELD_LAYOUT: - self->field_layout = g_value_get_enum (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); - } - -} - -static void -gst_deinterlace2_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstDeinterlace2 *self; - - g_return_if_fail (GST_IS_DEINTERLACE2 (object)); - self = GST_DEINTERLACE2 (object); - - switch (prop_id) { - case PROP_MODE: - g_value_set_enum (value, self->mode); - break; - case PROP_METHOD: - g_value_set_enum (value, self->method_id); - break; - case PROP_FIELDS: - g_value_set_enum (value, self->fields); - break; - case PROP_FIELD_LAYOUT: - g_value_set_enum (value, self->field_layout); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); - } -} - -static void -gst_deinterlace2_finalize (GObject * object) -{ - GstDeinterlace2 *self = GST_DEINTERLACE2 (object); - - gst_deinterlace2_reset (self); - - if (self->method) { - gst_object_unparent (GST_OBJECT (self->method)); - self->method = NULL; - } - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static GstBuffer * -gst_deinterlace2_pop_history (GstDeinterlace2 * self) -{ - GstBuffer *buffer = NULL; - - g_assert (self->history_count > 0); - - buffer = self->field_history[self->history_count - 1].buf; - - self->history_count--; - GST_DEBUG_OBJECT (self, "pop, size(history): %d", self->history_count); - - return buffer; -} - -#if 0 -static GstBuffer * -gst_deinterlace2_head_history (GstDeinterlace2 * self) -{ - return self->field_history[self->history_count - 1].buf; -} -#endif - - -/* invariant: field with smallest timestamp is self->field_history[self->history_count-1] - -*/ - -static void -gst_deinterlace2_push_history (GstDeinterlace2 * self, GstBuffer * buffer) -{ - int i = 1; - GstClockTime timestamp; - GstDeinterlace2FieldLayout field_layout = self->field_layout; - gboolean repeated = GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_RFF); - gboolean tff = GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_TFF); - gboolean onefield = - GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_ONEFIELD); - GstBuffer *field1, *field2; - guint fields_to_push = (onefield) ? 1 : (!repeated) ? 2 : 3; - gint field1_flags, field2_flags; - - g_assert (self->history_count < MAX_FIELD_HISTORY - fields_to_push); - - for (i = MAX_FIELD_HISTORY - 1; i >= fields_to_push; i--) { - self->field_history[i].buf = self->field_history[i - fields_to_push].buf; - self->field_history[i].flags = - self->field_history[i - fields_to_push].flags; - } - - if (field_layout == GST_DEINTERLACE2_LAYOUT_AUTO) { - if (!self->interlaced) { - GST_WARNING_OBJECT (self, "Can't detect field layout -- assuming TFF"); - field_layout = GST_DEINTERLACE2_LAYOUT_TFF; - } else if (tff) { - field_layout = GST_DEINTERLACE2_LAYOUT_TFF; - } else { - field_layout = GST_DEINTERLACE2_LAYOUT_BFF; - } - } - - if (field_layout == GST_DEINTERLACE2_LAYOUT_TFF) { - GST_DEBUG_OBJECT (self, "Top field first"); - field1 = gst_buffer_ref (buffer); - field1_flags = PICTURE_INTERLACED_TOP; - field2 = gst_buffer_create_sub (buffer, self->row_stride, - GST_BUFFER_SIZE (buffer) - self->row_stride); - field2_flags = PICTURE_INTERLACED_BOTTOM; - } else { - GST_DEBUG_OBJECT (self, "Bottom field first"); - field1 = gst_buffer_create_sub (buffer, self->row_stride, - GST_BUFFER_SIZE (buffer) - self->row_stride); - field1_flags = PICTURE_INTERLACED_BOTTOM; - field2 = gst_buffer_ref (buffer); - field2_flags = PICTURE_INTERLACED_TOP; - } - - /* Timestamps are assigned to the field buffers under the assumption that - the timestamp of the buffer equals the first fields timestamp */ - - timestamp = GST_BUFFER_TIMESTAMP (buffer); - GST_BUFFER_TIMESTAMP (field1) = timestamp; - GST_BUFFER_TIMESTAMP (field2) = timestamp + self->field_duration; - if (repeated) - GST_BUFFER_TIMESTAMP (field2) += self->field_duration; - - if (repeated) { - self->field_history[0].buf = field2; - self->field_history[0].flags = field2_flags; - self->field_history[1].buf = gst_buffer_ref (field1); - GST_BUFFER_TIMESTAMP (self->field_history[1].buf) += self->field_duration; - self->field_history[1].flags = field1_flags; - self->field_history[2].buf = field1; - self->field_history[2].flags = field1_flags; - } else if (!onefield) { - self->field_history[0].buf = field2; - self->field_history[0].flags = field2_flags; - self->field_history[1].buf = field1; - self->field_history[1].flags = field1_flags; - } else { /* onefield */ - self->field_history[0].buf = field1; - self->field_history[0].flags = field1_flags; - gst_buffer_unref (field2); - } - - self->history_count += fields_to_push; - GST_DEBUG_OBJECT (self, "push, size(history): %d", self->history_count); - - gst_buffer_unref (buffer); -} - -static GstFlowReturn -gst_deinterlace2_chain (GstPad * pad, GstBuffer * buf) -{ - GstDeinterlace2 *self = NULL; - GstClockTime timestamp; - GstFlowReturn ret = GST_FLOW_OK; - gint fields_required = 0; - gint cur_field_idx = 0; - GstBuffer *outbuf; - - self = GST_DEINTERLACE2 (GST_PAD_PARENT (pad)); - - if (!self->interlaced && self->mode != GST_DEINTERLACE2_MODE_INTERLACED) - return gst_pad_push (self->srcpad, buf); - - if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT)) { - GST_DEBUG_OBJECT (self, "DISCONT buffer, resetting history"); - gst_deinterlace2_reset_history (self); - } - - gst_deinterlace2_push_history (self, buf); - buf = NULL; - - fields_required = gst_deinterlace_method_get_fields_required (self->method); - - /* Not enough fields in the history */ - if (self->history_count < fields_required + 1) { - /* TODO: do bob or just forward frame */ - GST_DEBUG_OBJECT (self, "HistoryCount=%d", self->history_count); - return GST_FLOW_OK; - } - - while (self->history_count >= fields_required) { - if (self->fields == GST_DEINTERLACE2_ALL) - GST_DEBUG_OBJECT (self, "All fields"); - if (self->fields == GST_DEINTERLACE2_TF) - GST_DEBUG_OBJECT (self, "Top fields"); - if (self->fields == GST_DEINTERLACE2_BF) - GST_DEBUG_OBJECT (self, "Bottom fields"); - - cur_field_idx = self->history_count - fields_required; - - if ((self->field_history[cur_field_idx].flags == PICTURE_INTERLACED_TOP - && self->fields == GST_DEINTERLACE2_TF) || - self->fields == GST_DEINTERLACE2_ALL) { - GST_DEBUG_OBJECT (self, "deinterlacing top field"); - - /* create new buffer */ - ret = gst_pad_alloc_buffer_and_set_caps (self->srcpad, - GST_BUFFER_OFFSET_NONE, self->frame_size, - GST_PAD_CAPS (self->srcpad), &outbuf); - if (ret != GST_FLOW_OK) - return ret; - - /* do magic calculus */ - gst_deinterlace_method_deinterlace_frame (self->method, self, outbuf); - - g_assert (self->history_count - 1 - - gst_deinterlace_method_get_latency (self->method) >= 0); - buf = - self->field_history[self->history_count - 1 - - gst_deinterlace_method_get_latency (self->method)].buf; - timestamp = GST_BUFFER_TIMESTAMP (buf); - - gst_buffer_unref (gst_deinterlace2_pop_history (self)); - - GST_BUFFER_TIMESTAMP (outbuf) = timestamp; - if (self->fields == GST_DEINTERLACE2_ALL) - GST_BUFFER_DURATION (outbuf) = self->field_duration; - else - GST_BUFFER_DURATION (outbuf) = 2 * self->field_duration; - - ret = gst_pad_push (self->srcpad, outbuf); - outbuf = NULL; - if (ret != GST_FLOW_OK) - return ret; - } - /* no calculation done: remove excess field */ - else if (self->field_history[cur_field_idx].flags == - PICTURE_INTERLACED_TOP && self->fields == GST_DEINTERLACE2_BF) { - GST_DEBUG_OBJECT (self, "Removing unused top field"); - gst_buffer_unref (gst_deinterlace2_pop_history (self)); - } - - cur_field_idx = self->history_count - fields_required; - if (self->history_count < fields_required) - break; - - /* deinterlace bottom_field */ - if ((self->field_history[cur_field_idx].flags == PICTURE_INTERLACED_BOTTOM - && self->fields == GST_DEINTERLACE2_BF) || - self->fields == GST_DEINTERLACE2_ALL) { - GST_DEBUG_OBJECT (self, "deinterlacing bottom field"); - - /* create new buffer */ - ret = gst_pad_alloc_buffer_and_set_caps (self->srcpad, - GST_BUFFER_OFFSET_NONE, self->frame_size, - GST_PAD_CAPS (self->srcpad), &outbuf); - if (ret != GST_FLOW_OK) - return ret; - - /* do magic calculus */ - gst_deinterlace_method_deinterlace_frame (self->method, self, outbuf); - - g_assert (self->history_count - 1 - - gst_deinterlace_method_get_latency (self->method) >= 0); - buf = - self->field_history[self->history_count - 1 - - gst_deinterlace_method_get_latency (self->method)].buf; - timestamp = GST_BUFFER_TIMESTAMP (buf); - - gst_buffer_unref (gst_deinterlace2_pop_history (self)); - - GST_BUFFER_TIMESTAMP (outbuf) = timestamp; - if (self->fields == GST_DEINTERLACE2_ALL) - GST_BUFFER_DURATION (outbuf) = self->field_duration; - else - GST_BUFFER_DURATION (outbuf) = 2 * self->field_duration; - - ret = gst_pad_push (self->srcpad, outbuf); - outbuf = NULL; - - if (ret != GST_FLOW_OK) - return ret; - } - /* no calculation done: remove excess field */ - else if (self->field_history[cur_field_idx].flags == - PICTURE_INTERLACED_BOTTOM && self->fields == GST_DEINTERLACE2_TF) { - GST_DEBUG_OBJECT (self, "Removing unused bottom field"); - gst_buffer_unref (gst_deinterlace2_pop_history (self)); - } - } - - GST_DEBUG_OBJECT (self, "----chain end ----\n\n"); - - return ret; -} - -static gint -gst_greatest_common_divisor (gint a, gint b) -{ - while (b != 0) { - int temp = a; - - a = b; - b = temp % b; - } - - return ABS (a); -} - -static gboolean -gst_fraction_double (gint * n_out, gint * d_out, gboolean half) -{ - gint n, d, gcd; - - n = *n_out; - d = *d_out; - - if (d == 0) - return FALSE; - - if (n == 0 || (n == G_MAXINT && d == 1)) - return TRUE; - - gcd = gst_greatest_common_divisor (n, d); - n /= gcd; - d /= gcd; - - if (!half) { - if (G_MAXINT / 2 >= ABS (n)) { - n *= 2; - } else if (d >= 2) { - d /= 2; - } else { - return FALSE; - } - } else { - if (G_MAXINT / 2 >= ABS (d)) { - d *= 2; - } else if (n >= 2) { - n /= 2; - } else { - return FALSE; - } - } - - *n_out = n; - *d_out = d; - - return TRUE; -} - -static GstCaps * -gst_deinterlace2_getcaps (GstPad * pad) -{ - GstCaps *ret; - GstDeinterlace2 *self = GST_DEINTERLACE2 (gst_pad_get_parent (pad)); - GstPad *otherpad; - gint len; - const GstCaps *ourcaps; - GstCaps *peercaps; - - GST_OBJECT_LOCK (self); - - otherpad = (pad == self->srcpad) ? self->sinkpad : self->srcpad; - - ourcaps = gst_pad_get_pad_template_caps (pad); - peercaps = gst_pad_peer_get_caps (otherpad); - - if (peercaps) { - ret = gst_caps_intersect (ourcaps, peercaps); - gst_caps_unref (peercaps); - } else { - ret = gst_caps_copy (ourcaps); - } - - GST_OBJECT_UNLOCK (self); - - if ((self->interlaced || self->mode == GST_DEINTERLACE2_MODE_INTERLACED) && - self->fields == GST_DEINTERLACE2_ALL) { - for (len = gst_caps_get_size (ret); len > 0; len--) { - GstStructure *s = gst_caps_get_structure (ret, len - 1); - const GValue *val; - - val = gst_structure_get_value (s, "framerate"); - if (!val) - continue; - - if (G_VALUE_TYPE (val) == GST_TYPE_FRACTION) { - gint n, d; - - n = gst_value_get_fraction_numerator (val); - d = gst_value_get_fraction_denominator (val); - - if (!gst_fraction_double (&n, &d, pad != self->srcpad)) { - goto error; - } - - gst_structure_set (s, "framerate", GST_TYPE_FRACTION, n, d, NULL); - } else if (G_VALUE_TYPE (val) == GST_TYPE_FRACTION_RANGE) { - const GValue *min, *max; - GValue nrange = { 0, }, nmin = { - 0,}, nmax = { - 0,}; - gint n, d; - - g_value_init (&nrange, GST_TYPE_FRACTION_RANGE); - g_value_init (&nmin, GST_TYPE_FRACTION); - g_value_init (&nmax, GST_TYPE_FRACTION); - - min = gst_value_get_fraction_range_min (val); - max = gst_value_get_fraction_range_max (val); - - n = gst_value_get_fraction_numerator (min); - d = gst_value_get_fraction_denominator (min); - - if (!gst_fraction_double (&n, &d, pad != self->srcpad)) { - g_value_unset (&nrange); - g_value_unset (&nmax); - g_value_unset (&nmin); - goto error; - } - - gst_value_set_fraction (&nmin, n, d); - - n = gst_value_get_fraction_numerator (max); - d = gst_value_get_fraction_denominator (max); - - if (!gst_fraction_double (&n, &d, pad != self->srcpad)) { - g_value_unset (&nrange); - g_value_unset (&nmax); - g_value_unset (&nmin); - goto error; - } - - gst_value_set_fraction (&nmax, n, d); - gst_value_set_fraction_range (&nrange, &nmin, &nmax); - - gst_structure_set_value (s, "framerate", &nrange); - - g_value_unset (&nmin); - g_value_unset (&nmax); - g_value_unset (&nrange); - } else if (G_VALUE_TYPE (val) == GST_TYPE_LIST) { - const GValue *lval; - GValue nlist = { 0, }; - GValue nval = { 0, }; - gint i; - - g_value_init (&nlist, GST_TYPE_LIST); - for (i = gst_value_list_get_size (val); i > 0; i--) { - gint n, d; - - lval = gst_value_list_get_value (val, i); - - if (G_VALUE_TYPE (lval) != GST_TYPE_FRACTION) - continue; - - n = gst_value_get_fraction_numerator (lval); - d = gst_value_get_fraction_denominator (lval); - - /* Double/Half the framerate but if this fails simply - * skip this value from the list */ - if (!gst_fraction_double (&n, &d, pad != self->srcpad)) { - continue; - } - - g_value_init (&nval, GST_TYPE_FRACTION); - - gst_value_set_fraction (&nval, n, d); - gst_value_list_append_value (&nlist, &nval); - g_value_unset (&nval); - } - gst_structure_set_value (s, "framerate", &nlist); - g_value_unset (&nlist); - } - } - } - - GST_DEBUG_OBJECT (pad, "Returning caps %" GST_PTR_FORMAT, ret); - - return ret; - -error: - GST_ERROR_OBJECT (pad, "Unable to transform peer caps"); - gst_caps_unref (ret); - return NULL; -} - -static gboolean -gst_deinterlace2_setcaps (GstPad * pad, GstCaps * caps) -{ - gboolean res = TRUE; - GstDeinterlace2 *self = GST_DEINTERLACE2 (gst_pad_get_parent (pad)); - GstPad *otherpad; - GstStructure *structure; - GstVideoFormat fmt; - guint32 fourcc; - GstCaps *othercaps; - - otherpad = (pad == self->srcpad) ? self->sinkpad : self->srcpad; - - structure = gst_caps_get_structure (caps, 0); - - res = gst_structure_get_int (structure, "width", &self->frame_width); - res &= gst_structure_get_int (structure, "height", &self->frame_height); - res &= - gst_structure_get_fraction (structure, "framerate", &self->frame_rate_n, - &self->frame_rate_d); - res &= gst_structure_get_fourcc (structure, "format", &fourcc); - res &= gst_video_format_parse_caps_interlaced (caps, &self->interlaced); - if (!res) - goto invalid_caps; - - if ((self->interlaced || self->mode == GST_DEINTERLACE2_MODE_INTERLACED) && - self->fields == GST_DEINTERLACE2_ALL) { - gint fps_n = self->frame_rate_n, fps_d = self->frame_rate_d; - - if (!gst_fraction_double (&fps_n, &fps_d, otherpad != self->srcpad)) - goto invalid_caps; - - othercaps = gst_caps_copy (caps); - - gst_caps_set_simple (othercaps, "framerate", GST_TYPE_FRACTION, fps_n, - fps_d, NULL); - } else { - othercaps = gst_caps_ref (caps); - } - - if (!gst_pad_set_caps (otherpad, othercaps)) - goto caps_not_accepted; - gst_caps_unref (othercaps); - - self->field_height = self->frame_height / 2; - - fmt = gst_video_format_from_fourcc (fourcc); - - /* TODO: only true if fields are subbuffers of interlaced frames, - change when the buffer-fields concept has landed */ - self->field_stride = - gst_video_format_get_row_stride (fmt, 0, self->frame_width) * 2; - - /* in bytes */ - self->row_stride = - gst_video_format_get_row_stride (fmt, 0, self->frame_width); - self->frame_size = - gst_video_format_get_size (fmt, self->frame_width, self->frame_height); - - if (self->fields == GST_DEINTERLACE2_ALL && otherpad == self->srcpad) - self->field_duration = - gst_util_uint64_scale (GST_SECOND, self->frame_rate_d, - self->frame_rate_n); - else - self->field_duration = - gst_util_uint64_scale (GST_SECOND, self->frame_rate_d, - 2 * self->frame_rate_n); - - GST_DEBUG_OBJECT (self, "Set caps: %" GST_PTR_FORMAT, caps); - -done: - - gst_object_unref (self); - return res; - -invalid_caps: - res = FALSE; - GST_ERROR_OBJECT (pad, "Invalid caps: %" GST_PTR_FORMAT, caps); - goto done; - -caps_not_accepted: - res = FALSE; - GST_ERROR_OBJECT (pad, "Caps not accepted: %" GST_PTR_FORMAT, othercaps); - gst_caps_unref (othercaps); - goto done; -} - -static gboolean -gst_deinterlace2_sink_event (GstPad * pad, GstEvent * event) -{ - gboolean res = TRUE; - GstDeinterlace2 *self = GST_DEINTERLACE2 (gst_pad_get_parent (pad)); - - GST_LOG_OBJECT (pad, "received %s event", GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_STOP: - case GST_EVENT_EOS: - case GST_EVENT_NEWSEGMENT: - gst_deinterlace2_reset_history (self); - - /* fall through */ - default: - res = gst_pad_event_default (pad, event); - break; - } - - gst_object_unref (self); - return res; -} - -static GstStateChangeReturn -gst_deinterlace2_change_state (GstElement * element, GstStateChange transition) -{ - GstStateChangeReturn ret; - GstDeinterlace2 *self = GST_DEINTERLACE2 (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - if (ret != GST_STATE_CHANGE_SUCCESS) - return ret; - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_deinterlace2_reset (self); - break; - case GST_STATE_CHANGE_READY_TO_NULL: - default: - break; - } - - return ret; -} - -static gboolean -gst_deinterlace2_src_event (GstPad * pad, GstEvent * event) -{ - GstDeinterlace2 *self = GST_DEINTERLACE2 (gst_pad_get_parent (pad)); - gboolean res; - - GST_DEBUG_OBJECT (pad, "received %s event", GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - default: - res = gst_pad_event_default (pad, event); - break; - } - - gst_object_unref (self); - - return res; -} - -static gboolean -gst_deinterlace2_src_query (GstPad * pad, GstQuery * query) -{ - GstDeinterlace2 *self = GST_DEINTERLACE2 (gst_pad_get_parent (pad)); - gboolean res = FALSE; - - GST_LOG_OBJECT (self, "%s query", GST_QUERY_TYPE_NAME (query)); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_LATENCY: - if (self->interlaced || self->mode == GST_DEINTERLACE2_MODE_INTERLACED) { - GstClockTime min, max; - gboolean live; - GstPad *peer; - - if ((peer = gst_pad_get_peer (self->sinkpad))) { - if ((res = gst_pad_query (peer, query))) { - GstClockTime latency; - gint fields_required = 0; - gint method_latency = 0; - - if (self->method) { - fields_required = - gst_deinterlace_method_get_fields_required (self->method); - method_latency = - gst_deinterlace_method_get_latency (self->method); - } - - gst_query_parse_latency (query, &live, &min, &max); - - GST_DEBUG_OBJECT (self, "Peer latency: min %" - GST_TIME_FORMAT " max %" GST_TIME_FORMAT, - GST_TIME_ARGS (min), GST_TIME_ARGS (max)); - - /* add our own latency */ - latency = (fields_required + method_latency) * self->field_duration; - - GST_DEBUG_OBJECT (self, "Our latency: min %" GST_TIME_FORMAT - ", max %" GST_TIME_FORMAT, - GST_TIME_ARGS (latency), GST_TIME_ARGS (latency)); - - min += latency; - if (max != GST_CLOCK_TIME_NONE) - max += latency; - else - max = latency; - - GST_DEBUG_OBJECT (self, "Calculated total latency : min %" - GST_TIME_FORMAT " max %" GST_TIME_FORMAT, - GST_TIME_ARGS (min), GST_TIME_ARGS (max)); - - gst_query_set_latency (query, live, min, max); - } - gst_object_unref (peer); - } else { - res = gst_pad_query_default (pad, query); - } - break; - } - default: - res = gst_pad_query_default (pad, query); - break; - } - - gst_object_unref (self); - return res; -} - -static const GstQueryType * -gst_deinterlace2_src_query_types (GstPad * pad) -{ - static const GstQueryType types[] = { - GST_QUERY_LATENCY, - GST_QUERY_NONE - }; - return types; -} - -static gboolean -plugin_init (GstPlugin * plugin) -{ - GST_DEBUG_CATEGORY_INIT (deinterlace2_debug, "deinterlace2", 0, - "Deinterlacer"); - - oil_init (); - - if (!gst_element_register (plugin, "deinterlace2", GST_RANK_NONE, - GST_TYPE_DEINTERLACE2)) { - return FALSE; - } - - return TRUE; -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "deinterlace2", - "Deinterlacer", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, - GST_PACKAGE_ORIGIN); diff --git a/gst/deinterlace2/gstdeinterlace2.h b/gst/deinterlace2/gstdeinterlace2.h deleted file mode 100644 index de63f54e..00000000 --- a/gst/deinterlace2/gstdeinterlace2.h +++ /dev/null @@ -1,258 +0,0 @@ -/* - * GStreamer - * Copyright (C) 2005 Martin Eikermann <meiker@upb.de> - * Copyright (C) 2008-2009 Sebastian Dröge <slomo@collabora.co.uk> - * - * 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_DEINTERLACE_2_H__ -#define __GST_DEINTERLACE_2_H__ - -#include <gst/gst.h> -#include <gst/base/gstbasetransform.h> -#include <liboil/liboil.h> -#include <liboil/liboilcpu.h> -#include <liboil/liboilfunction.h> - -#ifdef HAVE_GCC_ASM -#if defined(HAVE_CPU_I386) || defined(HAVE_CPU_X86_64) -#define BUILD_X86_ASM -#endif -#endif - -G_BEGIN_DECLS - -#define GST_TYPE_DEINTERLACE2 \ - (gst_deinterlace2_get_type()) -#define GST_DEINTERLACE2(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DEINTERLACE2,GstDeinterlace2)) -#define GST_DEINTERLACE2_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DEINTERLACE2,GstDeinterlace2)) -#define GST_IS_DEINTERLACE2(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DEINTERLACE2)) -#define GST_IS_DEINTERLACE2_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DEINTERLACE2)) - -typedef struct _GstDeinterlace2 GstDeinterlace2; -typedef struct _GstDeinterlace2Class GstDeinterlace2Class; - -#define GST_TYPE_DEINTERLACE_METHOD (gst_deinterlace_method_get_type ()) -#define GST_IS_DEINTERLACE_METHOD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_DEINTERLACE_METHOD)) -#define GST_IS_DEINTERLACE_METHOD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_DEINTERLACE_METHOD)) -#define GST_DEINTERLACE_METHOD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_DEINTERLACE_METHOD, GstDeinterlaceMethodClass)) -#define GST_DEINTERLACE_METHOD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_DEINTERLACE_METHOD, GstDeinterlaceMethod)) -#define GST_DEINTERLACE_METHOD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEINTERLACE_METHOD, GstDeinterlaceMethodClass)) -#define GST_DEINTERLACE_METHOD_CAST(obj) ((GstDeinterlaceMethod*)(obj)) - -typedef struct _GstDeinterlaceMethod GstDeinterlaceMethod; -typedef struct _GstDeinterlaceMethodClass GstDeinterlaceMethodClass; - -/* - * This structure defines the deinterlacer plugin. - */ - -struct _GstDeinterlaceMethod { - GstObject parent; -}; - -struct _GstDeinterlaceMethodClass { - GstObjectClass parent_class; - guint fields_required; - guint latency; - - void (*deinterlace_frame) (GstDeinterlaceMethod *self, GstDeinterlace2 * parent, GstBuffer *outbuf); - - const gchar *name; - const gchar *nick; -}; - -GType gst_deinterlace_method_get_type (void); - -#define GST_TYPE_DEINTERLACE_SIMPLE_METHOD (gst_deinterlace_simple_method_get_type ()) -#define GST_IS_DEINTERLACE_SIMPLE_METHOD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_DEINTERLACE_SIMPLE_METHOD)) -#define GST_IS_DEINTERLACE_SIMPLE_METHOD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_DEINTERLACE_SIMPLE_METHOD)) -#define GST_DEINTERLACE_SIMPLE_METHOD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_DEINTERLACE_SIMPLE_METHOD, GstDeinterlaceSimpleMethodClass)) -#define GST_DEINTERLACE_SIMPLE_METHOD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_DEINTERLACE_SIMPLE_METHOD, GstDeinterlaceSimpleMethod)) -#define GST_DEINTERLACE_SIMPLE_METHOD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEINTERLACE_SIMPLE_METHOD, GstDeinterlaceSimpleMethodClass)) -#define GST_DEINTERLACE_SIMPLE_METHOD_CAST(obj) ((GstDeinterlaceSimpleMethod*)(obj)) - -typedef struct _GstDeinterlaceSimpleMethod GstDeinterlaceSimpleMethod; -typedef struct _GstDeinterlaceSimpleMethodClass GstDeinterlaceSimpleMethodClass; -typedef struct _GstDeinterlaceScanlineData GstDeinterlaceScanlineData; - -/* - * This structure defines the simple deinterlacer plugin. - */ - -struct _GstDeinterlaceScanlineData { - guint8 *tt0, *t0, *m0, *b0, *bb0; - guint8 *tt1, *t1, *m1, *b1, *bb1; - guint8 *tt2, *t2, *m2, *b2, *bb2; - guint8 *tt3, *t3, *m3, *b3, *bb3; - gboolean bottom_field; -}; - -/** - * For interpolate_scanline the input is: - * - * | t-3 t-2 t-1 t - * | Field 3 | Field 2 | Field 1 | Field 0 | - * | TT3 | | TT1 | | - * | | T2 | | T0 | - * | M3 | | M1 | | - * | | B2 | | B0 | - * | BB3 | | BB1 | | - * - * For copy_scanline the input is: - * - * | t-3 t-2 t-1 t - * | Field 3 | Field 2 | Field 1 | Field 0 | - * | | TT2 | | TT0 | - * | T3 | | T1 | | - * | | M2 | | M0 | - * | B3 | | B1 | | - * | | BB2 | | BB0 | - * - * All other values are NULL. - */ - -struct _GstDeinterlaceSimpleMethod { - GstDeinterlaceMethod parent; -}; - -struct _GstDeinterlaceSimpleMethodClass { - GstDeinterlaceMethodClass parent_class; - - void (*interpolate_scanline) (GstDeinterlaceMethod *self, GstDeinterlace2 * parent, guint8 *out, GstDeinterlaceScanlineData *scanlines, gint width); - void (*copy_scanline) (GstDeinterlaceMethod *self, GstDeinterlace2 * parent, guint8 *out, GstDeinterlaceScanlineData *scanlines, gint width); -}; - -GType gst_deinterlace_simple_method_get_type (void); - - -#define MAX_FIELD_HISTORY 10 - -#define PICTURE_PROGRESSIVE 0 -#define PICTURE_INTERLACED_BOTTOM 1 -#define PICTURE_INTERLACED_TOP 2 -#define PICTURE_INTERLACED_MASK (PICTURE_INTERLACED_BOTTOM | PICTURE_INTERLACED_TOP) - -typedef struct -{ - /* pointer to the start of data for this field */ - GstBuffer *buf; - /* see PICTURE_ flags */ - guint flags; -} GstPicture; - -typedef enum -{ - GST_DEINTERLACE2_TOMSMOCOMP, - GST_DEINTERLACE2_GREEDY_H, - GST_DEINTERLACE2_GREEDY_L, - GST_DEINTERLACE2_VFIR, - GST_DEINTERLACE2_LINEAR, - GST_DEINTERLACE2_LINEAR_BLEND, - GST_DEINTERLACE2_SCALER_BOB, - GST_DEINTERLACE2_WEAVE, - GST_DEINTERLACE2_WEAVE_TFF, - GST_DEINTERLACE2_WEAVE_BFF -} GstDeinterlace2Methods; - -typedef enum -{ - GST_DEINTERLACE2_ALL, /* All (missing data is interp.) */ - GST_DEINTERLACE2_TF, /* Top Fields Only */ - GST_DEINTERLACE2_BF /* Bottom Fields Only */ -} GstDeinterlace2Fields; - -typedef enum -{ - GST_DEINTERLACE2_LAYOUT_AUTO, - GST_DEINTERLACE2_LAYOUT_TFF, - GST_DEINTERLACE2_LAYOUT_BFF -} GstDeinterlace2FieldLayout; - -typedef enum { - GST_DEINTERLACE2_MODE_AUTO, - GST_DEINTERLACE2_MODE_INTERLACED -} GstDeinterlace2Mode; - -struct _GstDeinterlace2 -{ - GstElement parent; - - GstPad *srcpad, *sinkpad; - - /* <private> */ - - GstDeinterlace2Mode mode; - - GstDeinterlace2FieldLayout field_layout; - - guint frame_size; - gint frame_rate_n, frame_rate_d; - gboolean interlaced; - - /* Duration of one field */ - GstClockTime field_duration; - - GstDeinterlace2Fields fields; - - GstDeinterlace2Methods method_id; - GstDeinterlaceMethod *method; - - /* The most recent pictures - PictureHistory[0] is always the most recent. - Pointers are NULL if the picture in question isn't valid, e.g. because - the program just started or a picture was skipped. - */ - GstPicture field_history[MAX_FIELD_HISTORY]; - guint history_count; - - /* Number of bytes of actual data in each scanline. May be less than - OverlayPitch since the overlay's scanlines might have alignment - requirements. Generally equal to FrameWidth * 2. - */ - guint row_stride; - - /* Number of pixels in each scanline. */ - gint frame_width; - - /* Number of scanlines per frame. */ - gint frame_height; - - /* Number of scanlines per field. FrameHeight / 2, mostly for - cleanliness so we don't have to keep dividing FrameHeight by 2. - */ - gint field_height; - - /* distance between lines in image - need not match the pixel width - */ - guint field_stride; -}; - -struct _GstDeinterlace2Class -{ - GstElementClass parent_class; -}; - -GType gst_deinterlace2_get_type (void); - -G_END_DECLS -#endif /* __GST_DEINTERLACE_2_H__ */ diff --git a/gst/deinterlace2/tvtime/greedy.c b/gst/deinterlace2/tvtime/greedy.c deleted file mode 100644 index 7d4e4b3a..00000000 --- a/gst/deinterlace2/tvtime/greedy.c +++ /dev/null @@ -1,488 +0,0 @@ -/* - * - * GStreamer - * Copyright (c) 2000 Tom Barry All rights reserved. - * mmx.h port copyright (c) 2002 Billy Biggs <vektor@dumbterm.net>. - * - * Copyright (C) 2008 Sebastian Dröge <slomo@collabora.co.uk> - * - * 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. - */ - -/* - * Relicensed for GStreamer from GPL to LGPL with permit from Tom Barry - * and Billy Biggs. - * See: http://bugzilla.gnome.org/show_bug.cgi?id=163578 - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "_stdint.h" - -#include "gstdeinterlace2.h" -#include <string.h> - -#define GST_TYPE_DEINTERLACE_METHOD_GREEDY_L (gst_deinterlace_method_greedy_l_get_type ()) -#define GST_IS_DEINTERLACE_METHOD_GREEDY_L(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_DEINTERLACE_METHOD_GREEDY_L)) -#define GST_IS_DEINTERLACE_METHOD_GREEDY_L_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_DEINTERLACE_METHOD_GREEDY_L)) -#define GST_DEINTERLACE_METHOD_GREEDY_L_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_DEINTERLACE_METHOD_GREEDY_L, GstDeinterlaceMethodGreedyLClass)) -#define GST_DEINTERLACE_METHOD_GREEDY_L(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_DEINTERLACE_METHOD_GREEDY_L, GstDeinterlaceMethodGreedyL)) -#define GST_DEINTERLACE_METHOD_GREEDY_L_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEINTERLACE_METHOD_GREEDY_L, GstDeinterlaceMethodGreedyLClass)) -#define GST_DEINTERLACE_METHOD_GREEDY_L_CAST(obj) ((GstDeinterlaceMethodGreedyL*)(obj)) - -GType gst_deinterlace_method_greedy_l_get_type (void); - -typedef struct -{ - GstDeinterlaceMethod parent; - - guint max_comb; -} GstDeinterlaceMethodGreedyL; - -typedef struct -{ - GstDeinterlaceMethodClass parent_class; - void (*scanline) (GstDeinterlaceMethodGreedyL * self, uint8_t * L2, - uint8_t * L1, uint8_t * L3, uint8_t * L2P, uint8_t * Dest, int size); -} GstDeinterlaceMethodGreedyLClass; - -// This is a simple lightweight DeInterlace method that uses little CPU time -// but gives very good results for low or intermedite motion. -// It defers frames by one field, but that does not seem to produce noticeable -// lip sync problems. -// -// The method used is to take either the older or newer weave pixel depending -// upon which give the smaller comb factor, and then clip to avoid large damage -// when wrong. -// -// I'd intended this to be part of a larger more elaborate method added to -// Blended Clip but this give too good results for the CPU to ignore here. - -static inline void -deinterlace_greedy_packed422_scanline_c (GstDeinterlaceMethodGreedyL * self, - uint8_t * m0, uint8_t * t1, - uint8_t * b1, uint8_t * m2, uint8_t * output, int width) -{ - int avg, l2_diff, lp2_diff, max, min, best; - guint max_comb = self->max_comb; - - // L2 == m0 - // L1 == t1 - // L3 == b1 - // LP2 == m2 - - while (width--) { - avg = (*t1 + *b1) / 2; - - l2_diff = ABS (*m0 - avg); - lp2_diff = ABS (*m2 - avg); - - if (l2_diff > lp2_diff) - best = *m2; - else - best = *m0; - - max = MAX (*t1, *b1); - min = MIN (*t1, *b1); - - if (max < 256 - max_comb) - max += max_comb; - else - max = 255; - - if (min > max_comb) - min -= max_comb; - else - min = 0; - - *output = CLAMP (best, min, max); - - // Advance to the next set of pixels. - output += 1; - m0 += 1; - t1 += 1; - b1 += 1; - m2 += 1; - } -} - -#ifdef BUILD_X86_ASM -#include "mmx.h" -static void -deinterlace_greedy_packed422_scanline_mmx (GstDeinterlaceMethodGreedyL * self, - uint8_t * m0, uint8_t * t1, - uint8_t * b1, uint8_t * m2, uint8_t * output, int width) -{ - mmx_t MaxComb; - mmx_t ShiftMask; - - // How badly do we let it weave? 0-255 - MaxComb.ub[0] = self->max_comb; - MaxComb.ub[1] = self->max_comb; - MaxComb.ub[2] = self->max_comb; - MaxComb.ub[3] = self->max_comb; - MaxComb.ub[4] = self->max_comb; - MaxComb.ub[5] = self->max_comb; - MaxComb.ub[6] = self->max_comb; - MaxComb.ub[7] = self->max_comb; - - ShiftMask.ub[0] = 0x7f; - ShiftMask.ub[1] = 0x7f; - ShiftMask.ub[2] = 0x7f; - ShiftMask.ub[3] = 0x7f; - ShiftMask.ub[4] = 0x7f; - ShiftMask.ub[5] = 0x7f; - ShiftMask.ub[6] = 0x7f; - ShiftMask.ub[7] = 0x7f; - - // L2 == m0 - // L1 == t1 - // L3 == b1 - // LP2 == m2 - - movq_m2r (MaxComb, mm6); - - for (; width > 7; width -= 8) { - movq_m2r (*t1, mm1); // L1 - movq_m2r (*m0, mm2); // L2 - movq_m2r (*b1, mm3); // L3 - movq_m2r (*m2, mm0); // LP2 - - // average L1 and L3 leave result in mm4 - movq_r2r (mm1, mm4); // L1 - movq_r2r (mm3, mm5); // L3 - psrlw_i2r (1, mm4); // L1/2 - pand_m2r (ShiftMask, mm4); - psrlw_i2r (1, mm5); // L3/2 - pand_m2r (ShiftMask, mm5); - paddusb_r2r (mm5, mm4); // (L1 + L3) / 2 - - // get abs value of possible L2 comb - movq_r2r (mm2, mm7); // L2 - psubusb_r2r (mm4, mm7); // L2 - avg - movq_r2r (mm4, mm5); // avg - psubusb_r2r (mm2, mm5); // avg - L2 - por_r2r (mm7, mm5); // abs(avg-L2) - - // get abs value of possible LP2 comb - movq_r2r (mm0, mm7); // LP2 - psubusb_r2r (mm4, mm7); // LP2 - avg - psubusb_r2r (mm0, mm4); // avg - LP2 - por_r2r (mm7, mm4); // abs(avg-LP2) - - // use L2 or LP2 depending upon which makes smaller comb - psubusb_r2r (mm5, mm4); // see if it goes to zero - psubusb_r2r (mm5, mm5); // 0 - pcmpeqb_r2r (mm5, mm4); // if (mm4=0) then FF else 0 - pcmpeqb_r2r (mm4, mm5); // opposite of mm4 - - // if Comb(LP2) <= Comb(L2) then mm4=ff, mm5=0 else mm4=0, mm5 = 55 - pand_r2r (mm2, mm5); // use L2 if mm5 == ff, else 0 - pand_r2r (mm0, mm4); // use LP2 if mm4 = ff, else 0 - por_r2r (mm5, mm4); // may the best win - - // Now lets clip our chosen value to be not outside of the range - // of the high/low range L1-L3 by more than abs(L1-L3) - // This allows some comb but limits the damages and also allows more - // detail than a boring oversmoothed clip. - - movq_r2r (mm1, mm2); // copy L1 - psubusb_r2r (mm3, mm2); // - L3, with saturation - paddusb_r2r (mm3, mm2); // now = Max(L1,L3) - - pcmpeqb_r2r (mm7, mm7); // all ffffffff - psubusb_r2r (mm1, mm7); // - L1 - paddusb_r2r (mm7, mm3); // add, may sat at fff.. - psubusb_r2r (mm7, mm3); // now = Min(L1,L3) - - // allow the value to be above the high or below the low by amt of MaxComb - paddusb_r2r (mm6, mm2); // increase max by diff - psubusb_r2r (mm6, mm3); // lower min by diff - - psubusb_r2r (mm3, mm4); // best - Min - paddusb_r2r (mm3, mm4); // now = Max(best,Min(L1,L3) - - pcmpeqb_r2r (mm7, mm7); // all ffffffff - psubusb_r2r (mm4, mm7); // - Max(best,Min(best,L3) - paddusb_r2r (mm7, mm2); // add may sat at FFF.. - psubusb_r2r (mm7, mm2); // now = Min( Max(best, Min(L1,L3), L2 )=L2 clipped - - movq_r2m (mm2, *output); // move in our clipped best - - // Advance to the next set of pixels. - output += 8; - m0 += 8; - t1 += 8; - b1 += 8; - m2 += 8; - } - emms (); - if (width > 0) - deinterlace_greedy_packed422_scanline_c (self, m0, t1, b1, m2, output, - width); -} - -#include "sse.h" - -static void -deinterlace_greedy_packed422_scanline_mmxext (GstDeinterlaceMethodGreedyL * - self, uint8_t * m0, uint8_t * t1, uint8_t * b1, uint8_t * m2, - uint8_t * output, int width) -{ - mmx_t MaxComb; - - // How badly do we let it weave? 0-255 - MaxComb.ub[0] = self->max_comb; - MaxComb.ub[1] = self->max_comb; - MaxComb.ub[2] = self->max_comb; - MaxComb.ub[3] = self->max_comb; - MaxComb.ub[4] = self->max_comb; - MaxComb.ub[5] = self->max_comb; - MaxComb.ub[6] = self->max_comb; - MaxComb.ub[7] = self->max_comb; - - // L2 == m0 - // L1 == t1 - // L3 == b1 - // LP2 == m2 - - movq_m2r (MaxComb, mm6); - - for (; width > 7; width -= 8) { - movq_m2r (*t1, mm1); // L1 - movq_m2r (*m0, mm2); // L2 - movq_m2r (*b1, mm3); // L3 - movq_m2r (*m2, mm0); // LP2 - - // average L1 and L3 leave result in mm4 - movq_r2r (mm1, mm4); // L1 - pavgb_r2r (mm3, mm4); // (L1 + L3)/2 - - // get abs value of possible L2 comb - movq_r2r (mm2, mm7); // L2 - psubusb_r2r (mm4, mm7); // L2 - avg - movq_r2r (mm4, mm5); // avg - psubusb_r2r (mm2, mm5); // avg - L2 - por_r2r (mm7, mm5); // abs(avg-L2) - - // get abs value of possible LP2 comb - movq_r2r (mm0, mm7); // LP2 - psubusb_r2r (mm4, mm7); // LP2 - avg - psubusb_r2r (mm0, mm4); // avg - LP2 - por_r2r (mm7, mm4); // abs(avg-LP2) - - // use L2 or LP2 depending upon which makes smaller comb - psubusb_r2r (mm5, mm4); // see if it goes to zero - pxor_r2r (mm5, mm5); // 0 - pcmpeqb_r2r (mm5, mm4); // if (mm4=0) then FF else 0 - pcmpeqb_r2r (mm4, mm5); // opposite of mm4 - - // if Comb(LP2) <= Comb(L2) then mm4=ff, mm5=0 else mm4=0, mm5 = 55 - pand_r2r (mm2, mm5); // use L2 if mm5 == ff, else 0 - pand_r2r (mm0, mm4); // use LP2 if mm4 = ff, else 0 - por_r2r (mm5, mm4); // may the best win - - // Now lets clip our chosen value to be not outside of the range - // of the high/low range L1-L3 by more than abs(L1-L3) - // This allows some comb but limits the damages and also allows more - // detail than a boring oversmoothed clip. - - movq_r2r (mm1, mm2); // copy L1 - pmaxub_r2r (mm3, mm2); // now = Max(L1,L3) - - pminub_r2r (mm1, mm3); // now = Min(L1,L3) - - // allow the value to be above the high or below the low by amt of MaxComb - paddusb_r2r (mm6, mm2); // increase max by diff - psubusb_r2r (mm6, mm3); // lower min by diff - - - pmaxub_r2r (mm3, mm4); // now = Max(best,Min(L1,L3) - pminub_r2r (mm4, mm2); // now = Min( Max(best, Min(L1,L3)), L2 )=L2 clipped - - movq_r2m (mm2, *output); // move in our clipped best - - // Advance to the next set of pixels. - output += 8; - m0 += 8; - t1 += 8; - b1 += 8; - m2 += 8; - } - emms (); - - if (width > 0) - deinterlace_greedy_packed422_scanline_c (self, m0, t1, b1, m2, output, - width); -} - -#endif - -static void -deinterlace_frame_di_greedy (GstDeinterlaceMethod * d_method, - GstDeinterlace2 * object, GstBuffer * outbuf) -{ - GstDeinterlaceMethodGreedyL *self = - GST_DEINTERLACE_METHOD_GREEDY_L (d_method); - GstDeinterlaceMethodGreedyLClass *klass = - GST_DEINTERLACE_METHOD_GREEDY_L_GET_CLASS (self); - int InfoIsOdd = 0; - int Line; - unsigned int Pitch = object->field_stride; - unsigned char *L1; // ptr to Line1, of 3 - unsigned char *L2; // ptr to Line2, the weave line - unsigned char *L3; // ptr to Line3 - - unsigned char *L2P; // ptr to prev Line2 - unsigned char *Dest = GST_BUFFER_DATA (outbuf); - - // copy first even line no matter what, and the first odd line if we're - // processing an EVEN field. (note diff from other deint rtns.) - - if (object->field_history[object->history_count - 1].flags == - PICTURE_INTERLACED_BOTTOM) { - InfoIsOdd = 1; - - L1 = GST_BUFFER_DATA (object->field_history[object->history_count - 2].buf); - L2 = GST_BUFFER_DATA (object->field_history[object->history_count - 1].buf); - L3 = L1 + Pitch; - L2P = - GST_BUFFER_DATA (object->field_history[object->history_count - 3].buf); - - // copy first even line - oil_memcpy (Dest, L1, object->row_stride); - Dest += object->row_stride; - } else { - InfoIsOdd = 0; - L1 = GST_BUFFER_DATA (object->field_history[object->history_count - 2].buf); - L2 = GST_BUFFER_DATA (object->field_history[object->history_count - - 1].buf) + Pitch; - L3 = L1 + Pitch; - L2P = - GST_BUFFER_DATA (object->field_history[object->history_count - 3].buf) + - Pitch; - - // copy first even line - oil_memcpy (Dest, GST_BUFFER_DATA (object->field_history[0].buf), - object->row_stride); - Dest += object->row_stride; - // then first odd line - oil_memcpy (Dest, L1, object->row_stride); - Dest += object->row_stride; - } - - for (Line = 0; Line < (object->field_height - 1); ++Line) { - klass->scanline (self, L2, L1, L3, L2P, Dest, object->row_stride); - Dest += object->row_stride; - oil_memcpy (Dest, L3, object->row_stride); - Dest += object->row_stride; - - L1 += Pitch; - L2 += Pitch; - L3 += Pitch; - L2P += Pitch; - } - - if (InfoIsOdd) { - oil_memcpy (Dest, L2, object->row_stride); - } -} - - -G_DEFINE_TYPE (GstDeinterlaceMethodGreedyL, gst_deinterlace_method_greedy_l, - GST_TYPE_DEINTERLACE_METHOD); - -enum -{ - ARG_0, - ARG_MAX_COMB -}; - -static void -gst_deinterlace_method_greedy_l_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstDeinterlaceMethodGreedyL *self = GST_DEINTERLACE_METHOD_GREEDY_L (object); - - switch (prop_id) { - case ARG_MAX_COMB: - self->max_comb = g_value_get_uint (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -gst_deinterlace_method_greedy_l_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstDeinterlaceMethodGreedyL *self = GST_DEINTERLACE_METHOD_GREEDY_L (object); - - switch (prop_id) { - case ARG_MAX_COMB: - g_value_set_uint (value, self->max_comb); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -gst_deinterlace_method_greedy_l_class_init (GstDeinterlaceMethodGreedyLClass * - klass) -{ - GstDeinterlaceMethodClass *dim_class = (GstDeinterlaceMethodClass *) klass; - GObjectClass *gobject_class = (GObjectClass *) klass; -#ifdef BUILD_X86_ASM - guint cpu_flags = oil_cpu_get_flags (); -#endif - - gobject_class->set_property = gst_deinterlace_method_greedy_l_set_property; - gobject_class->get_property = gst_deinterlace_method_greedy_l_get_property; - - g_object_class_install_property (gobject_class, ARG_MAX_COMB, - g_param_spec_uint ("max-comb", - "Max comb", - "Max Comb", 0, 255, 15, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) - ); - - dim_class->fields_required = 4; - dim_class->deinterlace_frame = deinterlace_frame_di_greedy; - dim_class->name = "Motion Adaptive: Simple Detection"; - dim_class->nick = "greedyl"; - dim_class->latency = 1; - -#ifdef BUILD_X86_ASM - if (cpu_flags & OIL_IMPL_FLAG_MMXEXT) { - klass->scanline = deinterlace_greedy_packed422_scanline_mmxext; - } else if (cpu_flags & OIL_IMPL_FLAG_MMX) { - klass->scanline = deinterlace_greedy_packed422_scanline_mmx; - } else { - klass->scanline = deinterlace_greedy_packed422_scanline_c; - } -#else - klass->scanline = deinterlace_greedy_packed422_scanline_c; -#endif -} - -static void -gst_deinterlace_method_greedy_l_init (GstDeinterlaceMethodGreedyL * self) -{ - self->max_comb = 15; -} diff --git a/gst/deinterlace2/tvtime/greedyh.asm b/gst/deinterlace2/tvtime/greedyh.asm deleted file mode 100644 index 86e97c58..00000000 --- a/gst/deinterlace2/tvtime/greedyh.asm +++ /dev/null @@ -1,250 +0,0 @@ -/* - * - * GStreamer - * Copyright (c) 2001 Tom Barry. All rights reserved. - * Copyright (C) 2008 Sebastian Dröge <slomo@collabora.co.uk> - * - * 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. - */ - - -/* - * Relicensed for GStreamer from GPL to LGPL with permit from Tom Barry. - * See: http://bugzilla.gnome.org/show_bug.cgi?id=163578 - */ - - -#include "x86-64_macros.inc" - -void -FUNCT_NAME (GstDeinterlaceMethodGreedyH *self, uint8_t * L1, uint8_t * L2, uint8_t * L3, uint8_t * L2P, - uint8_t * Dest, int size) -{ - - // in tight loop some vars are accessed faster in local storage - int64_t YMask = 0x00ff00ff00ff00ffull; // to keep only luma - int64_t UVMask = 0xff00ff00ff00ff00ull; // to keep only chroma - int64_t ShiftMask = 0xfefefefefefefefeull; // to avoid shifting chroma to luma - int64_t QW256 = 0x0100010001000100ull; // 4 256's - int64_t MaxComb; - int64_t MotionThreshold; - int64_t MotionSense; - int64_t i; - long LoopCtr; - long oldbx; - - int64_t QW256B; - int64_t LastAvg = 0; //interp value from left qword - - // FIXME: Use C implementation if the width is not a multiple of 4 - // Do something more optimal later - if (size % 8 != 0) - greedyDScaler_C (self, L1, L2, L3, L2P, Dest, size); - - // Set up our two parms that are actually evaluated for each pixel - i = self->max_comb; - MaxComb = - i << 56 | i << 48 | i << 40 | i << 32 | i << 24 | i << 16 | i << 8 | i; - - i = self->motion_threshold; // scale to range of 0-257 - MotionThreshold = i << 48 | i << 32 | i << 16 | i | UVMask; - - i = self->motion_sense; // scale to range of 0-257 - MotionSense = i << 48 | i << 32 | i << 16 | i; - - i = 0xffffffff - 256; - QW256B = i << 48 | i << 32 | i << 16 | i; // save a couple instr on PMINSW instruct. - - LoopCtr = size / 8 - 1; // there are LineLength / 8 qwords per line but do 1 less, adj at end of loop - - // For ease of reading, the comments below assume that we're operating on an odd - // field (i.e., that InfoIsOdd is true). Assume the obvious for even lines.. - __asm__ __volatile__ ( - // save ebx (-fPIC) - MOVX " %%" XBX ", %[oldbx]\n\t" - MOVX " %[L1], %%" XAX "\n\t" - LEAX " 8(%%" XAX "), %%" XBX "\n\t" // next qword needed by DJR - MOVX " %[L3], %%" XCX "\n\t" - SUBX " %%" XAX ", %%" XCX "\n\t" // carry L3 addr as an offset - MOVX " %[L2P], %%" XDX "\n\t" - MOVX " %[L2], %%" XSI "\n\t" - MOVX " %[Dest], %%" XDI "\n\t" // DL1 if Odd or DL2 if Even - - ".align 8\n\t" - "1:\n\t" - "movq (%%" XSI "), %%mm0\n\t" // L2 - the newest weave pixel value - "movq (%%" XAX "), %%mm1\n\t" // L1 - the top pixel - "movq (%%" XDX "), %%mm2\n\t" // L2P - the prev weave pixel - "movq (%%" XAX ", %%" XCX "), %%mm3\n\t" // L3, next odd row - "movq %%mm1, %%mm6\n\t" // L1 - get simple single pixel interp - - // pavgb mm6, mm3 // use macro below - V_PAVGB ("%%mm6", "%%mm3", "%%mm4", "%[ShiftMask]") - - // DJR - Diagonal Jaggie Reduction - // In the event that we are going to use an average (Bob) pixel we do not want a jagged - // stair step effect. To combat this we avg in the 2 horizontally adjacen pixels into the - // interpolated Bob mix. This will do horizontal smoothing for only the Bob'd pixels. - - "movq %[LastAvg], %%mm4\n\t" // the bob value from prev qword in row - "movq %%mm6, %[LastAvg]\n\t" // save for next pass - "psrlq $48, %%mm4\n\t" // right justify 1 pixel - "movq %%mm6, %%mm7\n\t" // copy of simple bob pixel - "psllq $16, %%mm7\n\t" // left justify 3 pixels - "por %%mm7, %%mm4\n\t" // and combine - "movq (%%" XBX "), %%mm5\n\t" // next horiz qword from L1 - // pavgb mm5, qword ptr[ebx+ecx] // next horiz qword from L3, use macro below - - V_PAVGB ("%%mm5", "(%%" XBX ",%%" XCX ")", "%%mm7", "%[ShiftMask]") - "psllq $48, %%mm5\n\t" // left just 1 pixel - "movq %%mm6, %%mm7\n\t" // another copy of simple bob pixel - "psrlq $16, %%mm7\n\t" // right just 3 pixels - "por %%mm7, %%mm5\n\t" // combine - // pavgb mm4, mm5 // avg of forward and prev by 1 pixel, use macro - V_PAVGB ("%%mm4", "%%mm5", "%%mm5", "%[ShiftMask]") // mm5 gets modified if MMX - // pavgb mm6, mm4 // avg of center and surround interp vals, use macro - V_PAVGB ("%%mm6", "%%mm4", "%%mm7", "%[ShiftMask]") - - // Don't do any more averaging than needed for mmx. It hurts performance and causes rounding errors. -#ifndef IS_MMX - // pavgb mm4, mm6 // 1/4 center, 3/4 adjacent - V_PAVGB ("%%mm4", "%%mm6", "%%mm7", "%[ShiftMask]") - // pavgb mm6, mm4 // 3/8 center, 5/8 adjacent - V_PAVGB ("%%mm6", "%%mm4", "%%mm7", "%[ShiftMask]") -#endif - - // get abs value of possible L2 comb - "movq %%mm6, %%mm4\n\t" // work copy of interp val - "movq %%mm2, %%mm7\n\t" // L2 - "psubusb %%mm4, %%mm7\n\t" // L2 - avg - "movq %%mm4, %%mm5\n\t" // avg - "psubusb %%mm2, %%mm5\n\t" // avg - L2 - "por %%mm7, %%mm5\n\t" // abs(avg-L2) - - // get abs value of possible L2P comb - "movq %%mm0, %%mm7\n\t" // L2P - "psubusb %%mm4, %%mm7\n\t" // L2P - avg - "psubusb %%mm0, %%mm4\n\t" // avg - L2P - "por %%mm7, %%mm4\n\t" // abs(avg-L2P) - - // use L2 or L2P depending upon which makes smaller comb - "psubusb %%mm5, %%mm4\n\t" // see if it goes to zero - "psubusb %%mm5, %%mm5\n\t" // 0 - "pcmpeqb %%mm5, %%mm4\n\t" // if (mm4=0) then FF else 0 - "pcmpeqb %%mm4, %%mm5\n\t" // opposite of mm4 - - // if Comb(L2P) <= Comb(L2) then mm4=ff, mm5=0 else mm4=0, mm5 = 55 - "pand %%mm2, %%mm5\n\t" // use L2 if mm5 == ff, else 0 - "pand %%mm0, %%mm4\n\t" // use L2P if mm4 = ff, else 0 - "por %%mm5, %%mm4\n\t" // may the best win - - // Inventory: at this point we have the following values: - // mm0 = L2P (or L2) - // mm1 = L1 - // mm2 = L2 (or L2P) - // mm3 = L3 - // mm4 = the best of L2,L2P weave pixel, base upon comb - // mm6 = the avg interpolated value, if we need to use it - // Let's measure movement, as how much the weave pixel has changed - - "movq %%mm2, %%mm7\n\t" - "psubusb %%mm0, %%mm2\n\t" - "psubusb %%mm7, %%mm0\n\t" - "por %%mm2, %%mm0\n\t" // abs value of change, used later - - // Now lets clip our chosen value to be not outside of the range - // of the high/low range L1-L3 by more than MaxComb. - // This allows some comb but limits the damages and also allows more - // detail than a boring oversmoothed clip. - - "movq %%mm1, %%mm2\n\t" // copy L1 - // pmaxub mm2, mm3 // use macro - V_PMAXUB ("%%mm2", "%%mm3") // now = Max(L1,L3) - "movq %%mm1, %%mm5\n\t" // copy L1 - // pminub mm5, mm3 // now = Min(L1,L3), use macro - V_PMINUB ("%%mm5", "%%mm3", "%%mm7") - - // allow the value to be above the high or below the low by amt of MaxComb - "psubusb %[MaxComb], %%mm5\n\t" // lower min by diff - "paddusb %[MaxComb], %%mm2\n\t" // increase max by diff - // pmaxub mm4, mm5 // now = Max(best,Min(L1,L3) use macro - V_PMAXUB ("%%mm4", "%%mm5") - // pminub mm4, mm2 // now = Min( Max(best, Min(L1,L3), L2 )=L2 clipped - V_PMINUB ("%%mm4", "%%mm2", "%%mm7") - - // Blend weave pixel with bob pixel, depending on motion val in mm0 - "psubusb %[MotionThreshold], %%mm0\n\t" // test Threshold, clear chroma change >>>?? - "pmullw %[MotionSense], %%mm0\n\t" // mul by user factor, keep low 16 bits - "movq %[QW256], %%mm7\n\t" -#ifdef IS_MMXEXT - "pminsw %%mm7, %%mm0\n\t" // max = 256 -#else - "paddusw %[QW256B], %%mm0\n\t" // add, may sat at fff.. - "psubusw %[QW256B], %%mm0\n\t" // now = Min(L1,256) -#endif - "psubusw %%mm0, %%mm7\n\t" // so the 2 sum to 256, weighted avg - "movq %%mm4, %%mm2\n\t" // save weave chroma info before trashing - "pand %[YMask], %%mm4\n\t" // keep only luma from calc'd value - "pmullw %%mm7, %%mm4\n\t" // use more weave for less motion - "pand %[YMask], %%mm6\n\t" // keep only luma from calc'd value - "pmullw %%mm0, %%mm6\n\t" // use more bob for large motion - "paddusw %%mm6, %%mm4\n\t" // combine - "psrlw $8, %%mm4\n\t" // div by 256 to get weighted avg - // chroma comes from weave pixel - "pand %[UVMask], %%mm2\n\t" // keep chroma - "por %%mm4, %%mm2\n\t" // and combine - V_MOVNTQ ("(%%" XDI ")", "%%mm2") // move in our clipped best, use macro - // bump ptrs and loop - LEAX " 8(%%" XAX "), %%" XAX "\n\t" - LEAX " 8(%%" XBX "), %%" XBX "\n\t" - LEAX " 8(%%" XDX "), %%" XDX "\n\t" - LEAX " 8(%%" XDI "), %%" XDI "\n\t" - LEAX " 8(%%" XSI "), %%" XSI "\n\t" - DECX " %[LoopCtr]\n\t" - - "jg 1b\n\t" // loop if not to last line - // note P-III default assumes backward branches taken - "jl 1f\n\t" // done - MOVX " %%" XAX ", %%" XBX "\n\t" // sharpness lookahead 1 byte only, be wrong on 1 - "jmp 1b\n\t" - - "1:\n\t" - MOVX " %[oldbx], %%" XBX "\n\t" - "emms\n\t": /* no outputs */ - - :[LastAvg] "m" (LastAvg), - [L1] "m" (L1), - [L3] "m" (L3), - [L2P] "m" (L2P), - [L2] "m" (L2), - [Dest] "m" (Dest), - [ShiftMask] "m" (ShiftMask), - [MaxComb] "m" (MaxComb), - [MotionThreshold] "m" (MotionThreshold), - [MotionSense] "m" (MotionSense), - [QW256B] "m" (QW256B), - [YMask] "m" (YMask), - [UVMask] "m" (UVMask), - [LoopCtr] "m" (LoopCtr), - [QW256] "m" (QW256), - [oldbx] "m" (oldbx) - : XAX, XCX, XDX, XSI, XDI, - "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)", -#ifdef __MMX__ - "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", -#endif - "memory", "cc"); -} diff --git a/gst/deinterlace2/tvtime/greedyh.c b/gst/deinterlace2/tvtime/greedyh.c deleted file mode 100644 index a5aafd18..00000000 --- a/gst/deinterlace2/tvtime/greedyh.c +++ /dev/null @@ -1,420 +0,0 @@ -/* - * - * GStreamer - * Copyright (C) 2004 Billy Biggs <vektor@dumbterm.net> - * Copyright (C) 2008 Sebastian Dröge <slomo@collabora.co.uk> - * - * 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. - */ - -/* - * Relicensed for GStreamer from GPL to LGPL with permit from Billy Biggs. - * See: http://bugzilla.gnome.org/show_bug.cgi?id=163578 - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "greedyhmacros.h" - -#include <stdlib.h> -#include "_stdint.h" -#include <string.h> - -#include "gst/gst.h" -#include "plugins.h" -#include "gstdeinterlace2.h" - -#define GST_TYPE_DEINTERLACE_METHOD_GREEDY_H (gst_deinterlace_method_greedy_h_get_type ()) -#define GST_IS_DEINTERLACE_METHOD_GREEDY_H(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_DEINTERLACE_METHOD_GREEDY_H)) -#define GST_IS_DEINTERLACE_METHOD_GREEDY_H_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_DEINTERLACE_METHOD_GREEDY_H)) -#define GST_DEINTERLACE_METHOD_GREEDY_H_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_DEINTERLACE_METHOD_GREEDY_H, GstDeinterlaceMethodGreedyHClass)) -#define GST_DEINTERLACE_METHOD_GREEDY_H(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_DEINTERLACE_METHOD_GREEDY_H, GstDeinterlaceMethodGreedyH)) -#define GST_DEINTERLACE_METHOD_GREEDY_H_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEINTERLACE_METHOD_GREEDY_H, GstDeinterlaceMethodGreedyHClass)) -#define GST_DEINTERLACE_METHOD_GREEDY_H_CAST(obj) ((GstDeinterlaceMethodGreedyH*)(obj)) - -GType gst_deinterlace_method_greedy_h_get_type (void); - -typedef struct -{ - GstDeinterlaceMethod parent; - - guint max_comb, motion_threshold, motion_sense; -} GstDeinterlaceMethodGreedyH; - -typedef struct -{ - GstDeinterlaceMethodClass parent_class; - void (*scanline) (GstDeinterlaceMethodGreedyH * self, uint8_t * L2, - uint8_t * L1, uint8_t * L3, uint8_t * L2P, uint8_t * Dest, int size); -} GstDeinterlaceMethodGreedyHClass; - -void -greedyDScaler_C (GstDeinterlaceMethodGreedyH * self, uint8_t * L1, uint8_t * L2, - uint8_t * L3, uint8_t * L2P, uint8_t * Dest, int size) -{ - int Pos; - uint8_t l1_l, l1_1_l, l3_l, l3_1_l; - uint8_t l1_c, l1_1_c, l3_c, l3_1_c; - uint8_t avg_l, avg_c, avg_l_1, avg_c_1; - uint8_t avg_l__1 = 0, avg_c__1 = 0; - uint8_t avg_s_l, avg_s_c; - uint8_t avg_sc_l, avg_sc_c; - uint8_t best_l, best_c; - uint16_t mov_l; - uint8_t out_l, out_c; - uint8_t l2_l, l2_c, lp2_l, lp2_c; - uint8_t l2_l_diff, l2_c_diff, lp2_l_diff, lp2_c_diff; - uint8_t min_l, min_c, max_l, max_c; - guint max_comb = self->max_comb; - guint motion_sense = self->motion_sense; - guint motion_threshold = self->motion_threshold; - - for (Pos = 0; Pos < size; Pos += 2) { - l1_l = L1[0]; - l1_c = L1[1]; - l3_l = L3[0]; - l3_c = L3[1]; - - if (Pos == size - 1) { - l1_1_l = l1_l; - l1_1_c = l1_c; - l3_1_l = l3_l; - l3_1_c = l3_c; - } else { - l1_1_l = L1[2]; - l1_1_c = L1[3]; - l3_1_l = L3[2]; - l3_1_c = L3[3]; - } - - /* Average of L1 and L3 */ - avg_l = (l1_l + l3_l) / 2; - avg_c = (l1_c + l3_c) / 2; - - if (Pos == 0) { - avg_l__1 = avg_l; - avg_c__1 = avg_c; - } - - /* Average of next L1 and next L3 */ - avg_l_1 = (l1_1_l + l3_1_l) / 2; - avg_c_1 = (l1_1_c + l3_1_c) / 2; - - /* Calculate average of one pixel forward and previous */ - avg_s_l = (avg_l__1 + avg_l_1) / 2; - avg_s_c = (avg_c__1 + avg_c_1) / 2; - - /* Calculate average of center and surrounding pixels */ - avg_sc_l = (avg_l + avg_s_l) / 2; - avg_sc_c = (avg_c + avg_s_c) / 2; - - /* move forward */ - avg_l__1 = avg_l; - avg_c__1 = avg_c; - - /* Get best L2/L2P, i.e. least diff from above average */ - l2_l = L2[0]; - l2_c = L2[1]; - lp2_l = L2P[0]; - lp2_c = L2P[1]; - - l2_l_diff = ABS (l2_l - avg_sc_l); - l2_c_diff = ABS (l2_c - avg_sc_c); - - lp2_l_diff = ABS (lp2_l - avg_sc_l); - lp2_c_diff = ABS (lp2_c - avg_sc_c); - - if (l2_l_diff > lp2_l_diff) - best_l = lp2_l; - else - best_l = l2_l; - - if (l2_c_diff > lp2_c_diff) - best_c = lp2_c; - else - best_c = l2_c; - - /* Clip this best L2/L2P by L1/L3 and allow to differ by GreedyMaxComb */ - max_l = MAX (l1_l, l3_l); - min_l = MIN (l1_l, l3_l); - - if (max_l < 256 - max_comb) - max_l += max_comb; - else - max_l = 255; - - if (min_l > max_comb) - min_l -= max_comb; - else - min_l = 0; - - max_c = MAX (l1_c, l3_c); - min_c = MIN (l1_c, l3_c); - - if (max_c < 256 - max_comb) - max_c += max_comb; - else - max_c = 255; - - if (min_c > max_comb) - min_c -= max_comb; - else - min_c = 0; - - out_l = CLAMP (best_l, min_l, max_l); - out_c = CLAMP (best_c, min_c, max_c); - - /* Do motion compensation for luma, i.e. how much - * the weave pixel differs */ - mov_l = ABS (l2_l - lp2_l); - if (mov_l > motion_threshold) - mov_l -= motion_threshold; - else - mov_l = 0; - - mov_l = mov_l * motion_sense; - if (mov_l > 256) - mov_l = 256; - - /* Weighted sum on clipped weave pixel and average */ - out_l = (out_l * (256 - mov_l) + avg_sc_l * mov_l) / 256; - - Dest[0] = out_l; - Dest[1] = out_c; - - Dest += 2; - L1 += 2; - L2 += 2; - L3 += 2; - L2P += 2; - } -} - -#ifdef BUILD_X86_ASM - -#define IS_MMXEXT -#define SIMD_TYPE MMXEXT -#define FUNCT_NAME greedyDScaler_MMXEXT -#include "greedyh.asm" -#undef SIMD_TYPE -#undef IS_MMXEXT -#undef FUNCT_NAME - -#define IS_3DNOW -#define SIMD_TYPE 3DNOW -#define FUNCT_NAME greedyDScaler_3DNOW -#include "greedyh.asm" -#undef SIMD_TYPE -#undef IS_3DNOW -#undef FUNCT_NAME - -#define IS_MMX -#define SIMD_TYPE MMX -#define FUNCT_NAME greedyDScaler_MMX -#include "greedyh.asm" -#undef SIMD_TYPE -#undef IS_MMX -#undef FUNCT_NAME - -#endif - -static void -deinterlace_frame_di_greedyh (GstDeinterlaceMethod * d_method, - GstDeinterlace2 * object, GstBuffer * outbuf) -{ - GstDeinterlaceMethodGreedyH *self = - GST_DEINTERLACE_METHOD_GREEDY_H (d_method); - GstDeinterlaceMethodGreedyHClass *klass = - GST_DEINTERLACE_METHOD_GREEDY_H_GET_CLASS (self); - int InfoIsOdd = 0; - int Line; - unsigned int Pitch = object->field_stride; - - unsigned char *L1; // ptr to Line1, of 3 - unsigned char *L2; // ptr to Line2, the weave line - unsigned char *L3; // ptr to Line3 - - unsigned char *L2P; // ptr to prev Line2 - unsigned char *Dest = GST_BUFFER_DATA (outbuf); - - // copy first even line no matter what, and the first odd line if we're - // processing an EVEN field. (note diff from other deint rtns.) - - if (object->field_history[object->history_count - 1].flags == - PICTURE_INTERLACED_BOTTOM) { - InfoIsOdd = 1; - - L1 = GST_BUFFER_DATA (object->field_history[object->history_count - 2].buf); - L2 = GST_BUFFER_DATA (object->field_history[object->history_count - 1].buf); - L3 = L1 + Pitch; - L2P = - GST_BUFFER_DATA (object->field_history[object->history_count - 3].buf); - - // copy first even line - oil_memcpy (Dest, L1, object->row_stride); - Dest += object->row_stride; - } else { - InfoIsOdd = 0; - L1 = GST_BUFFER_DATA (object->field_history[object->history_count - 2].buf); - L2 = GST_BUFFER_DATA (object->field_history[object->history_count - - 1].buf) + Pitch; - L3 = L1 + Pitch; - L2P = - GST_BUFFER_DATA (object->field_history[object->history_count - 3].buf) + - Pitch; - - // copy first even line - oil_memcpy (Dest, GST_BUFFER_DATA (object->field_history[0].buf), - object->row_stride); - Dest += object->row_stride; - // then first odd line - oil_memcpy (Dest, L1, object->row_stride); - Dest += object->row_stride; - } - - for (Line = 0; Line < (object->field_height - 1); ++Line) { - klass->scanline (self, L1, L2, L3, L2P, Dest, object->row_stride); - Dest += object->row_stride; - oil_memcpy (Dest, L3, object->row_stride); - Dest += object->row_stride; - - L1 += Pitch; - L2 += Pitch; - L3 += Pitch; - L2P += Pitch; - } - - if (InfoIsOdd) { - oil_memcpy (Dest, L2, object->row_stride); - } -} - -G_DEFINE_TYPE (GstDeinterlaceMethodGreedyH, gst_deinterlace_method_greedy_h, - GST_TYPE_DEINTERLACE_METHOD); - -enum -{ - ARG_0, - ARG_MAX_COMB, - ARG_MOTION_THRESHOLD, - ARG_MOTION_SENSE -}; - -static void -gst_deinterlace_method_greedy_h_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstDeinterlaceMethodGreedyH *self = GST_DEINTERLACE_METHOD_GREEDY_H (object); - - switch (prop_id) { - case ARG_MAX_COMB: - self->max_comb = g_value_get_uint (value); - break; - case ARG_MOTION_THRESHOLD: - self->motion_threshold = g_value_get_uint (value); - break; - case ARG_MOTION_SENSE: - self->motion_sense = g_value_get_uint (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -gst_deinterlace_method_greedy_h_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstDeinterlaceMethodGreedyH *self = GST_DEINTERLACE_METHOD_GREEDY_H (object); - - switch (prop_id) { - case ARG_MAX_COMB: - g_value_set_uint (value, self->max_comb); - break; - case ARG_MOTION_THRESHOLD: - g_value_set_uint (value, self->motion_threshold); - break; - case ARG_MOTION_SENSE: - g_value_set_uint (value, self->motion_sense); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -gst_deinterlace_method_greedy_h_class_init (GstDeinterlaceMethodGreedyHClass * - klass) -{ - GstDeinterlaceMethodClass *dim_class = (GstDeinterlaceMethodClass *) klass; - GObjectClass *gobject_class = (GObjectClass *) klass; -#ifdef BUILD_X86_ASM - guint cpu_flags = oil_cpu_get_flags (); -#endif - - gobject_class->set_property = gst_deinterlace_method_greedy_h_set_property; - gobject_class->get_property = gst_deinterlace_method_greedy_h_get_property; - - g_object_class_install_property (gobject_class, ARG_MAX_COMB, - g_param_spec_uint ("max-comb", - "Max comb", - "Max Comb", 0, 255, 5, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) - ); - - g_object_class_install_property (gobject_class, ARG_MOTION_THRESHOLD, - g_param_spec_uint ("motion-threshold", - "Motion Threshold", - "Motion Threshold", - 0, 255, 25, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) - ); - - g_object_class_install_property (gobject_class, ARG_MOTION_SENSE, - g_param_spec_uint ("motion-sense", - "Motion Sense", - "Motion Sense", - 0, 255, 30, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) - ); - - dim_class->fields_required = 4; - dim_class->deinterlace_frame = deinterlace_frame_di_greedyh; - dim_class->name = "Motion Adaptive: Advanced Detection"; - dim_class->nick = "greedyh"; - dim_class->latency = 1; - -#ifdef BUILD_X86_ASM - if (cpu_flags & OIL_IMPL_FLAG_MMXEXT) { - klass->scanline = greedyDScaler_MMXEXT; - } else if (cpu_flags & OIL_IMPL_FLAG_3DNOW) { - klass->scanline = greedyDScaler_3DNOW; - } else if (cpu_flags & OIL_IMPL_FLAG_MMX) { - klass->scanline = greedyDScaler_MMX; - } else { - klass->scanline = greedyDScaler_C; - } -#else - klass->scanline = greedyDScaler_C; -#endif -} - -static void -gst_deinterlace_method_greedy_h_init (GstDeinterlaceMethodGreedyH * self) -{ - self->max_comb = 5; - self->motion_threshold = 25; - self->motion_sense = 30; -} diff --git a/gst/deinterlace2/tvtime/greedyhmacros.h b/gst/deinterlace2/tvtime/greedyhmacros.h deleted file mode 100644 index 0386c28e..00000000 --- a/gst/deinterlace2/tvtime/greedyhmacros.h +++ /dev/null @@ -1,75 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// Copyright (c) 2001 Tom Barry. All rights reserved. -///////////////////////////////////////////////////////////////////////////// -// -// This file is subject to the terms of the GNU General Public License as -// published by the Free Software Foundation. A copy of this license is -// included with this software distribution in the file COPYING. If you -// do not have a copy, you may obtain a copy by writing to the Free -// Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. -// -// This software 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 General Public License for more details -// -///////////////////////////////////////////////////////////////////////////// - -// Define a few macros for CPU dependent instructions. -// I suspect I don't really understand how the C macro preprocessor works but -// this seems to get the job done. // TRB 7/01 - -// BEFORE USING THESE YOU MUST SET: - -// #define SIMD_TYPE MMXEXT (or MMX or 3DNOW) - -// some macros for pavgb instruction -// V_PAVGB(mmr1, mmr2, mmr work register, smask) mmr2 may = mmrw if you can trash it - -#define V_PAVGB_MMX(mmr1, mmr2, mmrw, smask) \ - "movq "mmr2", "mmrw"\n\t" \ - "pand "smask", "mmrw"\n\t" \ - "psrlw $1, "mmrw"\n\t" \ - "pand "smask", "mmr1"\n\t" \ - "psrlw $1, "mmr1"\n\t" \ - "paddusb "mmrw", "mmr1"\n\t" -#define V_PAVGB_MMXEXT(mmr1, mmr2, mmrw, smask) "pavgb "mmr2", "mmr1"\n\t" -#define V_PAVGB_3DNOW(mmr1, mmr2, mmrw, smask) "pavgusb "mmr2", "mmr1"\n\t" -#define V_PAVGB(mmr1, mmr2, mmrw, smask) V_PAVGB2(mmr1, mmr2, mmrw, smask, SIMD_TYPE) -#define V_PAVGB2(mmr1, mmr2, mmrw, smask, simd_type) V_PAVGB3(mmr1, mmr2, mmrw, smask, simd_type) -#define V_PAVGB3(mmr1, mmr2, mmrw, smask, simd_type) V_PAVGB_##simd_type(mmr1, mmr2, mmrw, smask) - -// some macros for pmaxub instruction -#define V_PMAXUB_MMX(mmr1, mmr2) \ - "psubusb "mmr2", "mmr1"\n\t" \ - "paddusb "mmr2", "mmr1"\n\t" -#define V_PMAXUB_MMXEXT(mmr1, mmr2) "pmaxub "mmr2", "mmr1"\n\t" -#define V_PMAXUB_3DNOW(mmr1, mmr2) V_PMAXUB_MMX(mmr1, mmr2) // use MMX version -#define V_PMAXUB(mmr1, mmr2) V_PMAXUB2(mmr1, mmr2, SIMD_TYPE) -#define V_PMAXUB2(mmr1, mmr2, simd_type) V_PMAXUB3(mmr1, mmr2, simd_type) -#define V_PMAXUB3(mmr1, mmr2, simd_type) V_PMAXUB_##simd_type(mmr1, mmr2) - -// some macros for pminub instruction -// V_PMINUB(mmr1, mmr2, mmr work register) mmr2 may NOT = mmrw -#define V_PMINUB_MMX(mmr1, mmr2, mmrw) \ - "pcmpeqb "mmrw", "mmrw"\n\t" \ - "psubusb "mmr2", "mmrw"\n\t" \ - "paddusb "mmrw", "mmr1"\n\t" \ - "psubusb "mmrw", "mmr1"\n\t" -#define V_PMINUB_MMXEXT(mmr1, mmr2, mmrw) "pminub "mmr2", "mmr1"\n\t" -#define V_PMINUB_3DNOW(mmr1, mmr2, mmrw) V_PMINUB_MMX(mmr1, mmr2, mmrw) // use MMX version -#define V_PMINUB(mmr1, mmr2, mmrw) V_PMINUB2(mmr1, mmr2, mmrw, SIMD_TYPE) -#define V_PMINUB2(mmr1, mmr2, mmrw, simd_type) V_PMINUB3(mmr1, mmr2, mmrw, simd_type) -#define V_PMINUB3(mmr1, mmr2, mmrw, simd_type) V_PMINUB_##simd_type(mmr1, mmr2, mmrw) - -// some macros for movntq instruction -// V_MOVNTQ(mmr1, mmr2) -#define V_MOVNTQ_MMX(mmr1, mmr2) "movq "mmr2", "mmr1"\n\t" -#define V_MOVNTQ_3DNOW(mmr1, mmr2) "movq "mmr2", "mmr1"\n\t" -#define V_MOVNTQ_MMXEXT(mmr1, mmr2) "movntq "mmr2", "mmr1"\n\t" -#define V_MOVNTQ(mmr1, mmr2) V_MOVNTQ2(mmr1, mmr2, SIMD_TYPE) -#define V_MOVNTQ2(mmr1, mmr2, simd_type) V_MOVNTQ3(mmr1, mmr2, simd_type) -#define V_MOVNTQ3(mmr1, mmr2, simd_type) V_MOVNTQ_##simd_type(mmr1, mmr2) - -// end of macros - diff --git a/gst/deinterlace2/tvtime/linear.c b/gst/deinterlace2/tvtime/linear.c deleted file mode 100644 index 42403e49..00000000 --- a/gst/deinterlace2/tvtime/linear.c +++ /dev/null @@ -1,214 +0,0 @@ -/** - * Copyright (C) 2002 Billy Biggs <vektor@dumbterm.net>. - * Copyright (C) 2008 Sebastian Dröge <slomo@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser 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 "_stdint.h" -#include "gstdeinterlace2.h" -#include <string.h> - -#define GST_TYPE_DEINTERLACE_METHOD_LINEAR (gst_deinterlace_method_linear_get_type ()) -#define GST_IS_DEINTERLACE_METHOD_LINEAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_DEINTERLACE_METHOD_LINEAR)) -#define GST_IS_DEINTERLACE_METHOD_LINEAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_DEINTERLACE_METHOD_LINEAR)) -#define GST_DEINTERLACE_METHOD_LINEAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_DEINTERLACE_METHOD_LINEAR, GstDeinterlaceMethodLinearClass)) -#define GST_DEINTERLACE_METHOD_LINEAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_DEINTERLACE_METHOD_LINEAR, GstDeinterlaceMethodLinear)) -#define GST_DEINTERLACE_METHOD_LINEAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEINTERLACE_METHOD_LINEAR, GstDeinterlaceMethodLinearClass)) -#define GST_DEINTERLACE_METHOD_LINEAR_CAST(obj) ((GstDeinterlaceMethodLinear*)(obj)) - -GType gst_deinterlace_method_linear_get_type (void); - -typedef GstDeinterlaceSimpleMethod GstDeinterlaceMethodLinear; - -typedef GstDeinterlaceSimpleMethodClass GstDeinterlaceMethodLinearClass; - -static void -deinterlace_scanline_linear_c (GstDeinterlaceMethod * self, - GstDeinterlace2 * parent, guint8 * out, - GstDeinterlaceScanlineData * scanlines, gint width) -{ - gint i; - - width *= 2; - for (i = 0; i < width; i++) - out[i] = (scanlines->t0[i] + scanlines->b0[i]) / 2; -} - -#ifdef BUILD_X86_ASM -#include "mmx.h" -static void -deinterlace_scanline_linear_mmx (GstDeinterlaceMethod * self, - GstDeinterlace2 * parent, guint8 * out, - GstDeinterlaceScanlineData * scanlines, gint width) -{ - const mmx_t shiftmask = { 0xfefffefffefffeffULL }; /* To avoid shifting chroma to luma. */ - int i; - guint8 *bot = scanlines->b0, *top = scanlines->t0; - - for (i = width / 16; i; --i) { - movq_m2r (*bot, mm0); - movq_m2r (*top, mm1); - movq_m2r (*(bot + 8), mm2); - movq_m2r (*(top + 8), mm3); - movq_m2r (*(bot + 16), mm4); - movq_m2r (*(top + 16), mm5); - movq_m2r (*(bot + 24), mm6); - movq_m2r (*(top + 24), mm7); - pand_m2r (shiftmask, mm0); - pand_m2r (shiftmask, mm1); - pand_m2r (shiftmask, mm2); - pand_m2r (shiftmask, mm3); - pand_m2r (shiftmask, mm4); - pand_m2r (shiftmask, mm5); - pand_m2r (shiftmask, mm6); - pand_m2r (shiftmask, mm7); - psrlw_i2r (1, mm0); - psrlw_i2r (1, mm1); - psrlw_i2r (1, mm2); - psrlw_i2r (1, mm3); - psrlw_i2r (1, mm4); - psrlw_i2r (1, mm5); - psrlw_i2r (1, mm6); - psrlw_i2r (1, mm7); - paddb_r2r (mm1, mm0); - paddb_r2r (mm3, mm2); - paddb_r2r (mm5, mm4); - paddb_r2r (mm7, mm6); - movq_r2m (mm0, *out); - movq_r2m (mm2, *(out + 8)); - movq_r2m (mm4, *(out + 16)); - movq_r2m (mm6, *(out + 24)); - out += 32; - top += 32; - bot += 32; - } - width = (width & 0xf); - - for (i = width / 4; i; --i) { - movq_m2r (*bot, mm0); - movq_m2r (*top, mm1); - pand_m2r (shiftmask, mm0); - pand_m2r (shiftmask, mm1); - psrlw_i2r (1, mm0); - psrlw_i2r (1, mm1); - paddb_r2r (mm1, mm0); - movq_r2m (mm0, *out); - out += 8; - top += 8; - bot += 8; - } - width = width & 0x7; - - /* Handle last few pixels. */ - for (i = width * 2; i; --i) { - *out++ = ((*top++) + (*bot++)) >> 1; - } - - emms (); -} - -#include "sse.h" -static void -deinterlace_scanline_linear_mmxext (GstDeinterlaceMethod * self, - GstDeinterlace2 * parent, guint8 * out, - GstDeinterlaceScanlineData * scanlines, gint width) -{ - gint i; - guint8 *bot = scanlines->b0, *top = scanlines->t0; - - for (i = width / 16; i; --i) { - movq_m2r (*bot, mm0); - movq_m2r (*top, mm1); - movq_m2r (*(bot + 8), mm2); - movq_m2r (*(top + 8), mm3); - movq_m2r (*(bot + 16), mm4); - movq_m2r (*(top + 16), mm5); - movq_m2r (*(bot + 24), mm6); - movq_m2r (*(top + 24), mm7); - pavgb_r2r (mm1, mm0); - pavgb_r2r (mm3, mm2); - pavgb_r2r (mm5, mm4); - pavgb_r2r (mm7, mm6); - movntq_r2m (mm0, *out); - movntq_r2m (mm2, *(out + 8)); - movntq_r2m (mm4, *(out + 16)); - movntq_r2m (mm6, *(out + 24)); - out += 32; - top += 32; - bot += 32; - } - width = (width & 0xf); - - for (i = width / 4; i; --i) { - movq_m2r (*bot, mm0); - movq_m2r (*top, mm1); - pavgb_r2r (mm1, mm0); - movntq_r2m (mm0, *out); - out += 8; - top += 8; - bot += 8; - } - width = width & 0x7; - - /* Handle last few pixels. */ - for (i = width * 2; i; --i) { - *out++ = ((*top++) + (*bot++)) >> 1; - } - - emms (); -} - -#endif - -G_DEFINE_TYPE (GstDeinterlaceMethodLinear, gst_deinterlace_method_linear, - GST_TYPE_DEINTERLACE_SIMPLE_METHOD); - -static void -gst_deinterlace_method_linear_class_init (GstDeinterlaceMethodLinearClass * - klass) -{ - GstDeinterlaceMethodClass *dim_class = (GstDeinterlaceMethodClass *) klass; - GstDeinterlaceSimpleMethodClass *dism_class = - (GstDeinterlaceSimpleMethodClass *) klass; -#ifdef BUILD_X86_ASM - guint cpu_flags = oil_cpu_get_flags (); -#endif - - dim_class->fields_required = 1; - dim_class->name = "Television: Full resolution"; - dim_class->nick = "linear"; - dim_class->latency = 0; - - dism_class->interpolate_scanline = deinterlace_scanline_linear_c; - -#ifdef BUILD_X86_ASM - if (cpu_flags & OIL_IMPL_FLAG_MMXEXT) { - dism_class->interpolate_scanline = deinterlace_scanline_linear_mmxext; - } else if (cpu_flags & OIL_IMPL_FLAG_MMXEXT) { - dism_class->interpolate_scanline = deinterlace_scanline_linear_mmx; - } -#endif -} - -static void -gst_deinterlace_method_linear_init (GstDeinterlaceMethodLinear * self) -{ -} diff --git a/gst/deinterlace2/tvtime/linearblend.c b/gst/deinterlace2/tvtime/linearblend.c deleted file mode 100644 index c25c4d0e..00000000 --- a/gst/deinterlace2/tvtime/linearblend.c +++ /dev/null @@ -1,231 +0,0 @@ -/** - * Linear blend deinterlacing plugin. The idea for this algorithm came - * from the linear blend deinterlacer which originated in the mplayer - * sources. - * - * Copyright (C) 2002 Billy Biggs <vektor@dumbterm.net>. - * Copyright (C) 2008 Sebastian Dröge <slomo@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser 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 "_stdint.h" -#include "gstdeinterlace2.h" -#include <string.h> - -#define GST_TYPE_DEINTERLACE_METHOD_LINEAR_BLEND (gst_deinterlace_method_linear_blend_get_type ()) -#define GST_IS_DEINTERLACE_METHOD_LINEAR_BLEND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_DEINTERLACE_METHOD_LINEAR_BLEND)) -#define GST_IS_DEINTERLACE_METHOD_LINEAR_BLEND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_DEINTERLACE_METHOD_LINEAR_BLEND)) -#define GST_DEINTERLACE_METHOD_LINEAR_BLEND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_DEINTERLACE_METHOD_LINEAR_BLEND, GstDeinterlaceMethodLinearBlendClass)) -#define GST_DEINTERLACE_METHOD_LINEAR_BLEND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_DEINTERLACE_METHOD_LINEAR_BLEND, GstDeinterlaceMethodLinearBlend)) -#define GST_DEINTERLACE_METHOD_LINEAR_BLEND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEINTERLACE_METHOD_LINEAR_BLEND, GstDeinterlaceMethodLinearBlendClass)) -#define GST_DEINTERLACE_METHOD_LINEAR_BLEND_CAST(obj) ((GstDeinterlaceMethodLinearBlend*)(obj)) - -GType gst_deinterlace_method_linear_blend_get_type (void); - -typedef GstDeinterlaceSimpleMethod GstDeinterlaceMethodLinearBlend; - -typedef GstDeinterlaceSimpleMethodClass GstDeinterlaceMethodLinearBlendClass; - - -static inline void -deinterlace_scanline_linear_blend_c (GstDeinterlaceMethod * self, - GstDeinterlace2 * parent, guint8 * out, - GstDeinterlaceScanlineData * scanlines, gint width) -{ - guint8 *t0 = scanlines->t0; - guint8 *b0 = scanlines->b0; - guint8 *m1 = scanlines->m1; - - width *= 2; - - while (width--) { - *out++ = (*t0++ + *b0++ + (*m1++ << 1)) >> 2; - } -} - -static inline void -deinterlace_scanline_linear_blend2_c (GstDeinterlaceMethod * self, - GstDeinterlace2 * parent, guint8 * out, - GstDeinterlaceScanlineData * scanlines, gint width) -{ - guint8 *m0 = scanlines->m0; - guint8 *t1 = scanlines->t1; - guint8 *b1 = scanlines->b1; - - width *= 2; - while (width--) { - *out++ = (*t1++ + *b1++ + (*m0++ << 1)) >> 2; - } -} - -#ifdef BUILD_X86_ASM -#include "mmx.h" -static inline void -deinterlace_scanline_linear_blend_mmx (GstDeinterlaceMethod * self, - GstDeinterlace2 * parent, guint8 * out, - GstDeinterlaceScanlineData * scanlines, gint width) -{ - guint8 *t0 = scanlines->t0; - guint8 *b0 = scanlines->b0; - guint8 *m1 = scanlines->m1; - gint i; - - // Get width in bytes. - width *= 2; - i = width / 8; - width -= i * 8; - - pxor_r2r (mm7, mm7); - while (i--) { - movd_m2r (*t0, mm0); - movd_m2r (*b0, mm1); - movd_m2r (*m1, mm2); - - movd_m2r (*(t0 + 4), mm3); - movd_m2r (*(b0 + 4), mm4); - movd_m2r (*(m1 + 4), mm5); - - punpcklbw_r2r (mm7, mm0); - punpcklbw_r2r (mm7, mm1); - punpcklbw_r2r (mm7, mm2); - - punpcklbw_r2r (mm7, mm3); - punpcklbw_r2r (mm7, mm4); - punpcklbw_r2r (mm7, mm5); - - psllw_i2r (1, mm2); - psllw_i2r (1, mm5); - paddw_r2r (mm0, mm2); - paddw_r2r (mm3, mm5); - paddw_r2r (mm1, mm2); - paddw_r2r (mm4, mm5); - psrlw_i2r (2, mm2); - psrlw_i2r (2, mm5); - packuswb_r2r (mm2, mm2); - packuswb_r2r (mm5, mm5); - - movd_r2m (mm2, *out); - movd_r2m (mm5, *(out + 4)); - out += 8; - t0 += 8; - b0 += 8; - m1 += 8; - } - while (width--) { - *out++ = (*t0++ + *b0++ + (*m1++ << 1)) >> 2; - } - emms (); -} - -static inline void -deinterlace_scanline_linear_blend2_mmx (GstDeinterlaceMethod * self, - GstDeinterlace2 * parent, guint8 * out, - GstDeinterlaceScanlineData * scanlines, gint width) -{ - guint8 *m0 = scanlines->m0; - guint8 *t1 = scanlines->t1; - guint8 *b1 = scanlines->b1; - gint i; - - // Get width in bytes. - width *= 2; - i = width / 8; - width -= i * 8; - - pxor_r2r (mm7, mm7); - while (i--) { - movd_m2r (*t1, mm0); - movd_m2r (*b1, mm1); - movd_m2r (*m0, mm2); - - movd_m2r (*(t1 + 4), mm3); - movd_m2r (*(b1 + 4), mm4); - movd_m2r (*(m0 + 4), mm5); - - punpcklbw_r2r (mm7, mm0); - punpcklbw_r2r (mm7, mm1); - punpcklbw_r2r (mm7, mm2); - - punpcklbw_r2r (mm7, mm3); - punpcklbw_r2r (mm7, mm4); - punpcklbw_r2r (mm7, mm5); - - psllw_i2r (1, mm2); - psllw_i2r (1, mm5); - paddw_r2r (mm0, mm2); - paddw_r2r (mm3, mm5); - paddw_r2r (mm1, mm2); - paddw_r2r (mm4, mm5); - psrlw_i2r (2, mm2); - psrlw_i2r (2, mm5); - packuswb_r2r (mm2, mm2); - packuswb_r2r (mm5, mm5); - - movd_r2m (mm2, *out); - movd_r2m (mm5, *(out + 4)); - out += 8; - t1 += 8; - b1 += 8; - m0 += 8; - } - while (width--) { - *out++ = (*t1++ + *b1++ + (*m0++ << 1)) >> 2; - } - emms (); -} - -#endif - -G_DEFINE_TYPE (GstDeinterlaceMethodLinearBlend, - gst_deinterlace_method_linear_blend, GST_TYPE_DEINTERLACE_SIMPLE_METHOD); - -static void - gst_deinterlace_method_linear_blend_class_init - (GstDeinterlaceMethodLinearBlendClass * klass) -{ - GstDeinterlaceMethodClass *dim_class = (GstDeinterlaceMethodClass *) klass; - GstDeinterlaceSimpleMethodClass *dism_class = - (GstDeinterlaceSimpleMethodClass *) klass; -#ifdef BUILD_X86_ASM - guint cpu_flags = oil_cpu_get_flags (); -#endif - - dim_class->fields_required = 2; - dim_class->name = "Blur: Temporal"; - dim_class->nick = "linearblend"; - dim_class->latency = 0; - - dism_class->interpolate_scanline = deinterlace_scanline_linear_blend_c; - dism_class->copy_scanline = deinterlace_scanline_linear_blend2_c; - -#ifdef BUILD_X86_ASM - if (cpu_flags & OIL_IMPL_FLAG_MMX) { - dism_class->interpolate_scanline = deinterlace_scanline_linear_blend_mmx; - dism_class->copy_scanline = deinterlace_scanline_linear_blend2_mmx; - } -#endif -} - -static void -gst_deinterlace_method_linear_blend_init (GstDeinterlaceMethodLinearBlend * - self) -{ -} diff --git a/gst/deinterlace2/tvtime/mmx.h b/gst/deinterlace2/tvtime/mmx.h deleted file mode 100644 index 3627e61b..00000000 --- a/gst/deinterlace2/tvtime/mmx.h +++ /dev/null @@ -1,723 +0,0 @@ -/* mmx.h - - MultiMedia eXtensions GCC interface library for IA32. - - To use this library, simply include this header file - and compile with GCC. You MUST have inlining enabled - in order for mmx_ok() to work; this can be done by - simply using -O on the GCC command line. - - Compiling with -DMMX_TRACE will cause detailed trace - output to be sent to stderr for each mmx operation. - This adds lots of code, and obviously slows execution to - a crawl, but can be very useful for debugging. - - THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT - LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY - AND FITNESS FOR ANY PARTICULAR PURPOSE. - - 1997-98 by H. Dietz and R. Fisher - - History: - 97-98* R.Fisher Early versions - 980501 R.Fisher Original Release - 980611* H.Dietz Rewrite, correctly implementing inlines, and - R.Fisher including direct register accesses. - 980616 R.Fisher Release of 980611 as 980616. - 980714 R.Fisher Minor corrections to Makefile, etc. - 980715 R.Fisher mmx_ok() now prevents optimizer from using - clobbered values. - mmx_ok() now checks if cpuid instruction is - available before trying to use it. - 980726* R.Fisher mm_support() searches for AMD 3DNow, Cyrix - Extended MMX, and standard MMX. It returns a - value which is positive if any of these are - supported, and can be masked with constants to - see which. mmx_ok() is now a call to this - 980726* R.Fisher Added i2r support for shift functions - 980919 R.Fisher Fixed AMD extended feature recognition bug. - 980921 R.Fisher Added definition/check for _MMX_H. - Added "float s[2]" to mmx_t for use with - 3DNow and EMMX. So same mmx_t can be used. - 981013 R.Fisher Fixed cpuid function 1 bug (looked at wrong reg) - Fixed psllq_i2r error in mmxtest.c - - * Unreleased (internal or interim) versions - - Notes: - It appears that the latest gas has the pand problem fixed, therefore - I'll undefine BROKEN_PAND by default. - String compares may be quicker than the multiple test/jumps in vendor - test sequence in mmx_ok(), but I'm not concerned with that right now. - - Acknowledgments: - Jussi Laako for pointing out the errors ultimately found to be - connected to the failure to notify the optimizer of clobbered values. - Roger Hardiman for reminding us that CPUID isn't everywhere, and that - someone may actually try to use this on a machine without CPUID. - Also for suggesting code for checking this. - Robert Dale for pointing out the AMD recognition bug. - Jimmy Mayfield and Carl Witty for pointing out the Intel recognition - bug. - Carl Witty for pointing out the psllq_i2r test bug. -*/ - -#ifndef _MMX_H -#define _MMX_H - -/*#define MMX_TRACE */ - -/* Warning: at this writing, the version of GAS packaged - with most Linux distributions does not handle the - parallel AND operation mnemonic correctly. If the - symbol BROKEN_PAND is defined, a slower alternative - coding will be used. If execution of mmxtest results - in an illegal instruction fault, define this symbol. -*/ -#undef BROKEN_PAND - - -/* The type of an value that fits in an MMX register - (note that long long constant values MUST be suffixed - by LL and unsigned long long values by ULL, lest - they be truncated by the compiler) -*/ -typedef union { - long long q; /* Quadword (64-bit) value */ - unsigned long long uq; /* Unsigned Quadword */ - int d[2]; /* 2 Doubleword (32-bit) values */ - unsigned int ud[2]; /* 2 Unsigned Doubleword */ - short w[4]; /* 4 Word (16-bit) values */ - unsigned short uw[4]; /* 4 Unsigned Word */ - char b[8]; /* 8 Byte (8-bit) values */ - unsigned char ub[8]; /* 8 Unsigned Byte */ - float s[2]; /* Single-precision (32-bit) value */ -} mmx_t; - - -/* Function to test if multimedia instructions are supported... -*/ -inline extern int -mm_support(void) -{ - /* Returns 1 if MMX instructions are supported, - 3 if Cyrix MMX and Extended MMX instructions are supported - 5 if AMD MMX and 3DNow! instructions are supported - 0 if hardware does not support any of these - */ - register int rval = 0; - - __asm__ __volatile__ ( - /* See if CPUID instruction is supported ... */ - /* ... Get copies of EFLAGS into eax and ecx */ - "pushf\n\t" - "popl %%eax\n\t" - "movl %%eax, %%ecx\n\t" - - /* ... Toggle the ID bit in one copy and store */ - /* to the EFLAGS reg */ - "xorl $0x200000, %%eax\n\t" - "push %%eax\n\t" - "popf\n\t" - - /* ... Get the (hopefully modified) EFLAGS */ - "pushf\n\t" - "popl %%eax\n\t" - - /* ... Compare and test result */ - "xorl %%eax, %%ecx\n\t" - "testl $0x200000, %%ecx\n\t" - "jz NotSupported1\n\t" /* Nothing supported */ - - - /* Get standard CPUID information, and - go to a specific vendor section */ - "movl $0, %%eax\n\t" - "cpuid\n\t" - - /* Check for Intel */ - "cmpl $0x756e6547, %%ebx\n\t" - "jne TryAMD\n\t" - "cmpl $0x49656e69, %%edx\n\t" - "jne TryAMD\n\t" - "cmpl $0x6c65746e, %%ecx\n" - "jne TryAMD\n\t" - "jmp Intel\n\t" - - /* Check for AMD */ - "\nTryAMD:\n\t" - "cmpl $0x68747541, %%ebx\n\t" - "jne TryCyrix\n\t" - "cmpl $0x69746e65, %%edx\n\t" - "jne TryCyrix\n\t" - "cmpl $0x444d4163, %%ecx\n" - "jne TryCyrix\n\t" - "jmp AMD\n\t" - - /* Check for Cyrix */ - "\nTryCyrix:\n\t" - "cmpl $0x69727943, %%ebx\n\t" - "jne NotSupported2\n\t" - "cmpl $0x736e4978, %%edx\n\t" - "jne NotSupported3\n\t" - "cmpl $0x64616574, %%ecx\n\t" - "jne NotSupported4\n\t" - /* Drop through to Cyrix... */ - - - /* Cyrix Section */ - /* See if extended CPUID is supported */ - "movl $0x80000000, %%eax\n\t" - "cpuid\n\t" - "cmpl $0x80000000, %%eax\n\t" - "jl MMXtest\n\t" /* Try standard CPUID instead */ - - /* Extended CPUID supported, so get extended features */ - "movl $0x80000001, %%eax\n\t" - "cpuid\n\t" - "testl $0x00800000, %%eax\n\t" /* Test for MMX */ - "jz NotSupported5\n\t" /* MMX not supported */ - "testl $0x01000000, %%eax\n\t" /* Test for Ext'd MMX */ - "jnz EMMXSupported\n\t" - "movl $1, %0:\n\n\t" /* MMX Supported */ - "jmp Return\n\n" - "EMMXSupported:\n\t" - "movl $3, %0:\n\n\t" /* EMMX and MMX Supported */ - "jmp Return\n\t" - - - /* AMD Section */ - "AMD:\n\t" - - /* See if extended CPUID is supported */ - "movl $0x80000000, %%eax\n\t" - "cpuid\n\t" - "cmpl $0x80000000, %%eax\n\t" - "jl MMXtest\n\t" /* Try standard CPUID instead */ - - /* Extended CPUID supported, so get extended features */ - "movl $0x80000001, %%eax\n\t" - "cpuid\n\t" - "testl $0x00800000, %%edx\n\t" /* Test for MMX */ - "jz NotSupported6\n\t" /* MMX not supported */ - "testl $0x80000000, %%edx\n\t" /* Test for 3DNow! */ - "jnz ThreeDNowSupported\n\t" - "movl $1, %0:\n\n\t" /* MMX Supported */ - "jmp Return\n\n" - "ThreeDNowSupported:\n\t" - "movl $5, %0:\n\n\t" /* 3DNow! and MMX Supported */ - "jmp Return\n\t" - - - /* Intel Section */ - "Intel:\n\t" - - /* Check for MMX */ - "MMXtest:\n\t" - "movl $1, %%eax\n\t" - "cpuid\n\t" - "testl $0x00800000, %%edx\n\t" /* Test for MMX */ - "jz NotSupported7\n\t" /* MMX Not supported */ - "movl $1, %0:\n\n\t" /* MMX Supported */ - "jmp Return\n\t" - - /* Nothing supported */ - "\nNotSupported1:\n\t" - "#movl $101, %0:\n\n\t" - "\nNotSupported2:\n\t" - "#movl $102, %0:\n\n\t" - "\nNotSupported3:\n\t" - "#movl $103, %0:\n\n\t" - "\nNotSupported4:\n\t" - "#movl $104, %0:\n\n\t" - "\nNotSupported5:\n\t" - "#movl $105, %0:\n\n\t" - "\nNotSupported6:\n\t" - "#movl $106, %0:\n\n\t" - "\nNotSupported7:\n\t" - "#movl $107, %0:\n\n\t" - "movl $0, %0:\n\n\t" - - "Return:\n\t" - : "=a" (rval) - : /* no input */ - : "eax", "ebx", "ecx", "edx" - ); - - /* Return */ - return(rval); -} - -/* Function to test if mmx instructions are supported... -*/ -inline extern int -mmx_ok(void) -{ - /* Returns 1 if MMX instructions are supported, 0 otherwise */ - return ( mm_support() & 0x1 ); -} - - -/* Helper functions for the instruction macros that follow... - (note that memory-to-register, m2r, instructions are nearly - as efficient as register-to-register, r2r, instructions; - however, memory-to-memory instructions are really simulated - as a convenience, and are only 1/3 as efficient) -*/ -#ifdef MMX_TRACE - -/* Include the stuff for printing a trace to stderr... -*/ - -#include <stdio.h> - -#define mmx_i2r(op, imm, reg) \ - { \ - mmx_t mmx_trace; \ - mmx_trace = (imm); \ - fprintf(stderr, #op "_i2r(" #imm "=0x%016llx, ", mmx_trace.q); \ - __asm__ __volatile__ ("movq %%" #reg ", %0" \ - : "=X" (mmx_trace) \ - : /* nothing */ ); \ - fprintf(stderr, #reg "=0x%016llx) => ", mmx_trace.q); \ - __asm__ __volatile__ (#op " %0, %%" #reg \ - : /* nothing */ \ - : "X" (imm)); \ - __asm__ __volatile__ ("movq %%" #reg ", %0" \ - : "=X" (mmx_trace) \ - : /* nothing */ ); \ - fprintf(stderr, #reg "=0x%016llx\n", mmx_trace.q); \ - } - -#define mmx_m2r(op, mem, reg) \ - { \ - mmx_t mmx_trace; \ - mmx_trace = (mem); \ - fprintf(stderr, #op "_m2r(" #mem "=0x%016llx, ", mmx_trace.q); \ - __asm__ __volatile__ ("movq %%" #reg ", %0" \ - : "=X" (mmx_trace) \ - : /* nothing */ ); \ - fprintf(stderr, #reg "=0x%016llx) => ", mmx_trace.q); \ - __asm__ __volatile__ (#op " %0, %%" #reg \ - : /* nothing */ \ - : "X" (mem)); \ - __asm__ __volatile__ ("movq %%" #reg ", %0" \ - : "=X" (mmx_trace) \ - : /* nothing */ ); \ - fprintf(stderr, #reg "=0x%016llx\n", mmx_trace.q); \ - } - -#define mmx_r2m(op, reg, mem) \ - { \ - mmx_t mmx_trace; \ - __asm__ __volatile__ ("movq %%" #reg ", %0" \ - : "=X" (mmx_trace) \ - : /* nothing */ ); \ - fprintf(stderr, #op "_r2m(" #reg "=0x%016llx, ", mmx_trace.q); \ - mmx_trace = (mem); \ - fprintf(stderr, #mem "=0x%016llx) => ", mmx_trace.q); \ - __asm__ __volatile__ (#op " %%" #reg ", %0" \ - : "=X" (mem) \ - : /* nothing */ ); \ - mmx_trace = (mem); \ - fprintf(stderr, #mem "=0x%016llx\n", mmx_trace.q); \ - } - -#define mmx_r2r(op, regs, regd) \ - { \ - mmx_t mmx_trace; \ - __asm__ __volatile__ ("movq %%" #regs ", %0" \ - : "=X" (mmx_trace) \ - : /* nothing */ ); \ - fprintf(stderr, #op "_r2r(" #regs "=0x%016llx, ", mmx_trace.q); \ - __asm__ __volatile__ ("movq %%" #regd ", %0" \ - : "=X" (mmx_trace) \ - : /* nothing */ ); \ - fprintf(stderr, #regd "=0x%016llx) => ", mmx_trace.q); \ - __asm__ __volatile__ (#op " %" #regs ", %" #regd); \ - __asm__ __volatile__ ("movq %%" #regd ", %0" \ - : "=X" (mmx_trace) \ - : /* nothing */ ); \ - fprintf(stderr, #regd "=0x%016llx\n", mmx_trace.q); \ - } - -#define mmx_m2m(op, mems, memd) \ - { \ - mmx_t mmx_trace; \ - mmx_trace = (mems); \ - fprintf(stderr, #op "_m2m(" #mems "=0x%016llx, ", mmx_trace.q); \ - mmx_trace = (memd); \ - fprintf(stderr, #memd "=0x%016llx) => ", mmx_trace.q); \ - __asm__ __volatile__ ("movq %0, %%mm0\n\t" \ - #op " %1, %%mm0\n\t" \ - "movq %%mm0, %0" \ - : "=X" (memd) \ - : "X" (mems)); \ - mmx_trace = (memd); \ - fprintf(stderr, #memd "=0x%016llx\n", mmx_trace.q); \ - } - -#else - -/* These macros are a lot simpler without the tracing... -*/ - -#define mmx_i2r(op, imm, reg) \ - __asm__ __volatile__ (#op " $" #imm ", %%" #reg \ - : /* nothing */ \ - : /* nothing */); - -#define mmx_m2r(op, mem, reg) \ - __asm__ __volatile__ (#op " %0, %%" #reg \ - : /* nothing */ \ - : "m" (mem)) - -#define mmx_r2m(op, reg, mem) \ - __asm__ __volatile__ (#op " %%" #reg ", %0" \ - : "=m" (mem) \ - : /* nothing */ ) - -#define mmx_r2r(op, regs, regd) \ - __asm__ __volatile__ (#op " %" #regs ", %" #regd) - -#define mmx_m2m(op, mems, memd) \ - __asm__ __volatile__ ("movq %0, %%mm0\n\t" \ - #op " %1, %%mm0\n\t" \ - "movq %%mm0, %0" \ - : "=m" (memd) \ - : "m" (mems)) - -#endif - - -/* 1x64 MOVe Quadword - (this is both a load and a store... - in fact, it is the only way to store) -*/ -#define movq_m2r(var, reg) mmx_m2r(movq, var, reg) -#define movq_r2m(reg, var) mmx_r2m(movq, reg, var) -#define movq_r2r(regs, regd) mmx_r2r(movq, regs, regd) -#define movq(vars, vard) \ - __asm__ __volatile__ ("movq %1, %%mm0\n\t" \ - "movq %%mm0, %0" \ - : "=X" (vard) \ - : "X" (vars)) - - -/* 1x32 MOVe Doubleword - (like movq, this is both load and store... - but is most useful for moving things between - mmx registers and ordinary registers) -*/ -#define movd_m2r(var, reg) mmx_m2r(movd, var, reg) -#define movd_r2m(reg, var) mmx_r2m(movd, reg, var) -#define movd_r2r(regs, regd) mmx_r2r(movd, regs, regd) -#define movd(vars, vard) \ - __asm__ __volatile__ ("movd %1, %%mm0\n\t" \ - "movd %%mm0, %0" \ - : "=X" (vard) \ - : "X" (vars)) - - -/* 2x32, 4x16, and 8x8 Parallel ADDs -*/ -#define paddd_m2r(var, reg) mmx_m2r(paddd, var, reg) -#define paddd_r2r(regs, regd) mmx_r2r(paddd, regs, regd) -#define paddd(vars, vard) mmx_m2m(paddd, vars, vard) - -#define paddw_m2r(var, reg) mmx_m2r(paddw, var, reg) -#define paddw_r2r(regs, regd) mmx_r2r(paddw, regs, regd) -#define paddw(vars, vard) mmx_m2m(paddw, vars, vard) - -#define paddb_m2r(var, reg) mmx_m2r(paddb, var, reg) -#define paddb_r2r(regs, regd) mmx_r2r(paddb, regs, regd) -#define paddb(vars, vard) mmx_m2m(paddb, vars, vard) - - -/* 4x16 and 8x8 Parallel ADDs using Saturation arithmetic -*/ -#define paddsw_m2r(var, reg) mmx_m2r(paddsw, var, reg) -#define paddsw_r2r(regs, regd) mmx_r2r(paddsw, regs, regd) -#define paddsw(vars, vard) mmx_m2m(paddsw, vars, vard) - -#define paddsb_m2r(var, reg) mmx_m2r(paddsb, var, reg) -#define paddsb_r2r(regs, regd) mmx_r2r(paddsb, regs, regd) -#define paddsb(vars, vard) mmx_m2m(paddsb, vars, vard) - - -/* 4x16 and 8x8 Parallel ADDs using Unsigned Saturation arithmetic -*/ -#define paddusw_m2r(var, reg) mmx_m2r(paddusw, var, reg) -#define paddusw_r2r(regs, regd) mmx_r2r(paddusw, regs, regd) -#define paddusw(vars, vard) mmx_m2m(paddusw, vars, vard) - -#define paddusb_m2r(var, reg) mmx_m2r(paddusb, var, reg) -#define paddusb_r2r(regs, regd) mmx_r2r(paddusb, regs, regd) -#define paddusb(vars, vard) mmx_m2m(paddusb, vars, vard) - - -/* 2x32, 4x16, and 8x8 Parallel SUBs -*/ -#define psubd_m2r(var, reg) mmx_m2r(psubd, var, reg) -#define psubd_r2r(regs, regd) mmx_r2r(psubd, regs, regd) -#define psubd(vars, vard) mmx_m2m(psubd, vars, vard) - -#define psubw_m2r(var, reg) mmx_m2r(psubw, var, reg) -#define psubw_r2r(regs, regd) mmx_r2r(psubw, regs, regd) -#define psubw(vars, vard) mmx_m2m(psubw, vars, vard) - -#define psubb_m2r(var, reg) mmx_m2r(psubb, var, reg) -#define psubb_r2r(regs, regd) mmx_r2r(psubb, regs, regd) -#define psubb(vars, vard) mmx_m2m(psubb, vars, vard) - - -/* 4x16 and 8x8 Parallel SUBs using Saturation arithmetic -*/ -#define psubsw_m2r(var, reg) mmx_m2r(psubsw, var, reg) -#define psubsw_r2r(regs, regd) mmx_r2r(psubsw, regs, regd) -#define psubsw(vars, vard) mmx_m2m(psubsw, vars, vard) - -#define psubsb_m2r(var, reg) mmx_m2r(psubsb, var, reg) -#define psubsb_r2r(regs, regd) mmx_r2r(psubsb, regs, regd) -#define psubsb(vars, vard) mmx_m2m(psubsb, vars, vard) - - -/* 4x16 and 8x8 Parallel SUBs using Unsigned Saturation arithmetic -*/ -#define psubusw_m2r(var, reg) mmx_m2r(psubusw, var, reg) -#define psubusw_r2r(regs, regd) mmx_r2r(psubusw, regs, regd) -#define psubusw(vars, vard) mmx_m2m(psubusw, vars, vard) - -#define psubusb_m2r(var, reg) mmx_m2r(psubusb, var, reg) -#define psubusb_r2r(regs, regd) mmx_r2r(psubusb, regs, regd) -#define psubusb(vars, vard) mmx_m2m(psubusb, vars, vard) - - -/* 4x16 Parallel MULs giving Low 4x16 portions of results -*/ -#define pmullw_m2r(var, reg) mmx_m2r(pmullw, var, reg) -#define pmullw_r2r(regs, regd) mmx_r2r(pmullw, regs, regd) -#define pmullw(vars, vard) mmx_m2m(pmullw, vars, vard) - - -/* 4x16 Parallel MULs giving High 4x16 portions of results -*/ -#define pmulhw_m2r(var, reg) mmx_m2r(pmulhw, var, reg) -#define pmulhw_r2r(regs, regd) mmx_r2r(pmulhw, regs, regd) -#define pmulhw(vars, vard) mmx_m2m(pmulhw, vars, vard) - - -/* 4x16->2x32 Parallel Mul-ADD - (muls like pmullw, then adds adjacent 16-bit fields - in the multiply result to make the final 2x32 result) -*/ -#define pmaddwd_m2r(var, reg) mmx_m2r(pmaddwd, var, reg) -#define pmaddwd_r2r(regs, regd) mmx_r2r(pmaddwd, regs, regd) -#define pmaddwd(vars, vard) mmx_m2m(pmaddwd, vars, vard) - - -/* 1x64 bitwise AND -*/ -#ifdef BROKEN_PAND -#define pand_m2r(var, reg) \ - { \ - mmx_m2r(pandn, (mmx_t) -1LL, reg); \ - mmx_m2r(pandn, var, reg); \ - } -#define pand_r2r(regs, regd) \ - { \ - mmx_m2r(pandn, (mmx_t) -1LL, regd); \ - mmx_r2r(pandn, regs, regd); \ - } -#define pand(vars, vard) \ - { \ - movq_m2r(vard, mm0); \ - mmx_m2r(pandn, (mmx_t) -1LL, mm0); \ - mmx_m2r(pandn, vars, mm0); \ - movq_r2m(mm0, vard); \ - } -#else -#define pand_m2r(var, reg) mmx_m2r(pand, var, reg) -#define pand_r2r(regs, regd) mmx_r2r(pand, regs, regd) -#define pand(vars, vard) mmx_m2m(pand, vars, vard) -#endif - - -/* 1x64 bitwise AND with Not the destination -*/ -#define pandn_m2r(var, reg) mmx_m2r(pandn, var, reg) -#define pandn_r2r(regs, regd) mmx_r2r(pandn, regs, regd) -#define pandn(vars, vard) mmx_m2m(pandn, vars, vard) - - -/* 1x64 bitwise OR -*/ -#define por_m2r(var, reg) mmx_m2r(por, var, reg) -#define por_r2r(regs, regd) mmx_r2r(por, regs, regd) -#define por(vars, vard) mmx_m2m(por, vars, vard) - - -/* 1x64 bitwise eXclusive OR -*/ -#define pxor_m2r(var, reg) mmx_m2r(pxor, var, reg) -#define pxor_r2r(regs, regd) mmx_r2r(pxor, regs, regd) -#define pxor(vars, vard) mmx_m2m(pxor, vars, vard) - - -/* 2x32, 4x16, and 8x8 Parallel CoMPare for EQuality - (resulting fields are either 0 or -1) -*/ -#define pcmpeqd_m2r(var, reg) mmx_m2r(pcmpeqd, var, reg) -#define pcmpeqd_r2r(regs, regd) mmx_r2r(pcmpeqd, regs, regd) -#define pcmpeqd(vars, vard) mmx_m2m(pcmpeqd, vars, vard) - -#define pcmpeqw_m2r(var, reg) mmx_m2r(pcmpeqw, var, reg) -#define pcmpeqw_r2r(regs, regd) mmx_r2r(pcmpeqw, regs, regd) -#define pcmpeqw(vars, vard) mmx_m2m(pcmpeqw, vars, vard) - -#define pcmpeqb_m2r(var, reg) mmx_m2r(pcmpeqb, var, reg) -#define pcmpeqb_r2r(regs, regd) mmx_r2r(pcmpeqb, regs, regd) -#define pcmpeqb(vars, vard) mmx_m2m(pcmpeqb, vars, vard) - - -/* 2x32, 4x16, and 8x8 Parallel CoMPare for Greater Than - (resulting fields are either 0 or -1) -*/ -#define pcmpgtd_m2r(var, reg) mmx_m2r(pcmpgtd, var, reg) -#define pcmpgtd_r2r(regs, regd) mmx_r2r(pcmpgtd, regs, regd) -#define pcmpgtd(vars, vard) mmx_m2m(pcmpgtd, vars, vard) - -#define pcmpgtw_m2r(var, reg) mmx_m2r(pcmpgtw, var, reg) -#define pcmpgtw_r2r(regs, regd) mmx_r2r(pcmpgtw, regs, regd) -#define pcmpgtw(vars, vard) mmx_m2m(pcmpgtw, vars, vard) - -#define pcmpgtb_m2r(var, reg) mmx_m2r(pcmpgtb, var, reg) -#define pcmpgtb_r2r(regs, regd) mmx_r2r(pcmpgtb, regs, regd) -#define pcmpgtb(vars, vard) mmx_m2m(pcmpgtb, vars, vard) - - -/* 1x64, 2x32, and 4x16 Parallel Shift Left Logical -*/ -#define psllq_i2r(imm, reg) mmx_i2r(psllq, imm, reg) -#define psllq_m2r(var, reg) mmx_m2r(psllq, var, reg) -#define psllq_r2r(regs, regd) mmx_r2r(psllq, regs, regd) -#define psllq(vars, vard) mmx_m2m(psllq, vars, vard) - -#define pslld_i2r(imm, reg) mmx_i2r(pslld, imm, reg) -#define pslld_m2r(var, reg) mmx_m2r(pslld, var, reg) -#define pslld_r2r(regs, regd) mmx_r2r(pslld, regs, regd) -#define pslld(vars, vard) mmx_m2m(pslld, vars, vard) - -#define psllw_i2r(imm, reg) mmx_i2r(psllw, imm, reg) -#define psllw_m2r(var, reg) mmx_m2r(psllw, var, reg) -#define psllw_r2r(regs, regd) mmx_r2r(psllw, regs, regd) -#define psllw(vars, vard) mmx_m2m(psllw, vars, vard) - - -/* 1x64, 2x32, and 4x16 Parallel Shift Right Logical -*/ -#define psrlq_i2r(imm, reg) mmx_i2r(psrlq, imm, reg) -#define psrlq_m2r(var, reg) mmx_m2r(psrlq, var, reg) -#define psrlq_r2r(regs, regd) mmx_r2r(psrlq, regs, regd) -#define psrlq(vars, vard) mmx_m2m(psrlq, vars, vard) - -#define psrld_i2r(imm, reg) mmx_i2r(psrld, imm, reg) -#define psrld_m2r(var, reg) mmx_m2r(psrld, var, reg) -#define psrld_r2r(regs, regd) mmx_r2r(psrld, regs, regd) -#define psrld(vars, vard) mmx_m2m(psrld, vars, vard) - -#define psrlw_i2r(imm, reg) mmx_i2r(psrlw, imm, reg) -#define psrlw_m2r(var, reg) mmx_m2r(psrlw, var, reg) -#define psrlw_r2r(regs, regd) mmx_r2r(psrlw, regs, regd) -#define psrlw(vars, vard) mmx_m2m(psrlw, vars, vard) - - -/* 2x32 and 4x16 Parallel Shift Right Arithmetic -*/ -#define psrad_i2r(imm, reg) mmx_i2r(psrad, imm, reg) -#define psrad_m2r(var, reg) mmx_m2r(psrad, var, reg) -#define psrad_r2r(regs, regd) mmx_r2r(psrad, regs, regd) -#define psrad(vars, vard) mmx_m2m(psrad, vars, vard) - -#define psraw_i2r(imm, reg) mmx_i2r(psraw, imm, reg) -#define psraw_m2r(var, reg) mmx_m2r(psraw, var, reg) -#define psraw_r2r(regs, regd) mmx_r2r(psraw, regs, regd) -#define psraw(vars, vard) mmx_m2m(psraw, vars, vard) - - -/* 2x32->4x16 and 4x16->8x8 PACK and Signed Saturate - (packs source and dest fields into dest in that order) -*/ -#define packssdw_m2r(var, reg) mmx_m2r(packssdw, var, reg) -#define packssdw_r2r(regs, regd) mmx_r2r(packssdw, regs, regd) -#define packssdw(vars, vard) mmx_m2m(packssdw, vars, vard) - -#define packsswb_m2r(var, reg) mmx_m2r(packsswb, var, reg) -#define packsswb_r2r(regs, regd) mmx_r2r(packsswb, regs, regd) -#define packsswb(vars, vard) mmx_m2m(packsswb, vars, vard) - - -/* 4x16->8x8 PACK and Unsigned Saturate - (packs source and dest fields into dest in that order) -*/ -#define packuswb_m2r(var, reg) mmx_m2r(packuswb, var, reg) -#define packuswb_r2r(regs, regd) mmx_r2r(packuswb, regs, regd) -#define packuswb(vars, vard) mmx_m2m(packuswb, vars, vard) - - -/* 2x32->1x64, 4x16->2x32, and 8x8->4x16 UNPaCK Low - (interleaves low half of dest with low half of source - as padding in each result field) -*/ -#define punpckldq_m2r(var, reg) mmx_m2r(punpckldq, var, reg) -#define punpckldq_r2r(regs, regd) mmx_r2r(punpckldq, regs, regd) -#define punpckldq(vars, vard) mmx_m2m(punpckldq, vars, vard) - -#define punpcklwd_m2r(var, reg) mmx_m2r(punpcklwd, var, reg) -#define punpcklwd_r2r(regs, regd) mmx_r2r(punpcklwd, regs, regd) -#define punpcklwd(vars, vard) mmx_m2m(punpcklwd, vars, vard) - -#define punpcklbw_m2r(var, reg) mmx_m2r(punpcklbw, var, reg) -#define punpcklbw_r2r(regs, regd) mmx_r2r(punpcklbw, regs, regd) -#define punpcklbw(vars, vard) mmx_m2m(punpcklbw, vars, vard) - - -/* 2x32->1x64, 4x16->2x32, and 8x8->4x16 UNPaCK High - (interleaves high half of dest with high half of source - as padding in each result field) -*/ -#define punpckhdq_m2r(var, reg) mmx_m2r(punpckhdq, var, reg) -#define punpckhdq_r2r(regs, regd) mmx_r2r(punpckhdq, regs, regd) -#define punpckhdq(vars, vard) mmx_m2m(punpckhdq, vars, vard) - -#define punpckhwd_m2r(var, reg) mmx_m2r(punpckhwd, var, reg) -#define punpckhwd_r2r(regs, regd) mmx_r2r(punpckhwd, regs, regd) -#define punpckhwd(vars, vard) mmx_m2m(punpckhwd, vars, vard) - -#define punpckhbw_m2r(var, reg) mmx_m2r(punpckhbw, var, reg) -#define punpckhbw_r2r(regs, regd) mmx_r2r(punpckhbw, regs, regd) -#define punpckhbw(vars, vard) mmx_m2m(punpckhbw, vars, vard) - - -/* Empty MMx State - (used to clean-up when going from mmx to float use - of the registers that are shared by both; note that - there is no float-to-mmx operation needed, because - only the float tag word info is corruptible) -*/ -#ifdef MMX_TRACE - -#define emms() \ - { \ - fprintf(stderr, "emms()\n"); \ - __asm__ __volatile__ ("emms"); \ - } - -#else - -#define emms() __asm__ __volatile__ ("emms") - -#endif - -#endif diff --git a/gst/deinterlace2/tvtime/plugins.h b/gst/deinterlace2/tvtime/plugins.h deleted file mode 100644 index 8fb01af5..00000000 --- a/gst/deinterlace2/tvtime/plugins.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * - * GStreamer - * Copyright (C) 2004 Billy Biggs <vektor@dumbterm.net> - * Copyright (C) 2008 Sebastian Dröge <slomo@collabora.co.uk> - * - * 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. - */ - -/* - * Relicensed for GStreamer from GPL to LGPL with permit from Billy Biggs. - * See: http://bugzilla.gnome.org/show_bug.cgi?id=163578 - */ - -#ifndef TVTIME_PLUGINS_H_INCLUDED -#define TVTIME_PLUGINS_H_INCLUDED - -#define GST_TYPE_DEINTERLACE_TOMSMOCOMP (gst_deinterlace_method_tomsmocomp_get_type ()) -#define GST_TYPE_DEINTERLACE_GREEDY_H (gst_deinterlace_method_greedy_h_get_type ()) -#define GST_TYPE_DEINTERLACE_GREEDY_L (gst_deinterlace_method_greedy_l_get_type ()) -#define GST_TYPE_DEINTERLACE_VFIR (gst_deinterlace_method_vfir_get_type ()) -#define GST_TYPE_DEINTERLACE_LINEAR (gst_deinterlace_method_linear_get_type ()) -#define GST_TYPE_DEINTERLACE_LINEAR_BLEND (gst_deinterlace_method_linear_blend_get_type ()) -#define GST_TYPE_DEINTERLACE_SCALER_BOB (gst_deinterlace_method_scaler_bob_get_type ()) -#define GST_TYPE_DEINTERLACE_WEAVE (gst_deinterlace_method_weave_get_type ()) -#define GST_TYPE_DEINTERLACE_WEAVE_TFF (gst_deinterlace_method_weave_tff_get_type ()) -#define GST_TYPE_DEINTERLACE_WEAVE_BFF (gst_deinterlace_method_weave_bff_get_type ()) - -GType gst_deinterlace_method_tomsmocomp_get_type (void); -GType gst_deinterlace_method_greedy_h_get_type (void); -GType gst_deinterlace_method_greedy_l_get_type (void); -GType gst_deinterlace_method_vfir_get_type (void); - -GType gst_deinterlace_method_linear_get_type (void); -GType gst_deinterlace_method_linear_blend_get_type (void); -GType gst_deinterlace_method_scaler_bob_get_type (void); -GType gst_deinterlace_method_weave_get_type (void); -GType gst_deinterlace_method_weave_tff_get_type (void); -GType gst_deinterlace_method_weave_bff_get_type (void); - -#endif /* TVTIME_PLUGINS_H_INCLUDED */ diff --git a/gst/deinterlace2/tvtime/scalerbob.c b/gst/deinterlace2/tvtime/scalerbob.c deleted file mode 100644 index a37792ab..00000000 --- a/gst/deinterlace2/tvtime/scalerbob.c +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Double lines - * Copyright (C) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser 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 "_stdint.h" -#include "gstdeinterlace2.h" -#include <string.h> - -#define GST_TYPE_DEINTERLACE_METHOD_SCALER_BOB (gst_deinterlace_method_scaler_bob_get_type ()) -#define GST_IS_DEINTERLACE_METHOD_SCALER_BOB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_DEINTERLACE_METHOD_SCALER_BOB)) -#define GST_IS_DEINTERLACE_METHOD_SCALER_BOB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_DEINTERLACE_METHOD_SCALER_BOB)) -#define GST_DEINTERLACE_METHOD_SCALER_BOB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_DEINTERLACE_METHOD_SCALER_BOB, GstDeinterlaceMethodScalerBobClass)) -#define GST_DEINTERLACE_METHOD_SCALER_BOB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_DEINTERLACE_METHOD_SCALER_BOB, GstDeinterlaceMethodScalerBob)) -#define GST_DEINTERLACE_METHOD_SCALER_BOB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEINTERLACE_METHOD_SCALER_BOB, GstDeinterlaceMethodScalerBobClass)) -#define GST_DEINTERLACE_METHOD_SCALER_BOB_CAST(obj) ((GstDeinterlaceMethodScalerBob*)(obj)) - -GType gst_deinterlace_method_scaler_bob_get_type (void); - -typedef GstDeinterlaceSimpleMethod GstDeinterlaceMethodScalerBob; - -typedef GstDeinterlaceSimpleMethodClass GstDeinterlaceMethodScalerBobClass; - - -static void -deinterlace_scanline_scaler_bob (GstDeinterlaceMethod * self, - GstDeinterlace2 * parent, guint8 * out, - GstDeinterlaceScanlineData * scanlines, gint width) -{ - oil_memcpy (out, scanlines->t0, parent->row_stride); -} - -G_DEFINE_TYPE (GstDeinterlaceMethodScalerBob, gst_deinterlace_method_scaler_bob, - GST_TYPE_DEINTERLACE_SIMPLE_METHOD); - -static void -gst_deinterlace_method_scaler_bob_class_init (GstDeinterlaceMethodScalerBobClass - * klass) -{ - GstDeinterlaceMethodClass *dim_class = (GstDeinterlaceMethodClass *) klass; - GstDeinterlaceSimpleMethodClass *dism_class = - (GstDeinterlaceSimpleMethodClass *) klass; - - dim_class->fields_required = 1; - dim_class->name = "Double lines"; - dim_class->nick = "scalerbob"; - dim_class->latency = 0; - - dism_class->interpolate_scanline = deinterlace_scanline_scaler_bob; -} - -static void -gst_deinterlace_method_scaler_bob_init (GstDeinterlaceMethodScalerBob * self) -{ -} diff --git a/gst/deinterlace2/tvtime/sse.h b/gst/deinterlace2/tvtime/sse.h deleted file mode 100644 index 2e00ee0c..00000000 --- a/gst/deinterlace2/tvtime/sse.h +++ /dev/null @@ -1,992 +0,0 @@ -/* sse.h - - Streaming SIMD Extenstions (a.k.a. Katmai New Instructions) - GCC interface library for IA32. - - To use this library, simply include this header file - and compile with GCC. You MUST have inlining enabled - in order for sse_ok() to work; this can be done by - simply using -O on the GCC command line. - - Compiling with -DSSE_TRACE will cause detailed trace - output to be sent to stderr for each sse operation. - This adds lots of code, and obviously slows execution to - a crawl, but can be very useful for debugging. - - THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT - LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY - AND FITNESS FOR ANY PARTICULAR PURPOSE. - - 1999 by R. Fisher - Based on libmmx by H. Dietz and R. Fisher - - Notes: - This is still extremely alpha. - Because this library depends on an assembler which understands the - SSE opcodes, you probably won't be able to use this yet. - For now, do not use TRACE versions. These both make use - of the MMX registers, not the SSE registers. This will be resolved - at a later date. - ToDo: - Rewrite TRACE macros - Major Debugging Work -*/ - -#ifndef _SSE_H -#define _SSE_H - - - -/* The type of an value that fits in an SSE register - (note that long long constant values MUST be suffixed - by LL and unsigned long long values by ULL, lest - they be truncated by the compiler) -*/ -typedef union { - float sf[4]; /* Single-precision (32-bit) value */ -} __attribute__ ((aligned (16))) sse_t; /* On a 16 byte (128-bit) boundary */ - - -#if 0 -/* Function to test if multimedia instructions are supported... -*/ -inline extern int -mm_support(void) -{ - /* Returns 1 if MMX instructions are supported, - 3 if Cyrix MMX and Extended MMX instructions are supported - 5 if AMD MMX and 3DNow! instructions are supported - 9 if MMX and SSE instructions are supported - 0 if hardware does not support any of these - */ - register int rval = 0; - - __asm__ __volatile__ ( - /* See if CPUID instruction is supported ... */ - /* ... Get copies of EFLAGS into eax and ecx */ - "pushf\n\t" - "popl %%eax\n\t" - "movl %%eax, %%ecx\n\t" - - /* ... Toggle the ID bit in one copy and store */ - /* to the EFLAGS reg */ - "xorl $0x200000, %%eax\n\t" - "push %%eax\n\t" - "popf\n\t" - - /* ... Get the (hopefully modified) EFLAGS */ - "pushf\n\t" - "popl %%eax\n\t" - - /* ... Compare and test result */ - "xorl %%eax, %%ecx\n\t" - "testl $0x200000, %%ecx\n\t" - "jz NotSupported1\n\t" /* CPUID not supported */ - - - /* Get standard CPUID information, and - go to a specific vendor section */ - "movl $0, %%eax\n\t" - "cpuid\n\t" - - /* Check for Intel */ - "cmpl $0x756e6547, %%ebx\n\t" - "jne TryAMD\n\t" - "cmpl $0x49656e69, %%edx\n\t" - "jne TryAMD\n\t" - "cmpl $0x6c65746e, %%ecx\n" - "jne TryAMD\n\t" - "jmp Intel\n\t" - - /* Check for AMD */ - "\nTryAMD:\n\t" - "cmpl $0x68747541, %%ebx\n\t" - "jne TryCyrix\n\t" - "cmpl $0x69746e65, %%edx\n\t" - "jne TryCyrix\n\t" - "cmpl $0x444d4163, %%ecx\n" - "jne TryCyrix\n\t" - "jmp AMD\n\t" - - /* Check for Cyrix */ - "\nTryCyrix:\n\t" - "cmpl $0x69727943, %%ebx\n\t" - "jne NotSupported2\n\t" - "cmpl $0x736e4978, %%edx\n\t" - "jne NotSupported3\n\t" - "cmpl $0x64616574, %%ecx\n\t" - "jne NotSupported4\n\t" - /* Drop through to Cyrix... */ - - - /* Cyrix Section */ - /* See if extended CPUID level 80000001 is supported */ - /* The value of CPUID/80000001 for the 6x86MX is undefined - according to the Cyrix CPU Detection Guide (Preliminary - Rev. 1.01 table 1), so we'll check the value of eax for - CPUID/0 to see if standard CPUID level 2 is supported. - According to the table, the only CPU which supports level - 2 is also the only one which supports extended CPUID levels. - */ - "cmpl $0x2, %%eax\n\t" - "jne MMXtest\n\t" /* Use standard CPUID instead */ - - /* Extended CPUID supported (in theory), so get extended - features */ - "movl $0x80000001, %%eax\n\t" - "cpuid\n\t" - "testl $0x00800000, %%eax\n\t" /* Test for MMX */ - "jz NotSupported5\n\t" /* MMX not supported */ - "testl $0x01000000, %%eax\n\t" /* Test for Ext'd MMX */ - "jnz EMMXSupported\n\t" - "movl $1, %0:\n\n\t" /* MMX Supported */ - "jmp Return\n\n" - "EMMXSupported:\n\t" - "movl $3, %0:\n\n\t" /* EMMX and MMX Supported */ - "jmp Return\n\t" - - - /* AMD Section */ - "AMD:\n\t" - - /* See if extended CPUID is supported */ - "movl $0x80000000, %%eax\n\t" - "cpuid\n\t" - "cmpl $0x80000000, %%eax\n\t" - "jl MMXtest\n\t" /* Use standard CPUID instead */ - - /* Extended CPUID supported, so get extended features */ - "movl $0x80000001, %%eax\n\t" - "cpuid\n\t" - "testl $0x00800000, %%edx\n\t" /* Test for MMX */ - "jz NotSupported6\n\t" /* MMX not supported */ - "testl $0x80000000, %%edx\n\t" /* Test for 3DNow! */ - "jnz ThreeDNowSupported\n\t" - "movl $1, %0:\n\n\t" /* MMX Supported */ - "jmp Return\n\n" - "ThreeDNowSupported:\n\t" - "movl $5, %0:\n\n\t" /* 3DNow! and MMX Supported */ - "jmp Return\n\t" - - - /* Intel Section */ - "Intel:\n\t" - - /* Check for SSE */ - "SSEtest:\n\t" - "movl $1, %%eax\n\t" - "cpuid\n\t" - "testl $0x02000000, %%edx\n\t" /* Test for SSE */ - "jz MMXtest\n\t" /* SSE Not supported */ - "movl $9, %0:\n\n\t" /* SSE Supported */ - "jmp Return\n\t" - - /* Check for MMX */ - "MMXtest:\n\t" - "movl $1, %%eax\n\t" - "cpuid\n\t" - "testl $0x00800000, %%edx\n\t" /* Test for MMX */ - "jz NotSupported7\n\t" /* MMX Not supported */ - "movl $1, %0:\n\n\t" /* MMX Supported */ - "jmp Return\n\t" - - /* Nothing supported */ - "\nNotSupported1:\n\t" - "#movl $101, %0:\n\n\t" - "\nNotSupported2:\n\t" - "#movl $102, %0:\n\n\t" - "\nNotSupported3:\n\t" - "#movl $103, %0:\n\n\t" - "\nNotSupported4:\n\t" - "#movl $104, %0:\n\n\t" - "\nNotSupported5:\n\t" - "#movl $105, %0:\n\n\t" - "\nNotSupported6:\n\t" - "#movl $106, %0:\n\n\t" - "\nNotSupported7:\n\t" - "#movl $107, %0:\n\n\t" - "movl $0, %0:\n\n\t" - - "Return:\n\t" - : "=a" (rval) - : /* no input */ - : "eax", "ebx", "ecx", "edx" - ); - - /* Return */ - return(rval); -} - -/* Function to test if sse instructions are supported... -*/ -inline extern int -sse_ok(void) -{ - /* Returns 1 if SSE instructions are supported, 0 otherwise */ - return ( (mm_support() & 0x8) >> 3 ); -} -#endif - - - -/* Helper functions for the instruction macros that follow... - (note that memory-to-register, m2r, instructions are nearly - as efficient as register-to-register, r2r, instructions; - however, memory-to-memory instructions are really simulated - as a convenience, and are only 1/3 as efficient) -*/ -#ifdef SSE_TRACE - -/* Include the stuff for printing a trace to stderr... -*/ - -#include <stdio.h> - -#define sse_i2r(op, imm, reg) \ - { \ - sse_t sse_trace; \ - sse_trace.uq = (imm); \ - fprintf(stderr, #op "_i2r(" #imm "=0x%08x%08x, ", \ - sse_trace.d[1], sse_trace.d[0]); \ - __asm__ __volatile__ ("movq %%" #reg ", %0" \ - : "=X" (sse_trace) \ - : /* nothing */ ); \ - fprintf(stderr, #reg "=0x%08x%08x) => ", \ - sse_trace.d[1], sse_trace.d[0]); \ - __asm__ __volatile__ (#op " %0, %%" #reg \ - : /* nothing */ \ - : "X" (imm)); \ - __asm__ __volatile__ ("movq %%" #reg ", %0" \ - : "=X" (sse_trace) \ - : /* nothing */ ); \ - fprintf(stderr, #reg "=0x%08x%08x\n", \ - sse_trace.d[1], sse_trace.d[0]); \ - } - -#define sse_m2r(op, mem, reg) \ - { \ - sse_t sse_trace; \ - sse_trace = (mem); \ - fprintf(stderr, #op "_m2r(" #mem "=0x%08x%08x, ", \ - sse_trace.d[1], sse_trace.d[0]); \ - __asm__ __volatile__ ("movq %%" #reg ", %0" \ - : "=X" (sse_trace) \ - : /* nothing */ ); \ - fprintf(stderr, #reg "=0x%08x%08x) => ", \ - sse_trace.d[1], sse_trace.d[0]); \ - __asm__ __volatile__ (#op " %0, %%" #reg \ - : /* nothing */ \ - : "X" (mem)); \ - __asm__ __volatile__ ("movq %%" #reg ", %0" \ - : "=X" (sse_trace) \ - : /* nothing */ ); \ - fprintf(stderr, #reg "=0x%08x%08x\n", \ - sse_trace.d[1], sse_trace.d[0]); \ - } - -#define sse_r2m(op, reg, mem) \ - { \ - sse_t sse_trace; \ - __asm__ __volatile__ ("movq %%" #reg ", %0" \ - : "=X" (sse_trace) \ - : /* nothing */ ); \ - fprintf(stderr, #op "_r2m(" #reg "=0x%08x%08x, ", \ - sse_trace.d[1], sse_trace.d[0]); \ - sse_trace = (mem); \ - fprintf(stderr, #mem "=0x%08x%08x) => ", \ - sse_trace.d[1], sse_trace.d[0]); \ - __asm__ __volatile__ (#op " %%" #reg ", %0" \ - : "=X" (mem) \ - : /* nothing */ ); \ - sse_trace = (mem); \ - fprintf(stderr, #mem "=0x%08x%08x\n", \ - sse_trace.d[1], sse_trace.d[0]); \ - } - -#define sse_r2r(op, regs, regd) \ - { \ - sse_t sse_trace; \ - __asm__ __volatile__ ("movq %%" #regs ", %0" \ - : "=X" (sse_trace) \ - : /* nothing */ ); \ - fprintf(stderr, #op "_r2r(" #regs "=0x%08x%08x, ", \ - sse_trace.d[1], sse_trace.d[0]); \ - __asm__ __volatile__ ("movq %%" #regd ", %0" \ - : "=X" (sse_trace) \ - : /* nothing */ ); \ - fprintf(stderr, #regd "=0x%08x%08x) => ", \ - sse_trace.d[1], sse_trace.d[0]); \ - __asm__ __volatile__ (#op " %" #regs ", %" #regd); \ - __asm__ __volatile__ ("movq %%" #regd ", %0" \ - : "=X" (sse_trace) \ - : /* nothing */ ); \ - fprintf(stderr, #regd "=0x%08x%08x\n", \ - sse_trace.d[1], sse_trace.d[0]); \ - } - -#define sse_m2m(op, mems, memd) \ - { \ - sse_t sse_trace; \ - sse_trace = (mems); \ - fprintf(stderr, #op "_m2m(" #mems "=0x%08x%08x, ", \ - sse_trace.d[1], sse_trace.d[0]); \ - sse_trace = (memd); \ - fprintf(stderr, #memd "=0x%08x%08x) => ", \ - sse_trace.d[1], sse_trace.d[0]); \ - __asm__ __volatile__ ("movq %0, %%mm0\n\t" \ - #op " %1, %%mm0\n\t" \ - "movq %%mm0, %0" \ - : "=X" (memd) \ - : "X" (mems)); \ - sse_trace = (memd); \ - fprintf(stderr, #memd "=0x%08x%08x\n", \ - sse_trace.d[1], sse_trace.d[0]); \ - } - -#else - -/* These macros are a lot simpler without the tracing... -*/ - -#define sse_i2r(op, imm, reg) \ - __asm__ __volatile__ (#op " %0, %%" #reg \ - : /* nothing */ \ - : "X" (imm) ) - -#define sse_m2r(op, mem, reg) \ - __asm__ __volatile__ (#op " %0, %%" #reg \ - : /* nothing */ \ - : "X" (mem)) - -#define sse_r2m(op, reg, mem) \ - __asm__ __volatile__ (#op " %%" #reg ", %0" \ - : "=X" (mem) \ - : /* nothing */ ) - -#define sse_r2r(op, regs, regd) \ - __asm__ __volatile__ (#op " %" #regs ", %" #regd) - -#define sse_r2ri(op, regs, regd, imm) \ - __asm__ __volatile__ (#op " %0, %%" #regs ", %%" #regd \ - : /* nothing */ \ - : "X" (imm) ) - -/* Load data from mems to xmmreg, operate on xmmreg, and store data to memd */ -#define sse_m2m(op, mems, memd, xmmreg) \ - __asm__ __volatile__ ("movups %0, %%xmm0\n\t" \ - #op " %1, %%xmm0\n\t" \ - "movups %%mm0, %0" \ - : "=X" (memd) \ - : "X" (mems)) - -#define sse_m2ri(op, mem, reg, subop) \ - __asm__ __volatile__ (#op " %0, %%" #reg ", " #subop \ - : /* nothing */ \ - : "X" (mem)) - -#define sse_m2mi(op, mems, memd, xmmreg, subop) \ - __asm__ __volatile__ ("movups %0, %%xmm0\n\t" \ - #op " %1, %%xmm0, " #subop "\n\t" \ - "movups %%mm0, %0" \ - : "=X" (memd) \ - : "X" (mems)) -#endif - - - - -/* 1x128 MOVe Aligned four Packed Single-fp -*/ -#define movaps_m2r(var, reg) sse_m2r(movaps, var, reg) -#define movaps_r2m(reg, var) sse_r2m(movaps, reg, var) -#define movaps_r2r(regs, regd) sse_r2r(movaps, regs, regd) -#define movaps(vars, vard) \ - __asm__ __volatile__ ("movaps %1, %%mm0\n\t" \ - "movaps %%mm0, %0" \ - : "=X" (vard) \ - : "X" (vars)) - - -/* 1x128 MOVe aligned Non-Temporal four Packed Single-fp -*/ -#define movntps_r2m(xmmreg, var) sse_r2m(movntps, xmmreg, var) - - -/* 1x64 MOVe Non-Temporal Quadword -*/ -#define movntq_r2m(mmreg, var) sse_r2m(movntq, mmreg, var) - - -/* 1x128 MOVe Unaligned four Packed Single-fp -*/ -#define movups_m2r(var, reg) sse_m2r(movups, var, reg) -#define movups_r2m(reg, var) sse_r2m(movups, reg, var) -#define movups_r2r(regs, regd) sse_r2r(movups, regs, regd) -#define movups(vars, vard) \ - __asm__ __volatile__ ("movups %1, %%mm0\n\t" \ - "movups %%mm0, %0" \ - : "=X" (vard) \ - : "X" (vars)) - - -/* MOVe High to Low Packed Single-fp - high half of 4x32f (x) -> low half of 4x32f (y) -*/ -#define movhlps_r2r(regs, regd) sse_r2r(movhlps, regs, regd) - - -/* MOVe Low to High Packed Single-fp - low half of 4x32f (x) -> high half of 4x32f (y) -*/ -#define movlhps_r2r(regs, regd) sse_r2r(movlhps, regs, regd) - - -/* MOVe High Packed Single-fp - 2x32f -> high half of 4x32f -*/ -#define movhps_m2r(var, reg) sse_m2r(movhps, var, reg) -#define movhps_r2m(reg, var) sse_r2m(movhps, reg, var) -#define movhps(vars, vard) \ - __asm__ __volatile__ ("movhps %1, %%mm0\n\t" \ - "movhps %%mm0, %0" \ - : "=X" (vard) \ - : "X" (vars)) - - -/* MOVe Low Packed Single-fp - 2x32f -> low half of 4x32f -*/ -#define movlps_m2r(var, reg) sse_m2r(movlps, var, reg) -#define movlps_r2m(reg, var) sse_r2m(movlps, reg, var) -#define movlps(vars, vard) \ - __asm__ __volatile__ ("movlps %1, %%mm0\n\t" \ - "movlps %%mm0, %0" \ - : "=X" (vard) \ - : "X" (vars)) - - -/* MOVe Scalar Single-fp - lowest field of 4x32f (x) -> lowest field of 4x32f (y) -*/ -#define movss_m2r(var, reg) sse_m2r(movss, var, reg) -#define movss_r2m(reg, var) sse_r2m(movss, reg, var) -#define movss_r2r(regs, regd) sse_r2r(movss, regs, regd) -#define movss(vars, vard) \ - __asm__ __volatile__ ("movss %1, %%mm0\n\t" \ - "movss %%mm0, %0" \ - : "=X" (vard) \ - : "X" (vars)) - - -/* 4x16 Packed SHUFfle Word -*/ -#define pshufw_m2r(var, reg, index) sse_m2ri(pshufw, var, reg, index) -#define pshufw_r2r(regs, regd, index) sse_r2ri(pshufw, regs, regd, index) - - -/* 1x128 SHUFfle Packed Single-fp -*/ -#define shufps_m2r(var, reg, index) sse_m2ri(shufps, var, reg, index) -#define shufps_r2r(regs, regd, index) sse_r2ri(shufps, regs, regd, index) - - -/* ConVerT Packed signed Int32 to(2) Packed Single-fp -*/ -#define cvtpi2ps_m2r(var, xmmreg) sse_m2r(cvtpi2ps, var, xmmreg) -#define cvtpi2ps_r2r(mmreg, xmmreg) sse_r2r(cvtpi2ps, mmreg, xmmreg) - - -/* ConVerT Packed Single-fp to(2) Packed signed Int32 -*/ -#define cvtps2pi_m2r(var, mmreg) sse_m2r(cvtps2pi, var, mmreg) -#define cvtps2pi_r2r(xmmreg, mmreg) sse_r2r(cvtps2pi, mmreg, xmmreg) - - -/* ConVerT with Truncate Packed Single-fp to(2) Packed Int32 -*/ -#define cvttps2pi_m2r(var, mmreg) sse_m2r(cvttps2pi, var, mmreg) -#define cvttps2pi_r2r(xmmreg, mmreg) sse_r2r(cvttps2pi, mmreg, xmmreg) - - -/* ConVerT Signed Int32 to(2) Single-fp (Scalar) -*/ -#define cvtsi2ss_m2r(var, xmmreg) sse_m2r(cvtsi2ss, var, xmmreg) -#define cvtsi2ss_r2r(reg, xmmreg) sse_r2r(cvtsi2ss, reg, xmmreg) - - -/* ConVerT Scalar Single-fp to(2) Signed Int32 -*/ -#define cvtss2si_m2r(var, reg) sse_m2r(cvtss2si, var, reg) -#define cvtss2si_r2r(xmmreg, reg) sse_r2r(cvtss2si, xmmreg, reg) - - -/* ConVerT with Truncate Scalar Single-fp to(2) Signed Int32 -*/ -#define cvttss2si_m2r(var, reg) sse_m2r(cvtss2si, var, reg) -#define cvttss2si_r2r(xmmreg, reg) sse_r2r(cvtss2si, xmmreg, reg) - - -/* Parallel EXTRact Word from 4x16 -*/ -#define pextrw_r2r(mmreg, reg, field) sse_r2ri(pextrw, mmreg, reg, field) - - -/* Parallel INSeRt Word from 4x16 -*/ -#define pinsrw_r2r(reg, mmreg, field) sse_r2ri(pinsrw, reg, mmreg, field) - - - -/* MOVe MaSK from Packed Single-fp -*/ -#ifdef SSE_TRACE - #define movmskps(xmmreg, reg) \ - { \ - fprintf(stderr, "movmskps()\n"); \ - __asm__ __volatile__ ("movmskps %" #xmmreg ", %" #reg) \ - } -#else - #define movmskps(xmmreg, reg) \ - __asm__ __volatile__ ("movmskps %" #xmmreg ", %" #reg) -#endif - - -/* Parallel MOVe MaSK from mmx reg to 32-bit reg -*/ -#ifdef SSE_TRACE - #define pmovmskb(mmreg, reg) \ - { \ - fprintf(stderr, "movmskps()\n"); \ - __asm__ __volatile__ ("movmskps %" #mmreg ", %" #reg) \ - } -#else - #define pmovmskb(mmreg, reg) \ - __asm__ __volatile__ ("movmskps %" #mmreg ", %" #reg) -#endif - - -/* MASKed MOVe from 8x8 to memory pointed to by (e)di register -*/ -#define maskmovq(mmregs, fieldreg) sse_r2ri(maskmovq, mmregs, fieldreg) - - - - -/* 4x32f Parallel ADDs -*/ -#define addps_m2r(var, reg) sse_m2r(addps, var, reg) -#define addps_r2r(regs, regd) sse_r2r(addps, regs, regd) -#define addps(vars, vard, xmmreg) sse_m2m(addps, vars, vard, xmmreg) - - -/* Lowest Field of 4x32f Parallel ADDs -*/ -#define addss_m2r(var, reg) sse_m2r(addss, var, reg) -#define addss_r2r(regs, regd) sse_r2r(addss, regs, regd) -#define addss(vars, vard, xmmreg) sse_m2m(addss, vars, vard, xmmreg) - - -/* 4x32f Parallel SUBs -*/ -#define subps_m2r(var, reg) sse_m2r(subps, var, reg) -#define subps_r2r(regs, regd) sse_r2r(subps, regs, regd) -#define subps(vars, vard, xmmreg) sse_m2m(subps, vars, vard, xmmreg) - - -/* Lowest Field of 4x32f Parallel SUBs -*/ -#define subss_m2r(var, reg) sse_m2r(subss, var, reg) -#define subss_r2r(regs, regd) sse_r2r(subss, regs, regd) -#define subss(vars, vard, xmmreg) sse_m2m(subss, vars, vard, xmmreg) - - -/* 8x8u -> 4x16u Packed Sum of Absolute Differences -*/ -#define psadbw_m2r(var, reg) sse_m2r(psadbw, var, reg) -#define psadbw_r2r(regs, regd) sse_r2r(psadbw, regs, regd) -#define psadbw(vars, vard, mmreg) sse_m2m(psadbw, vars, vard, mmreg) - - -/* 4x16u Parallel MUL High Unsigned -*/ -#define pmulhuw_m2r(var, reg) sse_m2r(pmulhuw, var, reg) -#define pmulhuw_r2r(regs, regd) sse_r2r(pmulhuw, regs, regd) -#define pmulhuw(vars, vard, mmreg) sse_m2m(pmulhuw, vars, vard, mmreg) - - -/* 4x32f Parallel MULs -*/ -#define mulps_m2r(var, reg) sse_m2r(mulps, var, reg) -#define mulps_r2r(regs, regd) sse_r2r(mulps, regs, regd) -#define mulps(vars, vard, xmmreg) sse_m2m(mulps, vars, vard, xmmreg) - - -/* Lowest Field of 4x32f Parallel MULs -*/ -#define mulss_m2r(var, reg) sse_m2r(mulss, var, reg) -#define mulss_r2r(regs, regd) sse_r2r(mulss, regs, regd) -#define mulss(vars, vard, xmmreg) sse_m2m(mulss, vars, vard, xmmreg) - - -/* 4x32f Parallel DIVs -*/ -#define divps_m2r(var, reg) sse_m2r(divps, var, reg) -#define divps_r2r(regs, regd) sse_r2r(divps, regs, regd) -#define divps(vars, vard, xmmreg) sse_m2m(divps, vars, vard, xmmreg) - - -/* Lowest Field of 4x32f Parallel DIVs -*/ -#define divss_m2r(var, reg) sse_m2r(divss, var, reg) -#define divss_r2r(regs, regd) sse_r2r(divss, regs, regd) -#define divss(vars, vard, xmmreg) sse_m2m(divss, vars, vard, xmmreg) - - -/* 4x32f Parallel Reciprocals -*/ -#define rcpps_m2r(var, reg) sse_m2r(rcpps, var, reg) -#define rcpps_r2r(regs, regd) sse_r2r(rcpps, regs, regd) -#define rcpps(vars, vard, xmmreg) sse_m2m(rcpps, vars, vard, xmmreg) - - -/* Lowest Field of 4x32f Parallel Reciprocals -*/ -#define rcpss_m2r(var, reg) sse_m2r(rcpss, var, reg) -#define rcpss_r2r(regs, regd) sse_r2r(rcpss, regs, regd) -#define rcpss(vars, vard, xmmreg) sse_m2m(rcpss, vars, vard, xmmreg) - - -/* 4x32f Parallel Square Root of Reciprocals -*/ -#define rsqrtps_m2r(var, reg) sse_m2r(rsqrtps, var, reg) -#define rsqrtps_r2r(regs, regd) sse_r2r(rsqrtps, regs, regd) -#define rsqrtps(vars, vard, xmmreg) sse_m2m(rsqrtps, vars, vard, xmmreg) - - -/* Lowest Field of 4x32f Parallel Square Root of Reciprocals -*/ -#define rsqrtss_m2r(var, reg) sse_m2r(rsqrtss, var, reg) -#define rsqrtss_r2r(regs, regd) sse_r2r(rsqrtss, regs, regd) -#define rsqrtss(vars, vard, xmmreg) sse_m2m(rsqrtss, vars, vard, xmmreg) - - -/* 4x32f Parallel Square Roots -*/ -#define sqrtps_m2r(var, reg) sse_m2r(sqrtps, var, reg) -#define sqrtps_r2r(regs, regd) sse_r2r(sqrtps, regs, regd) -#define sqrtps(vars, vard, xmmreg) sse_m2m(sqrtps, vars, vard, xmmreg) - - -/* Lowest Field of 4x32f Parallel Square Roots -*/ -#define sqrtss_m2r(var, reg) sse_m2r(sqrtss, var, reg) -#define sqrtss_r2r(regs, regd) sse_r2r(sqrtss, regs, regd) -#define sqrtss(vars, vard, xmmreg) sse_m2m(sqrtss, vars, vard, xmmreg) - - -/* 8x8u and 4x16u Parallel AVeraGe -*/ -#define pavgb_m2r(var, reg) sse_m2r(pavgb, var, reg) -#define pavgb_r2r(regs, regd) sse_r2r(pavgb, regs, regd) -#define pavgb(vars, vard, mmreg) sse_m2m(pavgb, vars, vard, mmreg) - -#define pavgw_m2r(var, reg) sse_m2r(pavgw, var, reg) -#define pavgw_r2r(regs, regd) sse_r2r(pavgw, regs, regd) -#define pavgw(vars, vard, mmreg) sse_m2m(pavgw, vars, vard, mmreg) - - -/* 1x128 bitwise AND -*/ -#define andps_m2r(var, reg) sse_m2r(andps, var, reg) -#define andps_r2r(regs, regd) sse_r2r(andps, regs, regd) -#define andps(vars, vard, xmmreg) sse_m2m(andps, vars, vard, xmmreg) - - -/* 1x128 bitwise AND with Not the destination -*/ -#define andnps_m2r(var, reg) sse_m2r(andnps, var, reg) -#define andnps_r2r(regs, regd) sse_r2r(andnps, regs, regd) -#define andnps(vars, vard, xmmreg) sse_m2m(andnps, vars, vard, xmmreg) - - -/* 1x128 bitwise OR -*/ -#define orps_m2r(var, reg) sse_m2r(orps, var, reg) -#define orps_r2r(regs, regd) sse_r2r(orps, regs, regd) -#define orps(vars, vard, xmmreg) sse_m2m(orps, vars, vard, xmmreg) - - -/* 1x128 bitwise eXclusive OR -*/ -#define xorps_m2r(var, reg) sse_m2r(xorps, var, reg) -#define xorps_r2r(regs, regd) sse_r2r(xorps, regs, regd) -#define xorps(vars, vard, xmmreg) sse_m2m(xorps, vars, vard, xmmreg) - - -/* 8x8u, 4x16, and 4x32f Parallel Maximum -*/ -#define pmaxub_m2r(var, reg) sse_m2r(pmaxub, var, reg) -#define pmaxub_r2r(regs, regd) sse_r2r(pmaxub, regs, regd) -#define pmaxub(vars, vard, mmreg) sse_m2m(pmaxub, vars, vard, mmreg) - -#define pmaxsw_m2r(var, reg) sse_m2r(pmaxsw, var, reg) -#define pmaxsw_r2r(regs, regd) sse_r2r(pmaxsw, regs, regd) -#define pmaxsw(vars, vard, mmreg) sse_m2m(pmaxsw, vars, vard, mmreg) - -#define maxps_m2r(var, reg) sse_m2r(maxps, var, reg) -#define maxps_r2r(regs, regd) sse_r2r(maxps, regs, regd) -#define maxps(vars, vard, xmmreg) sse_m2m(maxps, vars, vard, xmmreg) - - -/* Lowest Field of 4x32f Parallel Maximum -*/ -#define maxss_m2r(var, reg) sse_m2r(maxss, var, reg) -#define maxss_r2r(regs, regd) sse_r2r(maxss, regs, regd) -#define maxss(vars, vard, xmmreg) sse_m2m(maxss, vars, vard, xmmreg) - - -/* 8x8u, 4x16, and 4x32f Parallel Minimum -*/ -#define pminub_m2r(var, reg) sse_m2r(pminub, var, reg) -#define pminub_r2r(regs, regd) sse_r2r(pminub, regs, regd) -#define pminub(vars, vard, mmreg) sse_m2m(pminub, vars, vard, mmreg) - -#define pminsw_m2r(var, reg) sse_m2r(pminsw, var, reg) -#define pminsw_r2r(regs, regd) sse_r2r(pminsw, regs, regd) -#define pminsw(vars, vard, mmreg) sse_m2m(pminsw, vars, vard, mmreg) - -#define minps_m2r(var, reg) sse_m2r(minps, var, reg) -#define minps_r2r(regs, regd) sse_r2r(minps, regs, regd) -#define minps(vars, vard, xmmreg) sse_m2m(minps, vars, vard, xmmreg) - - -/* Lowest Field of 4x32f Parallel Minimum -*/ -#define minss_m2r(var, reg) sse_m2r(minss, var, reg) -#define minss_r2r(regs, regd) sse_r2r(minss, regs, regd) -#define minss(vars, vard, xmmreg) sse_m2m(minss, vars, vard, xmmreg) - - -/* 4x32f Parallel CoMPares - (resulting fields are either 0 or -1) -*/ -#define cmpps_m2r(var, reg, op) sse_m2ri(cmpps, var, reg, op) -#define cmpps_r2r(regs, regd, op) sse_r2ri(cmpps, regs, regd, op) -#define cmpps(vars, vard, op, xmmreg) sse_m2mi(cmpps, vars, vard, xmmreg, op) - -#define cmpeqps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 0) -#define cmpeqps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 0) -#define cmpeqps(vars, vard, xmmreg) sse_m2mi(cmpps, vars, vard, xmmreg, 0) - -#define cmpltps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 1) -#define cmpltps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 1) -#define cmpltps(vars, vard, xmmreg) sse_m2mi(cmpps, vars, vard, xmmreg, 1) - -#define cmpleps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 2) -#define cmpleps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 2) -#define cmpleps(vars, vard, xmmreg) sse_m2mi(cmpps, vars, vard, xmmreg, 2) - -#define cmpunordps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 3) -#define cmpunordps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 3) -#define cmpunordps(vars, vard, xmmreg) sse_m2mi(cmpps, vars, vard, xmmreg, 3) - -#define cmpneqps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 4) -#define cmpneqps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 4) -#define cmpneqps(vars, vard, xmmreg) sse_m2mi(cmpps, vars, vard, xmmreg, 4) - -#define cmpnltps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 5) -#define cmpnltps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 5) -#define cmpnltps(vars, vard, xmmreg) sse_m2mi(cmpps, vars, vard, xmmreg, 5) - -#define cmpnleps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 6) -#define cmpnleps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 6) -#define cmpnleps(vars, vard, xmmreg) sse_m2mi(cmpps, vars, vard, xmmreg, 6) - -#define cmpordps_m2r(var, reg) sse_m2ri(cmpps, var, reg, 7) -#define cmpordps_r2r(regs, regd) sse_r2ri(cmpps, regs, regd, 7) -#define cmpordps(vars, vard, xmmreg) sse_m2mi(cmpps, vars, vard, xmmreg, 7) - - -/* Lowest Field of 4x32f Parallel CoMPares - (resulting fields are either 0 or -1) -*/ -#define cmpss_m2r(var, reg, op) sse_m2ri(cmpss, var, reg, op) -#define cmpss_r2r(regs, regd, op) sse_r2ri(cmpss, regs, regd, op) -#define cmpss(vars, vard, op, xmmreg) sse_m2mi(cmpss, vars, vard, xmmreg, op) - -#define cmpeqss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 0) -#define cmpeqss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 0) -#define cmpeqss(vars, vard, xmmreg) sse_m2mi(cmpss, vars, vard, xmmreg, 0) - -#define cmpltss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 1) -#define cmpltss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 1) -#define cmpltss(vars, vard, xmmreg) sse_m2mi(cmpss, vars, vard, xmmreg, 1) - -#define cmpless_m2r(var, reg) sse_m2ri(cmpss, var, reg, 2) -#define cmpless_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 2) -#define cmpless(vars, vard, xmmreg) sse_m2mi(cmpss, vars, vard, xmmreg, 2) - -#define cmpunordss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 3) -#define cmpunordss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 3) -#define cmpunordss(vars, vard, xmmreg) sse_m2mi(cmpss, vars, vard, xmmreg, 3) - -#define cmpneqss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 4) -#define cmpneqss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 4) -#define cmpneqss(vars, vard, xmmreg) sse_m2mi(cmpss, vars, vard, xmmreg, 4) - -#define cmpnltss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 5) -#define cmpnltss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 5) -#define cmpnltss(vars, vard, xmmreg) sse_m2mi(cmpss, vars, vard, xmmreg, 5) - -#define cmpnless_m2r(var, reg) sse_m2ri(cmpss, var, reg, 6) -#define cmpnless_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 6) -#define cmpnless(vars, vard, xmmreg) sse_m2mi(cmpss, vars, vard, xmmreg, 6) - -#define cmpordss_m2r(var, reg) sse_m2ri(cmpss, var, reg, 7) -#define cmpordss_r2r(regs, regd) sse_r2ri(cmpss, regs, regd, 7) -#define cmpordss(vars, vard, xmmreg) sse_m2mi(cmpss, vars, vard, xmmreg, 7) - - -/* Lowest Field of 4x32f Parallel CoMPares to set EFLAGS - (resulting fields are either 0 or -1) -*/ -#define comiss_m2r(var, reg) sse_m2r(comiss, var, reg) -#define comiss_r2r(regs, regd) sse_r2r(comiss, regs, regd) -#define comiss(vars, vard, xmmreg) sse_m2m(comiss, vars, vard, xmmreg) - - -/* Lowest Field of 4x32f Unordered Parallel CoMPares to set EFLAGS - (resulting fields are either 0 or -1) -*/ -#define ucomiss_m2r(var, reg) sse_m2r(ucomiss, var, reg) -#define ucomiss_r2r(regs, regd) sse_r2r(ucomiss, regs, regd) -#define ucomiss(vars, vard, xmmreg) sse_m2m(ucomiss, vars, vard, xmmreg) - - -/* 2-(4x32f) -> 4x32f UNPaCK Low Packed Single-fp - (interleaves low half of dest with low half of source - as padding in each result field) -*/ -#define unpcklps_m2r(var, reg) sse_m2r(unpcklps, var, reg) -#define unpcklps_r2r(regs, regd) sse_r2r(unpcklps, regs, regd) - - -/* 2-(4x32f) -> 4x32f UNPaCK High Packed Single-fp - (interleaves high half of dest with high half of source - as padding in each result field) -*/ -#define unpckhps_m2r(var, reg) sse_m2r(unpckhps, var, reg) -#define unpckhps_r2r(regs, regd) sse_r2r(unpckhps, regs, regd) - - - -/* Fp and mmX ReSTORe state -*/ -#ifdef SSE_TRACE - #define fxrstor(mem) \ - { \ - fprintf(stderr, "fxrstor()\n"); \ - __asm__ __volatile__ ("fxrstor %0" \ - : /* nothing */ \ - : "X" (mem)) \ - } -#else - #define fxrstor(mem) \ - __asm__ __volatile__ ("fxrstor %0" \ - : /* nothing */ \ - : "X" (mem)) -#endif - - -/* Fp and mmX SAVE state -*/ -#ifdef SSE_TRACE - #define fxsave(mem) \ - { \ - fprintf(stderr, "fxsave()\n"); \ - __asm__ __volatile__ ("fxsave %0" \ - : /* nothing */ \ - : "X" (mem)) \ - } -#else - #define fxsave(mem) \ - __asm__ __volatile__ ("fxsave %0" \ - : /* nothing */ \ - : "X" (mem)) -#endif - - -/* STore streaMing simd eXtensions Control/Status Register -*/ -#ifdef SSE_TRACE - #define stmxcsr(mem) \ - { \ - fprintf(stderr, "stmxcsr()\n"); \ - __asm__ __volatile__ ("stmxcsr %0" \ - : /* nothing */ \ - : "X" (mem)) \ - } -#else - #define stmxcsr(mem) \ - __asm__ __volatile__ ("stmxcsr %0" \ - : /* nothing */ \ - : "X" (mem)) -#endif - - -/* LoaD streaMing simd eXtensions Control/Status Register -*/ -#ifdef SSE_TRACE - #define ldmxcsr(mem) \ - { \ - fprintf(stderr, "ldmxcsr()\n"); \ - __asm__ __volatile__ ("ldmxcsr %0" \ - : /* nothing */ \ - : "X" (mem)) \ - } -#else - #define ldmxcsr(mem) \ - __asm__ __volatile__ ("ldmxcsr %0" \ - : /* nothing */ \ - : "X" (mem)) -#endif - - -/* Store FENCE - enforce ordering of stores before fence vs. stores - occuring after fence in source code. -*/ -#ifdef SSE_TRACE - #define sfence() \ - { \ - fprintf(stderr, "sfence()\n"); \ - __asm__ __volatile__ ("sfence\n\t") \ - } -#else - #define sfence() \ - __asm__ __volatile__ ("sfence\n\t") -#endif - - -/* PREFETCH data using T0, T1, T2, or NTA hint - T0 = Prefetch into all cache levels - T1 = Prefetch into all cache levels except 0th level - T2 = Prefetch into all cache levels except 0th and 1st levels - NTA = Prefetch data into non-temporal cache structure -*/ -#ifdef SSE_TRACE -#else - #define prefetch(mem, hint) \ - __asm__ __volatile__ ("prefetch" #hint " %0" \ - : /* nothing */ \ - : "X" (mem)) - - #define prefetcht0(mem) prefetch(mem, t0) - #define prefetcht1(mem) prefetch(mem, t1) - #define prefetcht2(mem) prefetch(mem, t2) - #define prefetchnta(mem) prefetch(mem, nta) -#endif - - - -#endif diff --git a/gst/deinterlace2/tvtime/tomsmocomp.c b/gst/deinterlace2/tvtime/tomsmocomp.c deleted file mode 100644 index 64e78c5b..00000000 --- a/gst/deinterlace2/tvtime/tomsmocomp.c +++ /dev/null @@ -1,211 +0,0 @@ -/** - * Copyright (C) 2004 Billy Biggs <vektor@dumbterm.net> - * Copyright (C) 2008 Sebastian Dröge <slomo@collabora.co.uk> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; 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 <stdlib.h> -#include "_stdint.h" -#include <string.h> - -#include "gst/gst.h" -#include "gstdeinterlace2.h" -#include "plugins.h" - -#define GST_TYPE_DEINTERLACE_METHOD_TOMSMOCOMP (gst_deinterlace_method_tomsmocomp_get_type ()) -#define GST_IS_DEINTERLACE_METHOD_TOMSMOCOMP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_DEINTERLACE_METHOD_TOMSMOCOMP)) -#define GST_IS_DEINTERLACE_METHOD_TOMSMOCOMP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_DEINTERLACE_METHOD_TOMSMOCOMP)) -#define GST_DEINTERLACE_METHOD_TOMSMOCOMP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_DEINTERLACE_METHOD_TOMSMOCOMP, GstDeinterlaceMethodTomsMoCompClass)) -#define GST_DEINTERLACE_METHOD_TOMSMOCOMP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_DEINTERLACE_METHOD_TOMSMOCOMP, GstDeinterlaceMethodTomsMoComp)) -#define GST_DEINTERLACE_METHOD_TOMSMOCOMP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEINTERLACE_METHOD_TOMSMOCOMP, GstDeinterlaceMethodTomsMoCompClass)) -#define GST_DEINTERLACE_METHOD_TOMSMOCOMP_CAST(obj) ((GstDeinterlaceMethodTomsMoComp*)(obj)) - -GType gst_deinterlace_method_tomsmocomp_get_type (void); - -typedef struct -{ - GstDeinterlaceMethod parent; - - guint search_effort; - gboolean strange_bob; -} GstDeinterlaceMethodTomsMoComp; - -typedef struct -{ - GstDeinterlaceMethodClass parent_class; -} GstDeinterlaceMethodTomsMoCompClass; - -static int -Fieldcopy (void *dest, const void *src, size_t count, - int rows, int dst_pitch, int src_pitch) -{ - unsigned char *pDest = (unsigned char *) dest; - unsigned char *pSrc = (unsigned char *) src; - - int i; - - for (i = 0; i < rows; i++) { - oil_memcpy (pDest, pSrc, count); - pSrc += src_pitch; - pDest += dst_pitch; - } - return 0; -} - -#define USE_FOR_DSCALER - -#define IS_C -#define SIMD_TYPE C -#define FUNCT_NAME tomsmocompDScaler_C -#include "tomsmocomp/TomsMoCompAll.inc" -#undef IS_C -#undef SIMD_TYPE -#undef FUNCT_NAME - -#ifdef BUILD_X86_ASM - -#include "tomsmocomp/tomsmocompmacros.h" -#include "x86-64_macros.inc" - -#define IS_MMX -#define SIMD_TYPE MMX -#define FUNCT_NAME tomsmocompDScaler_MMX -#include "tomsmocomp/TomsMoCompAll.inc" -#undef IS_MMX -#undef SIMD_TYPE -#undef FUNCT_NAME - -#define IS_3DNOW -#define SIMD_TYPE 3DNOW -#define FUNCT_NAME tomsmocompDScaler_3DNOW -#include "tomsmocomp/TomsMoCompAll.inc" -#undef IS_3DNOW -#undef SIMD_TYPE -#undef FUNCT_NAME - -#define IS_MMXEXT -#define SIMD_TYPE MMXEXT -#define FUNCT_NAME tomsmocompDScaler_MMXEXT -#include "tomsmocomp/TomsMoCompAll.inc" -#undef IS_MMXEXT -#undef SIMD_TYPE -#undef FUNCT_NAME - -#endif - -G_DEFINE_TYPE (GstDeinterlaceMethodTomsMoComp, - gst_deinterlace_method_tomsmocomp, GST_TYPE_DEINTERLACE_METHOD); - -enum -{ - ARG_0, - ARG_SEARCH_EFFORT, - ARG_STRANGE_BOB -}; - -static void -gst_deinterlace_method_tomsmocomp_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstDeinterlaceMethodTomsMoComp *self = - GST_DEINTERLACE_METHOD_TOMSMOCOMP (object); - - switch (prop_id) { - case ARG_SEARCH_EFFORT: - self->search_effort = g_value_get_uint (value); - break; - case ARG_STRANGE_BOB: - self->strange_bob = g_value_get_boolean (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -gst_deinterlace_method_tomsmocomp_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstDeinterlaceMethodTomsMoComp *self = - GST_DEINTERLACE_METHOD_TOMSMOCOMP (object); - - switch (prop_id) { - case ARG_SEARCH_EFFORT: - g_value_set_uint (value, self->search_effort); - break; - case ARG_STRANGE_BOB: - g_value_set_boolean (value, self->strange_bob); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void - gst_deinterlace_method_tomsmocomp_class_init - (GstDeinterlaceMethodTomsMoCompClass * klass) -{ - GstDeinterlaceMethodClass *dim_class = (GstDeinterlaceMethodClass *) klass; - GObjectClass *gobject_class = (GObjectClass *) klass; -#ifdef BUILD_X86_ASM - guint cpu_flags = oil_cpu_get_flags (); -#endif - - gobject_class->set_property = gst_deinterlace_method_tomsmocomp_set_property; - gobject_class->get_property = gst_deinterlace_method_tomsmocomp_get_property; - - g_object_class_install_property (gobject_class, ARG_SEARCH_EFFORT, - g_param_spec_uint ("search-effort", - "Search Effort", - "Search Effort", 0, 27, 5, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) - ); - - g_object_class_install_property (gobject_class, ARG_STRANGE_BOB, - g_param_spec_boolean ("strange-bob", - "Strange Bob", - "Use strange bob", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) - ); - - dim_class->fields_required = 4; - dim_class->name = "Motion Adaptive: Motion Search"; - dim_class->nick = "tomsmocomp"; - dim_class->latency = 1; - -#ifdef BUILD_X86_ASM - if (cpu_flags & OIL_IMPL_FLAG_MMXEXT) { - dim_class->deinterlace_frame = tomsmocompDScaler_MMXEXT; - } else if (cpu_flags & OIL_IMPL_FLAG_3DNOW) { - dim_class->deinterlace_frame = tomsmocompDScaler_3DNOW; - } else if (cpu_flags & OIL_IMPL_FLAG_MMX) { - dim_class->deinterlace_frame = tomsmocompDScaler_MMX; - } else { - dim_class->deinterlace_frame = tomsmocompDScaler_C; - } -#else - dim_class->deinterlace_frame = tomsmocompDScaler_C; -#endif -} - -static void -gst_deinterlace_method_tomsmocomp_init (GstDeinterlaceMethodTomsMoComp * self) -{ - self->search_effort = 5; - self->strange_bob = FALSE; -} diff --git a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoop0A.inc b/gst/deinterlace2/tvtime/tomsmocomp/SearchLoop0A.inc deleted file mode 100644 index b1d9aeca..00000000 --- a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoop0A.inc +++ /dev/null @@ -1,15 +0,0 @@ -// -*- c++ -*- - -// Searches just the center pixel, in both the old -// and new fields, but takes averages. This is an even -// pixel address. Any chroma match will be used. (YUY2) -// We best like finding 0 motion so we will bias everything we found previously -// up by a little, and adjust later - -#ifdef IS_SSE2 - "paddusb "_ONES", %%xmm7\n\t" // bias toward no motion -#else - "paddusb "_ONES", %%mm7\n\t" // bias toward no motion -#endif - - MERGE4PIXavg("(%%"XDI", %%"XCX")", "(%%"XSI", %%"XCX")") // center, in old and new diff --git a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopBottom.inc b/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopBottom.inc deleted file mode 100644 index e1560353..00000000 --- a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopBottom.inc +++ /dev/null @@ -1,174 +0,0 @@ -// -*- c++ -*- - -// Version for non-SSE2 - -#ifndef IS_C - -#ifdef SKIP_SEARCH - "movq %%mm6, %%mm0\n\t" // just use the results of our wierd bob -#else - - - // JA 9/Dec/2002 - // failed experiment - // but leave in placeholder for me to play about -#ifdef DONT_USE_STRANGE_BOB - // Use the best weave if diffs less than 10 as that - // means the image is still or moving cleanly - // if there is motion we will clip which will catch anything - "psubusb "_FOURS", %%mm7\n\t" // sets bits to zero if weave diff < 4 - "pxor %%mm0, %%mm0\n\t" - "pcmpeqb %%mm0, %%mm7\n\t" // all ff where weave better, else 00 - "pcmpeqb %%mm7, %%mm0\n\t" // all ff where bob better, else 00 - "pand %%mm6, %%mm0\n\t" // use bob for these pixel values - "pand %%mm5, %%mm7\n\t" // use weave for these - "por %%mm7, %%mm0\n\t" // combine both -#else - // Use the better of bob or weave - // pminub mm4, TENS // the most we care about - V_PMINUB ("%%mm4", _TENS, "%%mm0") // the most we care about - - "psubusb %%mm4, %%mm7\n\t" // foregive that much from weave est? - "psubusb "_FOURS", %%mm7\n\t" // bias it a bit toward weave - "pxor %%mm0, %%mm0\n\t" - "pcmpeqb %%mm0, %%mm7\n\t" // all ff where weave better, else 00 - "pcmpeqb %%mm7, %%mm0\n\t" // all ff where bob better, else 00 - "pand %%mm6, %%mm0\n\t" // use bob for these pixel values - "pand %%mm5, %%mm7\n\t" // use weave for these - "por %%mm7, %%mm0\n\t" // combine both -#endif - - - // pminub mm0, Max_Vals // but clip to catch the stray error - V_PMINUB ("%%mm0", _Max_Vals, "%%mm1") // but clip to catch the stray error - // pmaxub mm0, Min_Vals - V_PMAXUB ("%%mm0", _Min_Vals) - -#endif - - - MOVX" "_pDest", %%"XAX"\n\t" - -#ifdef USE_VERTICAL_FILTER - "movq %%mm0, %%mm1\n\t" - // pavgb mm0, qword ptr["XBX"] - V_PAVGB ("%%mm0", "(%%"XBX")", "%%mm2", _ShiftMask) - // movntq qword ptr["XAX"+"XDX"], mm0 - V_MOVNTQ ("(%"XAX", %%"XDX")", "%%mm0") - // pavgb mm1, qword ptr["XBX"+"XCX"] - V_PAVGB ("%%mm1", "(%%"XBX", %%"XCX")", "%%mm2", _ShiftMask) - //FIXME: XDX or XAX!! - "addq "_dst_pitchw", %%"XBX - // movntq qword ptr["XAX"+"XDX"], mm1 - V_MOVNTQ ("(%%"XAX", %%"XDX")", "%%mm1") -#else - - // movntq qword ptr["XAX"+"XDX"], mm0 - V_MOVNTQ ("(%%"XAX", %%"XDX")", "%%mm0") -#endif - - LEAX" 8(%%"XDX"), %%"XDX"\n\t" // bump offset pointer - CMPX" "_Last8", %%"XDX"\n\t" // done with line? - "jb 1b\n\t" // y - - MOVX" "_oldbx", %%"XBX"\n\t" - - : /* no outputs */ - - : "m"(pBob), - "m"(src_pitch2), - "m"(ShiftMask), - "m"(pDest), - "m"(dst_pitchw), - "m"(Last8), - "m"(pSrc), - "m"(pSrcP), - "m"(pBobP), - "m"(DiffThres), - "m"(Min_Vals), - "m"(Max_Vals), - "m"(FOURS), - "m"(TENS), - "m"(ONES), - "m"(UVMask), - "m"(Max_Mov), - "m"(YMask), - "m"(oldbx) - - : XAX, XCX, XDX, XSI, XDI, - "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)", -#ifdef __MMX__ - "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", -#endif - "memory", "cc" - ); - - // adjust for next line - pSrc += src_pitch2; - pSrcP += src_pitch2; - pDest += dst_pitch2; - pBob += src_pitch2; - pBobP += src_pitch2; - } - - return 0; -#else -#ifdef SKIP_SEARCH - out[0] = best[0]; // just use the results of our wierd bob - out[1] = best[1]; -#else - diff[0] = diff[0] - MIN (diff[0], 10) - 4; - diff[1] = diff[1] - MIN (diff[1] - 10) - 4; - if (diff[0] < 0) - out[0] = weave[0]; - else - out[0] = best[0]; - - if (diff[1] < 0) - out[1] = weave[1]; - else - out[1] = best[1]; - - - out[0] = CLAMP (out[0], MinVals[0], MaxVals[0]); - out[1] = CLAMP (out[1], MinVals[1], MaxVals[1]); -#endif - -#ifdef USE_VERTICAL_FILTER - pDest[x] = (out[0] + pBob[0]) / 2; - pDest[x + dst_pitchw] = (pBob[src_pitch2] + out[0]) / 2; - pDest[x + 1] = (out[1] + pBob[1]) / 2; - pDest[x + 1 + dst_pitchw] = (pBob[src_pitch2 + 1] + out[1]) / 2; -#else - pDest[x] = out[0]; - pDest[x+1] = out[1]; -#endif - pBob += 2; - pBobP += 2; - pSrc += 2; - pSrcP += 2; - } - // adjust for next line - pSrc = src_pitch2 * (y+1) + pWeaveSrc; - pSrcP = src_pitch2 * (y+1) + pWeaveSrcP; - pDest = dst_pitch2 * (y+1) + pWeaveDest + dst_pitch2; - - - if (TopFirst) - { - pBob = pCopySrc + src_pitch2; - pBobP = pCopySrcP + src_pitch2; - } - else - { - pBob = pCopySrc; - pBobP = pCopySrcP; - } - - pBob += src_pitch2 * (y+1); - pBobP += src_pitch2 * (y+1); - } - - return 0; - -#endif diff --git a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopEdgeA.inc b/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopEdgeA.inc deleted file mode 100644 index 6208fe8c..00000000 --- a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopEdgeA.inc +++ /dev/null @@ -1,11 +0,0 @@ -// -*- c++ -*- - -// Searches 2 pixel to the left and right, in both the old -// and new fields, but takes averages. These are even -// pixel addresses. Chroma match will be used. (YUY2) - MERGE4PIXavg("-4(%%"XDI")", "4(%%"XSI", %%"XCX", 2)") // up left, down right - MERGE4PIXavg("4(%%"XDI")", "-4(%%"XSI", %%"XCX", 2)") // up right, down left - MERGE4PIXavg("-4(%%"XDI", %%"XCX")", "4(%%"XSI", %%"XCX")") // left, right - MERGE4PIXavg("4(%%"XDI", %%"XCX")", "-4(%%"XSI", %%"XCX")") // right, left - MERGE4PIXavg("-4(%%"XDI", %%"XCX", 2)", "4(%%"XSI")") // down left, up right - MERGE4PIXavg("4(%%"XDI", %%"XCX", 2)", "-4(%%"XSI")") // down right, up left diff --git a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopEdgeA8.inc b/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopEdgeA8.inc deleted file mode 100644 index 2841c3f6..00000000 --- a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopEdgeA8.inc +++ /dev/null @@ -1,12 +0,0 @@ -// -*- c++ -*- - -// Searches 4 pixel to the left and right, in both the old -// and new fields, but takes averages. These are even -// pixel addresses. Chroma match will be used. (YUY2) - MERGE4PIXavg("-8(%%"XDI")", "8(%%"XSI", %%"XCX", 2)") // up left, down right - MERGE4PIXavg("8(%%"XDI")", "-8(%%"XSI", %%"XCX", 2)") // up right, down left - MERGE4PIXavg("-8(%%"XDI", %%"XCX")", "8(%%"XSI", %%"XCX")") // left, right - MERGE4PIXavg("8(%%"XDI", %%"XCX")", "-8(%%"XSI", %%"XCX")") // right, left - MERGE4PIXavg("-8(%%"XDI", %%"XCX", 2)", "8(%%"XSI")") // down left, up right - MERGE4PIXavg("8(%%"XDI", %%"XCX", 2)", "-8(%%"XSI")") // down right, up left - diff --git a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopOddA.inc b/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopOddA.inc deleted file mode 100644 index ab5375f4..00000000 --- a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopOddA.inc +++ /dev/null @@ -1,10 +0,0 @@ -// -*- c++ -*- - -// Searches 1 pixel to the left and right, in both the old -// and new fields, but takes averages. These are odd -// pixel addresses. Any chroma match will not be used. (YUY2) - MERGE4PIXavg("-2(%%"XDI")", "2(%%"XSI", %%"XCX", 2)") // up left, down right - MERGE4PIXavg("2(%%"XDI")", "-2(%%"XSI", %%"XCX", 2)") // up right, down left - MERGE4PIXavg("-2(%%"XDI", %%"XCX", 2)", "2(%%"XSI")") // down left, up right - MERGE4PIXavg("2(%%"XDI", %%"XCX", 2)", "-2(%%"XSI")") // down right, up left -#include "SearchLoopOddA2.inc" diff --git a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopOddA2.inc b/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopOddA2.inc deleted file mode 100644 index fd3f6fb0..00000000 --- a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopOddA2.inc +++ /dev/null @@ -1,5 +0,0 @@ -// Searches 1 pixel to the left and right, in both the old -// and new fields, but takes averages. These are odd -// pixel addresses. Any chroma match will not be used. (YUY2) - MERGE4PIXavg("-2(%%"XDI", %%"XCX")", "2(%%"XSI", %%"XCX")") // left, right - MERGE4PIXavg("2(%%"XDI", %%"XCX")", "-2(%%"XSI", %%"XCX")") // right, left diff --git a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopOddA6.inc b/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopOddA6.inc deleted file mode 100644 index cbae014e..00000000 --- a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopOddA6.inc +++ /dev/null @@ -1,11 +0,0 @@ -// -*- c++ -*- - -// Searches 3 pixels to the left and right, in both the old -// and new fields, but takes averages. These are odd -// pixel addresses. Any chroma match will not be used. (YUY2) - MERGE4PIXavg("-6(%%"XDI")", "6(%%"XSI", %%"XCX", 2)") // up left, down right - MERGE4PIXavg("6(%%"XDI")", "-6(%%"XSI", %%"XCX", 2)") // up right, down left - MERGE4PIXavg("-6(%%"XDI", %%"XCX")", "6(%%"XSI", %%"XCX")") // left, right - MERGE4PIXavg("6(%%"XDI", %%"XCX")", "-6(%%"XSI", %%"XCX")") // right, left - MERGE4PIXavg("-6(%%"XDI", %%"XCX", 2)", "6(%%"XSI")") // down left, up right - MERGE4PIXavg("6(%%"XDI", %%"XCX", 2)", "-6(%%"XSI")") // down right, up left diff --git a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopOddAH.inc b/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopOddAH.inc deleted file mode 100644 index e59e3c7e..00000000 --- a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopOddAH.inc +++ /dev/null @@ -1,10 +0,0 @@ -// Searches 1 pixel to the left and right, in both the old
-// and new fields, but takes v-half pel averages. These are odd
-// pixel addresses. Any chroma match will not be used. (YUY2)
- __asm
- {
- MERGE4PIXavgH("XDI"-2, "XDI"+"XCX"-2, "XSI"+"XCX"+2, "XSI"+2*"XCX"+2) // up left, down right
- MERGE4PIXavgH("XDI"+2, "XDI"+"XCX"+2, "XSI"+"XCX"-2, "XSI"+2*"XCX"-2) // up right, down left
- MERGE4PIXavgH("XDI"+2*"XCX"-2, "XDI"+"XCX"-2, "XSI"+"XCX"+2, "XSI"+2) // down left, up right
- MERGE4PIXavgH("XDI"+2*"XCX"+2, "XDI"+"XCX"+2, "XSI"+"XCX"-2, "XSI"-2) // down right, up left
- }
diff --git a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopOddAH2.inc b/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopOddAH2.inc deleted file mode 100644 index cd7d812a..00000000 --- a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopOddAH2.inc +++ /dev/null @@ -1,5 +0,0 @@ -// Searches 1 pixel to the left and right, in both the old -// and new fields, but takes vertical averages. These are odd -// pixel addresses. Any chroma match will not be used. (YUY2) - MERGE4PIXavgH("-2(%%"XDI", %%"XCX")", "(%%"XDI", %%"XCX")", "(%%"XSI", %%"XCX")", "2(%%"XSI", %%"XCX")") // left, right - MERGE4PIXavgH("2(%%"XDI", %%"XCX")", "(%%"XDI", %%"XCX")", "(%%"XSI", %%"XCX")", "-2(%%"XSI", %%"XCX")") // right, left diff --git a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopTop.inc b/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopTop.inc deleted file mode 100644 index 9d6a490f..00000000 --- a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopTop.inc +++ /dev/null @@ -1,254 +0,0 @@ -// -*- c++ -*- - -unsigned char* pDest; -const unsigned char* pSrcP; -const unsigned char* pSrc; -const unsigned char* pBob; -const unsigned char* pBobP; - -// long is int32 on ARCH_368, int64 on ARCH_AMD64. Declaring it this way -// saves a lot of xor's to delete 64bit garbage. - -#if defined(DBL_RESIZE) || defined(USE_FOR_DSCALER) -long src_pitch2 = src_pitch; // even & odd lines are not interleaved in DScaler -#else -long src_pitch2 = 2 * src_pitch; // even & odd lines are interleaved in Avisynth -#endif - - -long dst_pitch2 = 2 * dst_pitch; -long y; - -long Last8; - - pSrc = pWeaveSrc; // points 1 weave line above - pSrcP = pWeaveSrcP; // " - -#ifdef DBL_RESIZE - -#ifdef USE_VERTICAL_FILTER - pDest = pWeaveDest + dst_pitch2; -#else - pDest = pWeaveDest + 3*dst_pitch; -#endif - -#else - -#ifdef USE_VERTICAL_FILTER - pDest = pWeaveDest + dst_pitch; -#else - pDest = pWeaveDest + dst_pitch2; -#endif - -#endif - - if (TopFirst) - { - pBob = pCopySrc + src_pitch2; // remember one weave line just copied previously - pBobP = pCopySrcP + src_pitch2; - } - else - { - pBob = pCopySrc; - pBobP = pCopySrcP; - } - -#ifndef IS_C - -#ifndef _pBob -#define _pBob "%0" -#define _src_pitch2 "%1" -#define _ShiftMask "%2" -#define _pDest "%3" -#define _dst_pitchw "%4" -#define _Last8 "%5" -#define _pSrc "%6" -#define _pSrcP "%7" -#define _pBobP "%8" -#define _DiffThres "%9" -#define _Min_Vals "%10" -#define _Max_Vals "%11" -#define _FOURS "%12" -#define _TENS "%13" -#define _ONES "%14" -#define _UVMask "%15" -#define _Max_Mov "%16" -#define _YMask "%17" -#define _oldbx "%18" -#endif - Last8 = (rowsize-8); - - for (y=1; y < FldHeight-1; y++) - { - long dst_pitchw = dst_pitch; // local stor so asm can ref - int64_t Max_Mov = 0x0404040404040404ull; - int64_t DiffThres = 0x0f0f0f0f0f0f0f0full; - int64_t YMask = 0x00ff00ff00ff00ffull; // keeps only luma - int64_t UVMask = 0xff00ff00ff00ff00ull; // keeps only chroma - int64_t TENS = 0x0a0a0a0a0a0a0a0aull; - int64_t FOURS = 0x0404040404040404ull; - int64_t ONES = 0x0101010101010101ull; - int64_t Min_Vals = 0x0000000000000000ull; - int64_t Max_Vals = 0x0000000000000000ull; - int64_t ShiftMask = 0xfefffefffefffeffull; - - long oldbx; - - // pretend it's indented -->> - __asm__ __volatile__ - ( - // Loop general reg usage - // - // XAX - pBobP, then pDest - // XBX - pBob - // XCX - src_pitch2 - // XDX - current offset - // XDI - prev weave pixels, 1 line up - // XSI - next weave pixels, 1 line up - - // Save "XBX" (-fPIC) - MOVX" %%"XBX", "_oldbx"\n\t" - - // simple bob first 8 bytes - MOVX" "_pBob", %%"XBX"\n\t" - MOVX" "_src_pitch2", %%"XCX"\n\t" - -#ifdef USE_VERTICAL_FILTER - "movq (%%"XBX"), %%mm0\n\t" - "movq (%%"XBX", %%"XCX"), %%mm1\n\t" //, qword ptr["XBX"+"XCX"] - "movq %%mm0, %%mm2\n\t" - V_PAVGB ("%%mm2", "%%mm1", "%%mm3", _ShiftMask) // halfway between - V_PAVGB ("%%mm0", "%%mm2", "%%mm3", _ShiftMask) // 1/4 way - V_PAVGB ("%%mm1", "%%mm2", "%%mm3", _ShiftMask) // 3/4 way - MOVX" "_pDest", %%"XDI"\n\t" - MOVX" "_dst_pitchw", %%"XAX"\n\t" - V_MOVNTQ ("(%%"XDI")", "%%mm0") - V_MOVNTQ ("(%%"XDI", %%"XAX")", "%%mm1") // qword ptr["XDI"+"XAX"], mm1 - - // simple bob last 8 bytes - MOVX" "_Last8", %%"XDX"\n\t" - LEAX" (%%"XBX", %%"XDX"), %%"XSI"\n\t" // ["XBX"+"XDX"] - "movq (%%"XSI"), %%mm0\n\t" - "movq (%%"XSI", %%"XCX"), %%mm1\n\t" // qword ptr["XSI"+"XCX"] - "movq %%mm0, %%mm2\n\t" - V_PAVGB ("%%mm2", "%%mm1", "%%mm3", _ShiftMask) // halfway between - V_PAVGB ("%%mm0", "%%mm2", "%%mm3", _ShiftMask) // 1/4 way - V_PAVGB ("%%mm1", "%%mm2", "%%mm3", _ShiftMask) // 3/4 way - ADDX" %%"XDX", %%"XDI"\n\t" // last 8 bytes of dest - V_MOVNTQ ("%%"XDI"", "%%mm0") - V_MOVNTQ ("(%%"XDI", %%"XAX")", "%%mm1") // qword ptr["XDI"+"XAX"], mm1) - -#else - "movq (%%"XBX"), %%mm0\n\t" - // pavgb mm0, qword ptr["XBX"+"XCX"] - V_PAVGB ("%%mm0", "(%%"XBX", %%"XCX")", "%%mm2", _ShiftMask) // qword ptr["XBX"+"XCX"], mm2, ShiftMask) - MOVX" "_pDest", %%"XDI"\n\t" - V_MOVNTQ ("(%%"XDI")", "%%mm0") - - // simple bob last 8 bytes - MOVX" "_Last8", %%"XDX"\n\t" - LEAX" (%%"XBX", %%"XDX"), %%"XSI"\n\t" //"XSI", ["XBX"+"XDX"] - "movq (%%"XSI"), %%mm0\n\t" - // pavgb mm0, qword ptr["XSI"+"XCX"] - V_PAVGB ("%%mm0", "(%%"XSI", %%"XCX")", "%%mm2", _ShiftMask) // qword ptr["XSI"+"XCX"], mm2, ShiftMask) - V_MOVNTQ ("(%%"XDI", %%"XDX")", "%%mm0") // qword ptr["XDI"+"XDX"], mm0) -#endif - // now loop and get the middle qwords - MOVX" "_pSrc", %%"XSI"\n\t" - MOVX" "_pSrcP", %%"XDI"\n\t" - MOVX" $8, %%"XDX"\n\t" // curr offset longo all lines - - "1:\n\t" - MOVX" "_pBobP", %%"XAX"\n\t" - ADDX" $8, %%"XDI"\n\t" - ADDX" $8, %%"XSI"\n\t" - ADDX" $8, %%"XBX"\n\t" - ADDX" %%"XDX", %%"XAX"\n\t" - -#ifdef USE_STRANGE_BOB -#include "StrangeBob.inc" -#else -#include "WierdBob.inc" -#endif - - // For non-SSE2: - // through out most of the rest of this loop we will maintain - // mm4 our min bob value - // mm5 best weave pixels so far - // mm6 our max Bob value - // mm7 best weighted pixel ratings so far - - // We will keep a slight bias to using the weave pixels - // from the current location, by rating them by the min distance - // from the Bob value instead of the avg distance from that value. - // our best and only rating so far - "pcmpeqb %%mm7, %%mm7\n\t" // ffff, say we didn't find anything good yet - -#else - Last8 = (rowsize - 4); - - for (y=1; y < FldHeight-1; y++) - { - #ifdef USE_STRANGE_BOB - long DiffThres = 0x0f; - #endif - - #ifndef SKIP_SEARCH - long weave[2], MaxVals[2], MinVals[2]; - #endif - - long diff[2], best[2], avg[2], diff2[2], out[2], x; - -#ifdef USE_VERTICAL_FILTER - pDest[0] = (3 * pBob[0] + pBob[src_pitch2]) / 4; - pDest[1] = (3 * pBob[1] + pBob[src_pitch2 + 1]) / 4; - pDest[2] = (3 * pBob[2] + pBob[src_pitch2 + 2]) / 4; - pDest[3] = (3 * pBob[3] + pBob[src_pitch2 + 3]) / 4; - pDest[dst_pitchw] = (pBob[0] + 3 * pBob[src_pitch2]) / 4; - pDest[dst_pitchw + 1] = (pBob[1] + 3 * pBob[src_pitch2 + 1]) / 4; - pDest[dst_pitchw + 2] = (pBob[2] + 3 * pBob[src_pitch2 + 2]) / 4; - pDest[dst_pitchw + 3] = (pBob[3] + 3 * pBob[src_pitch2 + 3]) / 4; - - // simple bob last byte - pDest[Last8] = (3 * pBob[Last8] + pBob[Last8 + src_pitch2]) / 4; - pDest[Last8 + 1] = (3 * pBob[Last8 + 1] + pBob[Last8 + src_pitch2 + 1]) / 4; - pDest[Last8 + 2] = (3 * pBob[Last8 + 2] + pBob[Last8 + src_pitch2 + 2]) / 4; - pDest[Last8 + 3] = (3 * pBob[Last8 + 3] + pBob[Last8 + src_pitch2 + 3]) / 4; - pDest[Last8 + src_pitch2] = (pBob[Last8] + 3 * pBob[Last8 + src_pitch2]) / 4; - pDest[Last8 + src_pitch2 + 1] = (pBob[Last8 + 1] + 3 * pBob[Last8 + src_pitch2 + 1]) / 4; - pDest[Last8 + src_pitch2 + 2] = (pBob[Last8 + 2] + 3 * pBob[Last8 + src_pitch2 + 2]) / 4; - pDest[Last8 + src_pitch2 + 3] = (pBob[Last8 + 3] + 3 * pBob[Last8 + src_pitch2 + 3]) / 4; -#else - pDest[0] = (pBob[0] + pBob[src_pitch2 + 1]) / 2; - pDest[1] = (pBob[1] + pBob[src_pitch2 + 1]) / 2; - pDest[2] = (pBob[2] + pBob[src_pitch2 + 2]) / 2; - pDest[3] = (pBob[3] + pBob[src_pitch2 + 3]) / 2; - - // simple bob last byte - pDest[Last8] = (pBob[Last8] + pBob[Last8 + src_pitch2]) / 2; - pDest[Last8 + 1] = (pBob[Last8 + 1] + pBob[Last8 + src_pitch2 + 1]) / 2; - pDest[Last8 + 2] = (pBob[Last8 + 2] + pBob[Last8 + src_pitch2 + 2]) / 2; - pDest[Last8 + 3] = (pBob[Last8 + 3] + pBob[Last8 + src_pitch2 + 3]) / 2; -#endif - - pBob += 4; - pBobP += 4; - pSrc += 4; - pSrcP += 4; - - for (x=4; x < Last8; x += 2) { - -#ifdef USE_STRANGE_BOB -#include "StrangeBob.inc" -#else -#include "WierdBob.inc" -#endif - - // We will keep a slight bias to using the weave pixels - // from the current location, by rating them by the min distance - // from the Bob value instead of the avg distance from that value. - // our best and only rating so far - diff[0] = diff[1] = 255; - - -#endif diff --git a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopVA.inc b/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopVA.inc deleted file mode 100644 index 3e3d19b5..00000000 --- a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopVA.inc +++ /dev/null @@ -1,6 +0,0 @@ -// -*- c++ -*- - -// Searches the center vertical line above center and below, in both the old -// and new fields, but takes averages. These are even pixel addresses. - MERGE4PIXavg("(%%"XDI", %%"XCX", 2)", "(%%"XSI")") // down, up - MERGE4PIXavg("(%%"XDI")", "(%%"XSI", %%"XCX", 2)") // up, down diff --git a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopVAH.inc b/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopVAH.inc deleted file mode 100644 index 33155bc1..00000000 --- a/gst/deinterlace2/tvtime/tomsmocomp/SearchLoopVAH.inc +++ /dev/null @@ -1,6 +0,0 @@ -// -*- c++ -*- - -// Searches the center vertical line above center and below, in both the old -// and new fields, but takes averages. These are even pixel addresses. - MERGE4PIXavgH("(%%"XDI", %%"XCX", 2)", "(%%"XDI", %%"XCX")", "(%%"XSI", %%"XCX")", "(%%"XSI")") // down, up - MERGE4PIXavgH("(%%"XDI")", "(%%"XDI", %%"XCX")", "(%%"XSI", %%"XCX")", "(%%"XSI", %%"XCX", 2)") // up, down diff --git a/gst/deinterlace2/tvtime/tomsmocomp/StrangeBob.inc b/gst/deinterlace2/tvtime/tomsmocomp/StrangeBob.inc deleted file mode 100644 index 45b4c865..00000000 --- a/gst/deinterlace2/tvtime/tomsmocomp/StrangeBob.inc +++ /dev/null @@ -1,435 +0,0 @@ -// -*- c++ -*- - - // First, get and save our possible Bob values - // Assume our pixels are layed out as follows with x the calc'd bob value - // and the other pixels are from the current field - // - // j a b c k current field - // x calculated line - // m d e f n current field - // - // we calc the bob value luma value as: - // if |j - n| < Thres && |a - m| > Thres - // avg(j,n) - // end if - // if |k - m| < Thres && |c - n| > Thres - // avg(k,m) - // end if - // if |c - d| < Thres && |b - f| > Thres - // avg(c,d) - // end if - // if |a - f| < Thres && |b - d| > Thres - // avg(a,f) - // end if - // if |b - e| < Thres - // avg(b,e) - // end if - // pickup any thing not yet set with avg(b,e) - -#ifndef IS_C - - // j, n - "pxor %%mm5, %%mm5\n\t" - "pxor %%mm6, %%mm6\n\t" - "pxor %%mm7, %%mm7\n\t" - - "movq -2(%%"XBX"), %%mm0\n\t" // value a from top left - "movq -4(%%"XBX", %%"XCX"), %%mm1\n\t" // value m from bottom right - - "movq %%mm0, %%mm3\n\t" - "psubusb %%mm1, %%mm3\n\t" - "psubusb %%mm0, %%mm1\n\t" - "por %%mm1, %%mm3\n\t" // abs(a,m) - - "psubusb "_DiffThres", %%mm3\n\t" // nonzero where abs(a,m) > Thres else 0 - "pxor %%mm4, %%mm4\n\t" - "pcmpeqb %%mm4, %%mm3\n\t" // now ff where abs(a,m) < Thres, else 00 - "pcmpeqb %%mm3, %%mm4\n\t" // here ff where abs(a,m) > Thres, else 00 - - - "movq -4(%%"XBX"), %%mm0\n\t" // value j - "movq 4(%%"XBX", %%"XCX"), %%mm1\n\t" // value n - "movq %%mm0, %%mm2\n\t" - V_PAVGB ("%%mm2", "%%mm1", "%%mm3", _ShiftMask) // avg(j,n) - "movq %%mm0, %%mm3\n\t" - "psubusb %%mm1, %%mm0\n\t" - "psubusb %%mm3, %%mm1\n\t" - "por %%mm1, %%mm0\n\t" // abs(j,n) - - "movq %%mm0, %%mm1\n\t" - "psubusb "_DiffThres", %%mm1\n\t" // nonzero where abs(j,n) > Thres else 0 - "pxor %%mm3, %%mm3\n\t" - "pcmpeqb %%mm3, %%mm1\n\t" // now ff where abs(j,n) < Thres, else 00 - - "pand %%mm4, %%mm1\n\t" - "pand %%mm1, %%mm2\n\t" - "pand %%mm1, %%mm0\n\t" - - "movq %%mm1, %%mm3\n\t" - "pxor %%mm5, %%mm3\n\t" - "pand %%mm3, %%mm6\n\t" - "pand %%mm3, %%mm7\n\t" - "pand %%mm3, %%mm5\n\t" - - "por %%mm1, %%mm5\n\t" - "por %%mm2, %%mm6\n\t" - "por %%mm0, %%mm7\n\t" - - // k & m - "movq 2(%%"XBX"), %%mm0\n\t" // value c from top left - "movq 4(%%"XBX", %%"XCX"), %%mm1\n\t" // value n from bottom right - - "movq %%mm0, %%mm3\n\t" - "psubusb %%mm1, %%mm3\n\t" - "psubusb %%mm0, %%mm1\n\t" - "por %%mm1, %%mm3\n\t" // abs(c,n) - - "psubusb "_DiffThres", %%mm3\n\t" // nonzero where abs(c,n) > Thres else 0 - "pxor %%mm4, %%mm4\n\t" - "pcmpeqb %%mm4, %%mm3\n\t" // now ff where abs(c,n) < Thres, else 00 - "pcmpeqb %%mm3, %%mm4\n\t" // here ff where abs(c,n) > Thres, else 00 - - - "movq 4(%%"XBX"), %%mm0\n\t" // value k - "movq -4(%%"XBX", %%"XCX"), %%mm1\n\t" // value m - "movq %%mm0, %%mm2\n\t" - V_PAVGB ("%%mm2", "%%mm1", "%%mm3", _ShiftMask) // avg(k,m) - "movq %%mm0, %%mm3\n\t" - "psubusb %%mm1, %%mm0\n\t" - "psubusb %%mm3, %%mm1\n\t" - "por %%mm1, %%mm0\n\t" // abs(k,m) - - "movq %%mm0, %%mm1\n\t" - "psubusb "_DiffThres", %%mm1\n\t" // nonzero where abs(k,m) > Thres else 0 - "pxor %%mm3, %%mm3\n\t" - "pcmpeqb %%mm3, %%mm1\n\t" // now ff where abs(k,m) < Thres, else 00 - - "pand %%mm4, %%mm1\n\t" - - "pand %%mm1, %%mm2\n\t" - "pand %%mm1, %%mm0\n\t" - - "movq %%mm1, %%mm3\n\t" - "pxor %%mm5, %%mm3\n\t" - "pand %%mm3, %%mm6\n\t" - "pand %%mm3, %%mm7\n\t" - "pand %%mm3, %%mm5\n\t" - - "por %%mm1, %%mm5\n\t" - "por %%mm2, %%mm6\n\t" - "por %%mm0, %%mm7\n\t" - - - // c & d - "movq (%%"XBX"), %%mm0\n\t" // value b from top left - "movq 2(%%"XBX", %%"XCX"), %%mm1\n\t" // value f from bottom right - - "movq %%mm0, %%mm3\n\t" - "psubusb %%mm1, %%mm3\n\t" - "psubusb %%mm0, %%mm1\n\t" - "por %%mm1, %%mm3\n\t" // abs(b,f) - - "psubusb "_DiffThres", %%mm3\n\t" // nonzero where abs(b,f) > Thres else 0 - "pxor %%mm4, %%mm4\n\t" - "pcmpeqb %%mm4, %%mm3\n\t" // now ff where abs(b,f) < Thres, else 00 - "pcmpeqb %%mm3, %%mm4\n\t" // here ff where abs(b,f) > Thres, else 00 - - "movq 2(%%"XBX"), %%mm0\n\t" // value c - "movq -2(%%"XBX", %%"XCX"), %%mm1\n\t" // value d - "movq %%mm0, %%mm2\n\t" - V_PAVGB ("%%mm2", "%%mm1", "%%mm3", _ShiftMask) // avg(c,d) - "movq %%mm0, %%mm3\n\t" - "psubusb %%mm1, %%mm0\n\t" - "psubusb %%mm3, %%mm1\n\t" - "por %%mm1, %%mm0\n\t" // abs(c,d) - - "movq %%mm0, %%mm1\n\t" - "psubusb "_DiffThres", %%mm1\n\t" // nonzero where abs(c,d) > Thres else 0 - "pxor %%mm3, %%mm3\n\t" - "pcmpeqb %%mm3, %%mm1\n\t" // now ff where abs(c,d) < Thres, else 00 - - "pand %%mm4, %%mm1\n\t" - - "pand %%mm1, %%mm2\n\t" - "pand %%mm1, %%mm0\n\t" - - "movq %%mm1, %%mm3\n\t" - "pxor %%mm5, %%mm3\n\t" - "pand %%mm3, %%mm6\n\t" - "pand %%mm3, %%mm7\n\t" - "pand %%mm3, %%mm5\n\t" - - "por %%mm1, %%mm5\n\t" - "por %%mm2, %%mm6\n\t" - "por %%mm0, %%mm7\n\t" - - // a & f - "movq (%%"XBX"), %%mm0\n\t" // value b from top left - "movq -2(%%"XBX", %%"XCX"), %%mm1\n\t" // value d from bottom right - - "movq %%mm0, %%mm3\n\t" - "psubusb %%mm1, %%mm3\n\t" - "psubusb %%mm0, %%mm1\n\t" - "por %%mm1, %%mm3\n\t" // abs(b,d) - - "psubusb "_DiffThres", %%mm3\n\t" // nonzero where abs(b,d) > Thres else 0 - "pxor %%mm4, %%mm4\n\t" - "pcmpeqb %%mm4, %%mm3\n\t" // now ff where abs(b,d) < Thres, else 00 - "pcmpeqb %%mm3, %%mm4\n\t" // here ff where abs(b,d) > Thres, else 00 - - "movq -2(%%"XBX"), %%mm0\n\t" // value a - "movq 2(%%"XBX", %%"XCX"), %%mm1\n\t" // value f - "movq %%mm0, %%mm2\n\t" - V_PAVGB ("%%mm2", "%%mm1", "%%mm3", _ShiftMask) // avg(a,f) - "movq %%mm0, %%mm3\n\t" - "psubusb %%mm1, %%mm0\n\t" - "psubusb %%mm3, %%mm1\n\t" - "por %%mm1, %%mm0\n\t" // abs(a,f) - - "movq %%mm0, %%mm1\n\t" - "psubusb "_DiffThres", %%mm1\n\t" // nonzero where abs(a,f) > Thres else 0 - "pxor %%mm3, %%mm3\n\t" - "pcmpeqb %%mm3, %%mm1\n\t" // now ff where abs(a,f) < Thres, else 00 - - "pand %%mm4, %%mm1\n\t" - - "pand %%mm1, %%mm2\n\t" - "pand %%mm1, %%mm0\n\t" - - "movq %%mm1, %%mm3\n\t" - "pxor %%mm5, %%mm3\n\t" - "pand %%mm3, %%mm6\n\t" - "pand %%mm3, %%mm7\n\t" - "pand %%mm3, %%mm5\n\t" - - "por %%mm1, %%mm5\n\t" - "por %%mm2, %%mm6\n\t" - "por %%mm0, %%mm7\n\t" - - "pand "_YMask", %%mm5\n\t" // mask out chroma from here - "pand "_YMask", %%mm6\n\t" // mask out chroma from here - "pand "_YMask", %%mm7\n\t" // mask out chroma from here - - // b,e - "movq (%%"XBX"), %%mm0\n\t" // value b from top - "movq (%%"XBX", %%"XCX"), %%mm1\n\t" // value e from bottom - "movq %%mm0, %%mm2\n\t" - V_PAVGB ("%%mm2", "%%mm1", "%%mm3", _ShiftMask) // avg(b,e) - "movq %%mm0, %%mm3\n\t" - "psubusb %%mm1, %%mm0\n\t" - "psubusb %%mm3, %%mm1\n\t" - "por %%mm1, %%mm0\n\t" // abs(b,e) - - "movq %%mm0, %%mm1\n\t" - "psubusb "_DiffThres", %%mm1\n\t" // nonzero where abs(b,e) > Thres else 0 - "pxor %%mm3, %%mm3\n\t" - "pcmpeqb %%mm3, %%mm1\n\t" // now ff where abs(b,e) < Thres, else 00 - - "pand %%mm1, %%mm2\n\t" - "pand %%mm1, %%mm0\n\t" - - "movq %%mm1, %%mm3\n\t" - "pxor %%mm5, %%mm3\n\t" - "pand %%mm3, %%mm6\n\t" - "pand %%mm3, %%mm7\n\t" - "pand %%mm3, %%mm5\n\t" - - "por %%mm1, %%mm5\n\t" - "por %%mm2, %%mm6\n\t" - "por %%mm0, %%mm7\n\t" - - // bob in any leftovers - "movq (%%"XBX"), %%mm0\n\t" // value b from top - "movq (%%"XBX", %%"XCX"), %%mm1\n\t" // value e from bottom - - -// We will also calc here the max/min values to later limit comb -// so the max excursion will not exceed the Max_Comb constant - -#ifdef SKIP_SEARCH - "movq %%mm0, %%mm2\n\t" -// pminub %%mm2, %%mm1 - V_PMINUB ("%%mm2", "%%mm1", "%%mm4") - -// pmaxub %%mm6, %%mm2 // clip our current results so far to be above this - V_PMAXUB ("%%mm6", "%%mm2") - "movq %%mm0, %%mm2\n\t" - V_PMAXUB ("%%mm2", "%%mm1") -// pminub %%mm6, %%mm2 // clip our current results so far to be below this - V_PMINUB ("%%mm6", "%%mm2", "%%mm4") - -#else - "movq %%mm0, %%mm2\n\t" - "movq (%%"XAX"), %%mm4\n\t" - "psubusb %%mm4, %%mm2\n\t" - "psubusb %%mm0, %%mm4\n\t" - "por %%mm2, %%mm4\n\t" // abs diff - - "movq %%mm1, %%mm2\n\t" - "movq (%%"XAX", %%"XCX"), %%mm3\n\t" - "psubusb %%mm3, %%mm2\n\t" - "psubusb %%mm1, %%mm3\n\t" - "por %%mm2, %%mm3\n\t" // abs diff -// pmaxub %%mm3, %%mm4 // top or bottom pixel moved most - V_PMAXUB ("%%mm3", "%%mm4") // top or bottom pixel moved most - "psubusb "_DiffThres", %%mm3\n\t" // moved more than allowed? or goes to 0? - "pxor %%mm4, %%mm4\n\t" - "pcmpeqb %%mm4, %%mm3\n\t" // now ff where low motion, else high motion - - "movq %%mm0, %%mm2\n\t" -// pminub %%mm2, %%mm1 - V_PMINUB ("%%mm2", "%%mm1", "%%mm4") - -// pmaxub %%mm6, %%mm2 // clip our current results so far to be above this - V_PMAXUB ("%%mm6", "%%mm2") - - "psubusb %%mm3, %%mm2\n\t" // maybe decrease it to 0000.. if no surround motion - "movq %%mm2, "_Min_Vals"\n\t" - - "movq %%mm0, %%mm2\n\t" - V_PMAXUB ("%%mm2", "%%mm1") -// pminub %%mm6, %%mm2 // clip our current results so far to be below this - V_PMINUB ("%%mm6", "%%mm2", "%%mm4") - "paddusb %%mm3, %%mm2\n\t" // maybe increase it to ffffff if no surround motion - "movq %%mm2, "_Max_Vals"\n\t" -#endif - - "movq %%mm0, %%mm2\n\t" -// pavgb %%mm2, %%mm1 // avg(b,e) - V_PAVGB ("%%mm2", "%%mm1", "%%mm3", _ShiftMask) // avg(b,e) - - "movq %%mm0, %%mm3\n\t" - "psubusb %%mm1, %%mm3\n\t" - "psubusb %%mm0, %%mm1\n\t" - "por %%mm1, %%mm3\n\t" // abs(b,e) - "movq %%mm3, %%mm1\n\t" // keep copy of diffs - - "pxor %%mm4, %%mm4\n\t" - "psubusb %%mm7, %%mm3\n\t" // nonzero where new weights bigger, else 0 - "pcmpeqb %%mm4, %%mm3\n\t" // now ff where new better, else 00 - "pcmpeqb %%mm0, %%mm0\n\t" - "pandn %%mm0, %%mm5\n\t" - "por %%mm5, %%mm3\n\t" - "pcmpeqb %%mm3, %%mm4\n\t" // here ff where old better, else 00 - - "pand %%mm3, %%mm1\n\t" - "pand %%mm3, %%mm2\n\t" - - "pand %%mm4, %%mm6\n\t" - "pand %%mm4, %%mm7\n\t" - - "por %%mm2, %%mm6\n\t" // our x2 value - "por %%mm1, %%mm7\n\t" // our x2 diffs - "movq %%mm7, %%mm4\n\t" // save as bob uncertainty indicator - -#else - - diff[0] = -1; - diff[1] = -1; - best[0] = 0; - best[1] = 0; - // j, n - if (ABS (pBob[-2] - pBob[src_pitch2 - 4]) < DiffThres && - ABS (pBob[-4] - pBob[src_pitch2 + 4]) > DiffThres) { - best[0] = (pBob[-2] + pBob[src_pitch2 - 4]) / 2; - diff[0] = ABS (pBob[-2] - pBob[src_pitch2 - 4]); - } - if (ABS (pBob[-1] - pBob[src_pitch2 - 3]) < DiffThres && - ABS (pBob[-3] - pBob[src_pitch2 + 5]) > DiffThres) { - best[1] = (pBob[-1] + pBob[src_pitch2 - 3]) / 2; - diff[1] = ABS (pBob[-1] - pBob[src_pitch2 - 3]); - } - - // k & m - if (ABS (pBob[2] - pBob[src_pitch2 + 4]) < DiffThres && - ABS (pBob[4] - pBob[src_pitch2 - 4]) > DiffThres) { - best[0] = (pBob[4] + pBob[src_pitch2 - 4]) / 2; - diff[0] = ABS (pBob[4] - pBob[src_pitch2 - 4]); - } - - if (ABS (pBob[3] - pBob[src_pitch2 + 5]) < DiffThres && - ABS (pBob[5] - pBob[src_pitch2 - 3]) > DiffThres) { - best[1] = (pBob[5] + pBob[src_pitch2 - 3]) / 2; - diff[1] = ABS (pBob[5] - pBob[src_pitch2 - 3]); - } - - // c & d - if (ABS (pBob[0] - pBob[src_pitch2 + 2]) < DiffThres && - ABS (pBob[2] - pBob[src_pitch2 - 2]) > DiffThres) { - best[0] = (pBob[2] + pBob[src_pitch2 - 2]) / 2; - diff[0] = ABS (pBob[2] - pBob[src_pitch2 - 2]); - } - - if (ABS (pBob[1] - pBob[src_pitch2 + 3]) < DiffThres && - ABS (pBob[3] - pBob[src_pitch2 - 1]) > DiffThres) { - best[1] = (pBob[3] + pBob[src_pitch2 - 1]) / 2; - diff[1] = ABS (pBob[3] - pBob[src_pitch2 - 1]); - } - - // a & f - if (ABS (pBob[0] - pBob[src_pitch2 - 2]) < DiffThres && - ABS (pBob[-2] - pBob[src_pitch2 + 2]) > DiffThres) { - best[0] = (pBob[-2] + pBob[src_pitch2 + 2]) / 2; - diff[0] = ABS (pBob[-2] - pBob[src_pitch2 + 2]); - } - - if (ABS (pBob[1] - pBob[src_pitch2 - 1]) < DiffThres && - ABS (pBob[-1] - pBob[src_pitch2 + 3]) > DiffThres) { - best[1] = (pBob[-1] + pBob[src_pitch2 + 3]) / 2; - diff[1] = ABS (pBob[-1] - pBob[src_pitch2 + 3]); - } - - // b,e - if (ABS (pBob[0] - pBob[src_pitch2]) < DiffThres) { - best[0] = (pBob[0] + pBob[src_pitch2]) / 2; - diff[0] = ABS (pBob[0] - pBob[src_pitch2]); - } - - if (ABS (pBob[1] - pBob[src_pitch2 + 1]) < DiffThres) { - best[1] = (pBob[1] + pBob[src_pitch2 + 1]) / 2; - diff[1] = ABS (pBob[1] - pBob[src_pitch2 + 1]); - } - - -// We will also calc here the max/min values to later limit comb -// so the max excursion will not exceed the Max_Comb constant - -#ifdef SKIP_SEARCH - best[0] = CLAMP (best[0], MIN (pBob[src_pitch2], pBob[0]), MAX (pBob[src_pitch2], pBob[0])); - best[1] = CLAMP (best[1], MIN (pBob[src_pitch2 + 1], pBob[1]), MAX (pBob[src_pitch2 + 1], pBob[1])); -#else - mov[0] = MAX (ABS (pBob[0] - pBobP[0]), ABS (pBob[src_pitch2] - pBobP[src_pitch2])); - mov[1] = MAX (ABS (pBob[1] - pBobP[1]), ABS (pBob[src_pitch2 + 1] - pBobP[src_pitch2 + 1])); - - MinVals[0] = 0; - MinVals[1] = 0; - MaxVals[0] = 255; - MaxVals[1] = 255; - if (mov[0] > DiffThres) { - MinVals[0] = MAX (MIN (pBob[0], pBob[src_pitch2]), best[0]); - MaxVals[0] = MIN (MAX (pBob[0], pBob[src_pitch2]), best[0]); - } - - if (mov[1] > DiffThres) { - MinVals[1] = MAX (MIN (pBob[1], pBob[src_pitch2+1]), best[1]); - MaxVals[1] = MIN (MAX (pBob[1], pBob[src_pitch2+1]), best[1]); - } - - best[0] = CLAMP (best[0], MIN (pBob[src_pitch2], pBob[0]), MAX (pBob[src_pitch2], pBob[0])); - best[1] = CLAMP (best[1], MIN (pBob[src_pitch2 + 1], pBob[1]), MAX (pBob[src_pitch2 + 1], pBob[1])); -#endif - avg[0] = (pBob[src_pitch2] + pBob[0]) / 2; - avg[1] = (pBob[src_pitch2 + 1] + pBob[1]) / 2; - diff2[0] = ABS (pBob[src_pitch2 + 1] - pBob[1]); - diff2[1] = ABS (pBob[src_pitch2 + 1] - pBob[1]); - - if (diff[0] == -1 || diff2[0] < diff[0]) { - best[0] = avg[0]; - diff[0] = diff2[0]; - } - - if (diff[1] == -1 || diff2[1] < diff[1]) { - best[1] = avg[1]; - diff[1] = diff2[1]; - } -#endif diff --git a/gst/deinterlace2/tvtime/tomsmocomp/TomsMoCompAll.inc b/gst/deinterlace2/tvtime/tomsmocomp/TomsMoCompAll.inc deleted file mode 100644 index 89ed39e4..00000000 --- a/gst/deinterlace2/tvtime/tomsmocomp/TomsMoCompAll.inc +++ /dev/null @@ -1,241 +0,0 @@ -/* - * GStreamer - * Copyright (c) 2002 Tom Barry All rights reserved. - * - * 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. - */ - -/* - * Relicensed for GStreamer from GPL to LGPL with permit from Tom Barry. - * See: http://bugzilla.gnome.org/show_bug.cgi?id=163578 - */ - - -#ifndef TopFirst -#define TopFirst IsOdd -#endif - -#ifdef SEFUNC -#undef SEFUNC -#endif - -#if defined(IS_MMXEXT) -#define SEFUNC(x) Search_Effort_MMXEXT_##x(int src_pitch, int dst_pitch, int rowsize, const unsigned char *pWeaveSrc, const unsigned char *pWeaveSrcP, unsigned char *pWeaveDest, int IsOdd, const unsigned char *pCopySrc, const unsigned char *pCopySrcP, int FldHeight) -#elif defined(IS_3DNOW) -#define SEFUNC(x) Search_Effort_3DNOW_##x(int src_pitch, int dst_pitch, int rowsize, const unsigned char *pWeaveSrc, const unsigned char *pWeaveSrcP, unsigned char *pWeaveDest, int IsOdd, const unsigned char *pCopySrc, const unsigned char *pCopySrcP, int FldHeight) -#elif defined(IS_MMX) -#define SEFUNC(x) Search_Effort_MMX_##x(int src_pitch, int dst_pitch, int rowsize, const unsigned char *pWeaveSrc, const unsigned char *pWeaveSrcP, unsigned char *pWeaveDest, int IsOdd, const unsigned char *pCopySrc, const unsigned char *pCopySrcP, int FldHeight) -#else -#define SEFUNC(x) Search_Effort_C_##x(int src_pitch, int dst_pitch, int rowsize, const unsigned char *pWeaveSrc, const unsigned char *pWeaveSrcP, unsigned char *pWeaveDest, int IsOdd, const unsigned char *pCopySrc, const unsigned char *pCopySrcP, int FldHeight) -#endif - -#include "TomsMoCompAll2.inc" - -#define USE_STRANGE_BOB - -#include "TomsMoCompAll2.inc" - -#undef USE_STRANGE_BOB - -#undef SEFUNC -#if defined(IS_MMXEXT) -#define SEFUNC(x) Search_Effort_MMXEXT_##x(src_pitch, dst_pitch, rowsize, pWeaveSrc, pWeaveSrcP, pWeaveDest, IsOdd, pCopySrc, pCopySrcP, FldHeight) -#elif defined(IS_3DNOW) -#define SEFUNC(x) Search_Effort_3DNOW_##x(src_pitch, dst_pitch, rowsize, pWeaveSrc, pWeaveSrcP, pWeaveDest, IsOdd, pCopySrc, pCopySrcP, FldHeight) -#elif defined(IS_MMX) -#define SEFUNC(x) Search_Effort_MMX_##x(src_pitch, dst_pitch, rowsize, pWeaveSrc, pWeaveSrcP, pWeaveDest, IsOdd, pCopySrc, pCopySrcP, FldHeight) -#else -#define SEFUNC(x) Search_Effort_C_##x(src_pitch, dst_pitch, rowsize, pWeaveSrc, pWeaveSrcP, pWeaveDest, IsOdd, pCopySrc, pCopySrcP, FldHeight) -#endif - -void FUNCT_NAME(GstDeinterlaceMethod *d_method, GstDeinterlace2* object, GstBuffer *outbuf) -{ - GstDeinterlaceMethodTomsMoComp *self = GST_DEINTERLACE_METHOD_TOMSMOCOMP (d_method); - long SearchEffort = self->search_effort; - int UseStrangeBob = self->strange_bob; - int IsOdd; - const unsigned char *pWeaveSrc; - const unsigned char *pWeaveSrcP; - unsigned char *pWeaveDest; - const unsigned char *pCopySrc; - const unsigned char *pCopySrcP; - unsigned char *pCopyDest; - int src_pitch; - int dst_pitch; - int rowsize; - int FldHeight; - - /* double stride do address just every odd/even scanline */ - src_pitch = object->field_stride; - dst_pitch = object->row_stride; - rowsize = object->row_stride; - FldHeight = object->field_height; - - pCopySrc = GST_BUFFER_DATA(object->field_history[object->history_count-1].buf); - pCopySrcP = GST_BUFFER_DATA(object->field_history[object->history_count-3].buf); - pWeaveSrc = GST_BUFFER_DATA(object->field_history[object->history_count-2].buf); - pWeaveSrcP = GST_BUFFER_DATA(object->field_history[object->history_count-4].buf); - - /* use bottom field and interlace top field */ - if (object->field_history[object->history_count-2].flags == PICTURE_INTERLACED_BOTTOM) { - IsOdd = 1; - - // if we have an odd field we copy an even field and weave an odd field - pCopyDest = GST_BUFFER_DATA(outbuf); - pWeaveDest = pCopyDest + dst_pitch; - } - /* do it vice verca */ - else { - - IsOdd = 0; - // if we have an even field we copy an odd field and weave an even field - pCopyDest = GST_BUFFER_DATA(outbuf) + dst_pitch; - pWeaveDest = GST_BUFFER_DATA(outbuf); - } - - - // copy 1st and last weave lines - Fieldcopy(pWeaveDest, pCopySrc, rowsize, - 1, dst_pitch*2, src_pitch); - Fieldcopy(pWeaveDest+(FldHeight-1)*dst_pitch*2, - pCopySrc+(FldHeight-1)*src_pitch, rowsize, - 1, dst_pitch*2, src_pitch); - -#ifdef USE_VERTICAL_FILTER - // Vertical Filter currently not implemented for DScaler !! - // copy 1st and last lines the copy field - Fieldcopy(pCopyDest, pCopySrc, rowsize, - 1, dst_pitch*2, src_pitch); - Fieldcopy(pCopyDest+(FldHeight-1)*dst_pitch*2, - pCopySrc+(FldHeight-1)*src_pitch, rowsize, - 1, dst_pitch*2, src_pitch); -#else - - // copy all of the copy field - Fieldcopy(pCopyDest, pCopySrc, rowsize, - FldHeight, dst_pitch*2, src_pitch); -#endif - // then go fill in the hard part, being variously lazy depending upon - // SearchEffort - - if(!UseStrangeBob) { - if (SearchEffort == 0) - { - SEFUNC(0); - } - else if (SearchEffort <= 1) - { - SEFUNC(1); - } - /* else if (SearchEffort <= 2) - { - SEFUNC(2); - } - */ - else if (SearchEffort <= 3) - { - SEFUNC(3); - } - else if (SearchEffort <= 5) - { - SEFUNC(5); - } - else if (SearchEffort <= 9) - { - SEFUNC(9); - } - else if (SearchEffort <= 11) - { - SEFUNC(11); - } - else if (SearchEffort <= 13) - { - SEFUNC(13); - } - else if (SearchEffort <= 15) - { - SEFUNC(15); - } - else if (SearchEffort <= 19) - { - SEFUNC(19); - } - else if (SearchEffort <= 21) - { - SEFUNC(21); - } - else - { - SEFUNC(Max); - } - } - else - { - if (SearchEffort == 0) - { - SEFUNC(0SB); - } - else if (SearchEffort <= 1) - { - SEFUNC(1SB); - } - /* else if (SearchEffort <= 2) - { - SEFUNC(2SB); - } - */ - else if (SearchEffort <= 3) - { - SEFUNC(3SB); - } - else if (SearchEffort <= 5) - { - SEFUNC(5SB); - } - else if (SearchEffort <= 9) - { - SEFUNC(9SB); - } - else if (SearchEffort <= 11) - { - SEFUNC(11SB); - } - else if (SearchEffort <= 13) - { - SEFUNC(13SB); - } - else if (SearchEffort <= 15) - { - SEFUNC(15SB); - } - else if (SearchEffort <= 19) - { - SEFUNC(19SB); - } - else if (SearchEffort <= 21) - { - SEFUNC(21SB); - } - else - { - SEFUNC(MaxSB); - } - } - -#if defined(BUILD_X86_ASM) && !defined(IS_C) - __asm__ __volatile__("emms"); -#endif -} diff --git a/gst/deinterlace2/tvtime/tomsmocomp/TomsMoCompAll2.inc b/gst/deinterlace2/tvtime/tomsmocomp/TomsMoCompAll2.inc deleted file mode 100644 index f6344eab..00000000 --- a/gst/deinterlace2/tvtime/tomsmocomp/TomsMoCompAll2.inc +++ /dev/null @@ -1,243 +0,0 @@ -// -*- c++ -*- - -#ifdef SEARCH_EFFORT_FUNC -#undef SEARCH_EFFORT_FUNC -#endif - -#ifdef USE_STRANGE_BOB -#define SEARCH_EFFORT_FUNC(n) SEFUNC(n##SB) -#else -#define SEARCH_EFFORT_FUNC(n) SEFUNC(n) -#endif - -static inline int SEARCH_EFFORT_FUNC(0) // we don't try at all ;-) -{ - //see Search_Effort_Max() for comments -#define SKIP_SEARCH -#include "SearchLoopTop.inc" -#include "SearchLoopBottom.inc" -#undef SKIP_SEARCH -} - -static inline int SEARCH_EFFORT_FUNC(1) -{ -#ifdef IS_C -#define SKIP_SEARCH -#include "SearchLoopTop.inc" -#include "SearchLoopBottom.inc" -#undef SKIP_SEARCH -#else - //see Search_Effort_Max() for comments -#include "SearchLoopTop.inc" - RESET_CHROMA // pretend chroma diffs was 255 each -#include "SearchLoop0A.inc" -#include "SearchLoopBottom.inc" -#endif -} - -static inline int SEARCH_EFFORT_FUNC(3) -{ -#ifdef IS_C -#define SKIP_SEARCH -#include "SearchLoopTop.inc" -#include "SearchLoopBottom.inc" -#undef SKIP_SEARCH -#else - //see Search_Effort_Max() for comments -#include "SearchLoopTop.inc" -#include "SearchLoopOddA2.inc" - RESET_CHROMA // pretend chroma diffs was 255 each -#include "SearchLoop0A.inc" -#include "SearchLoopBottom.inc" -#endif -} - -static inline int SEARCH_EFFORT_FUNC(5) -{ -#ifdef IS_C -#define SKIP_SEARCH -#include "SearchLoopTop.inc" -#include "SearchLoopBottom.inc" -#undef SKIP_SEARCH -#else - //see Search_Effort_Max() for comments -#include "SearchLoopTop.inc" -#include "SearchLoopOddA2.inc" -#include "SearchLoopOddAH2.inc" - RESET_CHROMA // pretend chroma diffs was 255 each -#include "SearchLoop0A.inc" -#include "SearchLoopBottom.inc" -#endif -} - -// 3x3 search -static inline int SEARCH_EFFORT_FUNC(9) -{ -#ifdef IS_C -#define SKIP_SEARCH -#include "SearchLoopTop.inc" -#include "SearchLoopBottom.inc" -#undef SKIP_SEARCH -#else - //see SearchEffortMax() for comments -#include "SearchLoopTop.inc" -#include "SearchLoopOddA.inc" - RESET_CHROMA // pretend chroma diffs was 255 each -#include "SearchLoopVA.inc" -#include "SearchLoop0A.inc" -#include "SearchLoopBottom.inc" -#endif -} - -// Search 9 with 2 H-half pels added -static inline int SEARCH_EFFORT_FUNC(11) -{ -#ifdef IS_C -#define SKIP_SEARCH -#include "SearchLoopTop.inc" -#include "SearchLoopBottom.inc" -#undef SKIP_SEARCH -#else - //see SearchEffortMax() for comments -#include "SearchLoopTop.inc" -#include "SearchLoopOddA.inc" -#include "SearchLoopOddAH2.inc" - RESET_CHROMA // pretend chroma diffs was 255 each -#include "SearchLoopVA.inc" -#include "SearchLoop0A.inc" -#include "SearchLoopBottom.inc" -#endif -} - -// Search 11 with 2 V-half pels added -static inline int SEARCH_EFFORT_FUNC(13) -{ -#ifdef IS_C -#define SKIP_SEARCH -#include "SearchLoopTop.inc" -#include "SearchLoopBottom.inc" -#undef SKIP_SEARCH -#else - //see SearchEffortMax() for comments -#include "SearchLoopTop.inc" -#include "SearchLoopOddA.inc" -#include "SearchLoopOddAH2.inc" - RESET_CHROMA // pretend chroma diffs was 255 each -#include "SearchLoopVAH.inc" -#include "SearchLoopVA.inc" -#include "SearchLoop0A.inc" -#include "SearchLoopBottom.inc" -#endif -} - -// 5x3 -static inline int SEARCH_EFFORT_FUNC(15) -{ -#ifdef IS_C -#define SKIP_SEARCH -#include "SearchLoopTop.inc" -#include "SearchLoopBottom.inc" -#undef SKIP_SEARCH -#else - //see SearchEffortMax() for comments -#include "SearchLoopTop.inc" -#include "SearchLoopOddA.inc" - RESET_CHROMA // pretend chroma diffs was 255 each -#include "SearchLoopEdgeA.inc" -#include "SearchLoopVA.inc" -#include "SearchLoop0A.inc" -#include "SearchLoopBottom.inc" -#endif -} - -// 5x3 + 4 half pels -static inline int SEARCH_EFFORT_FUNC(19) -{ -#ifdef IS_C -#define SKIP_SEARCH -#include "SearchLoopTop.inc" -#include "SearchLoopBottom.inc" -#undef SKIP_SEARCH -#else - //see SearchEffortMax() for comments -#include "SearchLoopTop.inc" -#include "SearchLoopOddA.inc" -#include "SearchLoopOddAH2.inc" - RESET_CHROMA // pretend chroma diffs was 255 each -#include "SearchLoopEdgeA.inc" -#include "SearchLoopVAH.inc" -#include "SearchLoopVA.inc" -#include "SearchLoop0A.inc" -#include "SearchLoopBottom.inc" -#endif -} - -// Handle one 4x1 block of pixels -// Search a 7x3 area, no half pels - -static inline int SEARCH_EFFORT_FUNC(21) -{ -#ifdef IS_C -#define SKIP_SEARCH -#include "SearchLoopTop.inc" -#include "SearchLoopBottom.inc" -#undef SKIP_SEARCH -#else - //see SearchLoopTop.inc for comments -#include "SearchLoopTop.inc" - - // odd addresses -- the pixels at odd address wouldn't generate - // good luma values but we will mask those off - -#include "SearchLoopOddA6.inc" // 4 odd v half pels, 3 to left & right -#include "SearchLoopOddA.inc" // 6 odd pels, 1 to left & right - - RESET_CHROMA // pretend chroma diffs was 255 each - - // even addresses -- use both luma and chroma from these - // search averages of 2 pixels left and right -#include "SearchLoopEdgeA.inc" - // search vertical line and averages, -1,0,+1 -#include "SearchLoopVA.inc" - // blend our results and loop -#include "SearchLoop0A.inc" -#include "SearchLoopBottom.inc" -#endif -} - -// Handle one 4x1 block of pixels -// Search a 9x3 area, no half pels -static inline int SEARCH_EFFORT_FUNC(Max) -{ -#ifdef IS_C -#define SKIP_SEARCH -#include "SearchLoopTop.inc" -#include "SearchLoopBottom.inc" -#undef SKIP_SEARCH -#else - //see SearchLoopTop.inc for comments -#include "SearchLoopTop.inc" - - // odd addresses -- the pixels at odd address wouldn't generate - // good luma values but we will mask those off - -#include "SearchLoopOddA6.inc" // 4 odd v half pels, 3 to left & right -#include "SearchLoopOddA.inc" // 6 odd pels, 1 to left & right - - RESET_CHROMA // pretend chroma diffs was 255 each - - // even addresses -- use both luma and chroma from these - // search averages of 4 pixels left and right -#include "SearchLoopEdgeA8.inc" - // search averages of 2 pixels left and right -#include "SearchLoopEdgeA.inc" - // search vertical line and averages, -1,0,+1 -#include "SearchLoopVA.inc" - // blend our results and loop -#include "SearchLoop0A.inc" -#include "SearchLoopBottom.inc" -#endif -} - -#undef SEARCH_EFFORT_FUNC - diff --git a/gst/deinterlace2/tvtime/tomsmocomp/WierdBob.inc b/gst/deinterlace2/tvtime/tomsmocomp/WierdBob.inc deleted file mode 100644 index f4bbb830..00000000 --- a/gst/deinterlace2/tvtime/tomsmocomp/WierdBob.inc +++ /dev/null @@ -1,286 +0,0 @@ -// -*- c++ -*- - - // First, get and save our possible Bob values - // Assume our pixels are layed out as follows with x the calc'd bob value - // and the other pixels are from the current field - // - // j a b c k current field - // x calculated line - // m d e f n current field - // - // we calc the bob value as: - // x2 = either avg(a,f), avg(c,d), avg(b,e), avg(j,n), or avg(k,m) - - // selected for the smallest of abs(a,f), abs(c,d), or abs(b,e), etc. - -#ifndef IS_C - // a,f - "movq -2(%%"XBX"), %%mm0\n\t" // value a from top left - "movq 2(%%"XBX", %%"XCX"), %%mm1\n\t" // value f from bottom right - "movq %%mm0, %%mm6\n\t" -// pavgb %%mm6, %%mm1 // avg(a,f), also best so far - V_PAVGB ("%%mm6", "%%mm1", "%%mm7", _ShiftMask) // avg(a,f), also best so far - "movq %%mm0, %%mm7\n\t" - "psubusb %%mm1, %%mm7\n\t" - "psubusb %%mm0, %%mm1\n\t" - "por %%mm1, %%mm7\n\t" // abs diff, also best so far - - // c,d - "movq 2(%%"XBX"), %%mm0\n\t" // value a from top left - "movq -2(%%"XBX", %%"XCX"), %%mm1\n\t" // value f from bottom right - "movq %%mm0, %%mm2\n\t" -// pavgb %%mm2, %%mm1 // avg(c,d) - V_PAVGB ("%%mm2", "%%mm1", "%%mm3", _ShiftMask) // avg(c,d) - "movq %%mm0, %%mm3\n\t" - "psubusb %%mm1, %%mm3\n\t" - "psubusb %%mm0, %%mm1\n\t" - "por %%mm1, %%mm3\n\t" // abs(c,d) - "movq %%mm3, %%mm1\n\t" // keep copy - - "psubusb %%mm7, %%mm3\n\t" // nonzero where new weights bigger, else 0 - "pxor %%mm4, %%mm4\n\t" - "pcmpeqb %%mm4, %%mm3\n\t" // now ff where new better, else 00 - "pcmpeqb %%mm3, %%mm4\n\t" // here ff where old better, else 00 - - "pand %%mm3, %%mm1\n\t" // keep only better new avg and abs - "pand %%mm3, %%mm2\n\t" - - "pand %%mm4, %%mm6\n\t" - "pand %%mm4, %%mm7\n\t" - - "por %%mm2, %%mm6\n\t" // and merge new & old vals keeping best - "por %%mm1, %%mm7\n\t" - "por "_UVMask", %%mm7\n\t" // but we know chroma is worthless so far - "pand "_YMask", %%mm5\n\t" // mask out chroma from here also - - // j,n - "movq -4(%%"XBX"), %%mm0\n\t" // value j from top left - "movq 4(%%"XBX", %%"XCX"), %%mm1\n\t" // value n from bottom right - "movq %%mm0, %%mm2\n\t" -// pavgb %%mm2, %%mm1 // avg(j,n) - V_PAVGB ("%%mm2", "%%mm1", "%%mm3", _ShiftMask) // avg(j,n) - "movq %%mm0, %%mm3\n\t" - "psubusb %%mm1, %%mm3\n\t" - "psubusb %%mm0, %%mm1\n\t" - "por %%mm1, %%mm3\n\t" // abs(j-n) - "movq %%mm3, %%mm1\n\t" // keep copy - - "psubusb %%mm7, %%mm3\n\t" // nonzero where new weights bigger, else 0 - "pxor %%mm4, %%mm4\n\t" - "pcmpeqb %%mm4, %%mm3\n\t" // now ff where new better, else 00 - "pcmpeqb %%mm3, %%mm4\n\t" // here ff where old better, else 00 - - "pand %%mm3, %%mm1\n\t" // keep only better new avg and abs - "pand %%mm2, %%mm3\n\t" - - "pand %%mm4, %%mm6\n\t" - "pand %%mm4, %%mm7\n\t" - - "por %%mm3, %%mm6\n\t" // and merge new & old vals keeping best - "por %%mm1, %%mm7\n\t" // " - - // k, m - "movq 4(%%"XBX"), %%mm0\n\t" // value k from top right - "movq -4(%%"XBX", %%"XCX"), %%mm1\n\t" // value n from bottom left - "movq %%mm0, %%mm4\n\t" -// pavgb %%mm4, %%mm1 // avg(k,m) - V_PAVGB ("%%mm4", "%%mm1", "%%mm3", _ShiftMask) // avg(k,m) - - "movq %%mm0, %%mm3\n\t" - "psubusb %%mm1, %%mm3\n\t" - "psubusb %%mm0, %%mm1\n\t" - "por %%mm1, %%mm3\n\t" // abs(k,m) - "movq %%mm3, %%mm1\n\t" // keep copy - - "movq %%mm4, %%mm2\n\t" // avg(k,m) - - "psubusb %%mm7, %%mm3\n\t" // nonzero where new weights bigger, else 0 - "pxor %%mm4, %%mm4\n\t" - "pcmpeqb %%mm4, %%mm3\n\t" // now ff where new better, else 00 - "pcmpeqb %%mm3, %%mm4\n\t" // here ff where old better, else 00 - - "pand %%mm3, %%mm1\n\t" // keep only better new avg and abs - "pand %%mm2, %%mm3\n\t" - - "pand %%mm4, %%mm6\n\t" - "pand %%mm4, %%mm7\n\t" - - "por %%mm3, %%mm6\n\t" // and merge new & old vals keeping best - "por %%mm1, %%mm7\n\t" // " - - // b,e - "movq (%%"XBX"), %%mm0\n\t" // value b from top - "movq (%%"XBX", %%"XCX"), %%mm1\n\t" // value e from bottom - -// We will also calc here the max/min values to later limit comb -// so the max excursion will not exceed the Max_Comb constant - -#ifdef SKIP_SEARCH - "movq %%mm0, %%mm2\n\t" -// pminub %%mm2, %%mm1 - V_PMINUB ("%%mm2", "%%mm1", "%%mm4") - -// pmaxub %%mm6, %%mm2 // clip our current results so far to be above this - V_PMAXUB ("%%mm6", "%%mm2") - "movq %%mm0, %%mm2\n\t" - V_PMAXUB ("%%mm2", "%%mm1") -// pminub %%mm6, %%mm2 // clip our current results so far to be below this - V_PMINUB ("%%mm6", "%%mm2", "%%mm4") - -#else - "movq %%mm0, %%mm2\n\t" - "movq (%%"XAX"), %%mm4\n\t" - "psubusb %%mm4, %%mm2\n\t" - "psubusb %%mm0, %%mm4\n\t" - "por %%mm2, %%mm4\n\t" // abs diff - - "movq %%mm1, %%mm2\n\t" - "movq (%%"XAX", %%"XCX"), %%mm3\n\t" - "psubusb %%mm3, %%mm2\n\t" - "psubusb %%mm1, %%mm3\n\t" - "por %%mm2, %%mm3\n\t" // abs diff -// pmaxub %%mm3, %%mm4 // top or bottom pixel moved most - V_PMAXUB ("%%mm3", "%%mm4") // top or bottom pixel moved most - "psubusb "_Max_Mov", %%mm3\n\t" // moved more than allowed? or goes to 0? - "pxor %%mm4, %%mm4\n\t" - "pcmpeqb %%mm4, %%mm3\n\t" // now ff where low motion, else high motion - - "movq %%mm0, %%mm2\n\t" -// pminub %%mm2, %%mm1 - V_PMINUB ("%%mm2", "%%mm1", "%%mm4") - -// pmaxub %%mm6, %%mm2 // clip our current results so far to be above this - V_PMAXUB ("%%mm6", "%%mm2") - - "psubusb %%mm3, %%mm2\n\t" // maybe decrease it to 0000.. if no surround motion - "movq %%mm2, "_Min_Vals"\n\t" - - "movq %%mm0, %%mm2\n\t" - V_PMAXUB ("%%mm2", "%%mm1") -// pminub %%mm6, %%mm2 // clip our current results so far to be below this - V_PMINUB ("%%mm6", "%%mm2", "%%mm4") - "paddusb %%mm3, %%mm2\n\t" // maybe increase it to ffffff if no surround motion - "movq %%mm2, "_Max_Vals"\n\t" -#endif - - "movq %%mm0, %%mm2\n\t" -// pavgb %%mm2, %%mm1 // avg(b,e) - V_PAVGB ("%%mm2", "%%mm1", "%%mm3", _ShiftMask) // avg(b,e) - - "movq %%mm0, %%mm3\n\t" - "psubusb %%mm1, %%mm3\n\t" - "psubusb %%mm0, %%mm1\n\t" - "por %%mm1, %%mm3\n\t" // abs(c,d) - "movq %%mm3, %%mm1\n\t" // keep copy of diffs - - "pxor %%mm4, %%mm4\n\t" - "psubusb %%mm7, %%mm3\n\t" // nonzero where new weights bigger, else 0 - "pcmpeqb %%mm4, %%mm3\n\t" // now ff where new better, else 00 - - "pcmpeqb %%mm3, %%mm4\n\t" // here ff where old better, else 00 - - "pand %%mm3, %%mm1\n\t" - "pand %%mm3, %%mm2\n\t" - - "pand %%mm4, %%mm6\n\t" - "pand %%mm4, %%mm7\n\t" - - "por %%mm2, %%mm6\n\t" // our x2 value - "por %%mm1, %%mm7\n\t" // our x2 diffs - "movq %%mm7, %%mm4\n\t" // save as bob uncertainty indicator - -#else - - // a,f - best[0] = (pBob[-2] + pBob[src_pitch2 + 2]) / 2; - diff[0] = ABS (pBob[-2] - pBob[src_pitch2 + 2]); - best[1] = (pBob[-1] + pBob[src_pitch2 + 3]) / 2; - diff[1] = ABS (pBob[-1] - pBob[src_pitch2 + 3]); - - // c,d - if (ABS (pBob[2] - pBob[src_pitch2 - 2]) < diff[0]) { - best[0] = (pBob[2] + pBob[src_pitch2 - 2]) / 2; - diff[0] = ABS (pBob[2] - pBob[src_pitch2 - 2]); - } - - if (ABS (pBob[3] - pBob[src_pitch2 - 1]) < diff[1]) { - best[1] = (pBob[3] + pBob[src_pitch2 - 1]) / 2; - diff[1] = ABS (pBob[3] - pBob[src_pitch2 - 1]); - } - - // j,n - if (ABS (pBob[-4] - pBob[src_pitch2 + 4]) < diff[0]) { - best[0] = (pBob[-4] + pBob[src_pitch2 + 4]) / 2; - diff[0] = ABS (pBob[-4] - pBob[src_pitch2 + 4]); - } - - if (ABS (pBob[-3] - pBob[src_pitch2 + 5]) < diff[1]) { - best[1] = (pBob[-3] + pBob[src_pitch2 + 5]) / 2; - diff[1] = ABS (pBob[-3] - pBob[src_pitch2 + 5]); - } - - // k,m - if (ABS (pBob[4] - pBob[src_pitch2 - 4]) < diff[0]) { - best[0] = (pBob[4] + pBob[src_pitch2 - 4]) / 2; - diff[0] = ABS (pBob[-4] - pBob[src_pitch2 - 4]); - } - - if (ABS (pBob[5] - pBob[src_pitch2 - 3]) < diff[1]) { - best[1] = (pBob[5] + pBob[src_pitch2 - 3]) / 2; - diff[1] = ABS (pBob[-3] - pBob[src_pitch2 - 3]); - } - // k,m - if (ABS (pBob[4] - pBob[src_pitch2 - 4]) < diff[0]) { - best[0] = (pBob[4] + pBob[src_pitch2 - 4]) / 2; - diff[0] = ABS (pBob[-4] - pBob[src_pitch2 - 4]); - } - - if (ABS (pBob[5] - pBob[src_pitch2 - 3]) < diff[1]) { - best[1] = (pBob[5] + pBob[src_pitch2 - 3]) / 2; - diff[1] = ABS (pBob[-3] - pBob[src_pitch2 - 3]); - } - -// We will also calc here the max/min values to later limit comb -// so the max excursion will not exceed the Max_Comb constant - -#ifdef SKIP_SEARCH - best[0] = CLAMP (best[0], MIN (pBob[src_pitch2], pBob[0]), MAX (pBob[src_pitch2], pBob[0])); - best[1] = CLAMP (best[1], MIN (pBob[src_pitch2 + 1], pBob[1]), MAX (pBob[src_pitch2 + 1], pBob[1])); -#else - mov[0] = MAX (ABS (pBob[0] - pBobP[0]), ABS (pBob[src_pitch2] - pBobP[src_pitch2])); - mov[1] = MAX (ABS (pBob[1] - pBobP[1]), ABS (pBob[src_pitch2 + 1] - pBobP[src_pitch2 + 1])); - - MinVals[0] = 0; - MinVals[1] = 0; - MaxVals[0] = 255; - MaxVals[1] = 255; - - if (mov[0] > Max_Mov[0]) { - MinVals[0] = MAX (MIN (pBob[0], pBob[src_pitch2]), best[0]); - MaxVals[0] = MIN (MAX (pBob[0], pBob[src_pitch2]), best[0]); - } - - if (mov[1] > Max_Mov[1]) { - MinVals[1] = MAX (MIN (pBob[1], pBob[src_pitch2 + 1]), best[1]); - MaxVals[1] = MIN (MAX (pBob[1], pBob[src_pitch2 + 1]), best[1]); - } - - best[0] = CLAMP (best[0], MIN (pBob[src_pitch2], pBob[0]), MAX (pBob[src_pitch2], pBob[0])); - best[1] = CLAMP (best[1], MIN (pBob[src_pitch2 + 1], pBob[1]), MAX (pBob[src_pitch2 + 1], pBob[1])); -#endif - - avg[0] = (pBob[src_pitch2] + pBob[0]) / 2; - avg[1] = (pBob[src_pitch2 + 1] + pBob[1]) / 2; - diff2[0] = ABS (pBob[src_pitch2] - pBob[0]); - diff2[1] = ABS (pBob[src_pitch2 + 1] - pBob[1]); - - if (diff2[0] < diff[0]) { - best[0] = avg[0]; - diff[0] = diff2[0]; - } - - if (diff2[1] < diff[1]) { - best[1] = avg[1]; - diff[1] = diff2[1]; - } -#endif diff --git a/gst/deinterlace2/tvtime/tomsmocomp/tomsmocompmacros.h b/gst/deinterlace2/tvtime/tomsmocomp/tomsmocompmacros.h deleted file mode 100644 index 7e8147ec..00000000 --- a/gst/deinterlace2/tvtime/tomsmocomp/tomsmocompmacros.h +++ /dev/null @@ -1,164 +0,0 @@ -#include <string.h> -#include <math.h> - -// Define a few macros for CPU dependent instructions. -// I suspect I don't really understand how the C macro preprocessor works but -// this seems to get the job done. // TRB 7/01 - -// BEFORE USING THESE YOU MUST SET: - -// #define SIMD_TYPE MMXEXT (or MMX or 3DNOW) - -// some macros for pavgb instruction -// V_PAVGB(mmr1, mmr2, mmr work register, smask) mmr2 may = mmrw if you can trash it - -#define V_PAVGB_MMX(mmr1, mmr2, mmrw, smask) \ - "movq "mmr2", "mmrw"\n\t" \ - "pand "smask", "mmrw"\n\t" \ - "psrlw $1, "mmrw"\n\t" \ - "pand "smask", "mmr1"\n\t" \ - "psrlw $1, "mmr1"\n\t" \ - "paddusb "mmrw", "mmr1"\n\t" -#define V_PAVGB_MMXEXT(mmr1, mmr2, mmrw, smask) "pavgb "mmr2", "mmr1"\n\t" -#define V_PAVGB_3DNOW(mmr1, mmr2, mmrw, smask) "pavgusb "mmr2", "mmr1"\n\t" -#define V_PAVGB(mmr1, mmr2, mmrw, smask) V_PAVGB2(mmr1, mmr2, mmrw, smask, SIMD_TYPE) -#define V_PAVGB2(mmr1, mmr2, mmrw, smask, simd_type) V_PAVGB3(mmr1, mmr2, mmrw, smask, simd_type) -#define V_PAVGB3(mmr1, mmr2, mmrw, smask, simd_type) V_PAVGB_##simd_type(mmr1, mmr2, mmrw, smask) - -// some macros for pmaxub instruction -#define V_PMAXUB_MMX(mmr1, mmr2) \ - "psubusb "mmr2", "mmr1"\n\t" \ - "paddusb "mmr2", "mmr1"\n\t" -#define V_PMAXUB_MMXEXT(mmr1, mmr2) "pmaxub "mmr2", "mmr1"\n\t" -#define V_PMAXUB_3DNOW(mmr1, mmr2) V_PMAXUB_MMX(mmr1, mmr2) // use MMX version -#define V_PMAXUB(mmr1, mmr2) V_PMAXUB2(mmr1, mmr2, SIMD_TYPE) -#define V_PMAXUB2(mmr1, mmr2, simd_type) V_PMAXUB3(mmr1, mmr2, simd_type) -#define V_PMAXUB3(mmr1, mmr2, simd_type) V_PMAXUB_##simd_type(mmr1, mmr2) - -// some macros for pminub instruction -// V_PMINUB(mmr1, mmr2, mmr work register) mmr2 may NOT = mmrw -#define V_PMINUB_MMX(mmr1, mmr2, mmrw) \ - "pcmpeqb "mmrw", "mmrw"\n\t" \ - "psubusb "mmr2", "mmrw"\n\t" \ - "paddusb "mmrw", "mmr1"\n\t" \ - "psubusb "mmrw", "mmr1"\n\t" -#define V_PMINUB_MMXEXT(mmr1, mmr2, mmrw) "pminub "mmr2", "mmr1"\n\t" -#define V_PMINUB_3DNOW(mmr1, mmr2, mmrw) V_PMINUB_MMX(mmr1, mmr2, mmrw) // use MMX version -#define V_PMINUB(mmr1, mmr2, mmrw) V_PMINUB2(mmr1, mmr2, mmrw, SIMD_TYPE) -#define V_PMINUB2(mmr1, mmr2, mmrw, simd_type) V_PMINUB3(mmr1, mmr2, mmrw, simd_type) -#define V_PMINUB3(mmr1, mmr2, mmrw, simd_type) V_PMINUB_##simd_type(mmr1, mmr2, mmrw) - -// some macros for movntq instruction -// V_MOVNTQ(mmr1, mmr2) -#define V_MOVNTQ_MMX(mmr1, mmr2) "movq "mmr2", "mmr1"\n\t" -#define V_MOVNTQ_3DNOW(mmr1, mmr2) "movq "mmr2", "mmr1"\n\t" -#define V_MOVNTQ_MMXEXT(mmr1, mmr2) "movntq "mmr2", "mmr1"\n\t" -#define V_MOVNTQ(mmr1, mmr2) V_MOVNTQ2(mmr1, mmr2, SIMD_TYPE) -#define V_MOVNTQ2(mmr1, mmr2, simd_type) V_MOVNTQ3(mmr1, mmr2, simd_type) -#define V_MOVNTQ3(mmr1, mmr2, simd_type) V_MOVNTQ_##simd_type(mmr1, mmr2) - -// end of macros - -#ifdef IS_SSE2 - -#define MERGE4PIXavg(PADDR1, PADDR2) \ - "movdqu "PADDR1", %%xmm0\n\t" /* our 4 pixels */ \ - "movdqu "PADDR2", %%xmm1\n\t" /* our pixel2 value */ \ - "movdqa %%xmm0, %%xmm2\n\t" /* another copy of our pixel1 value */ \ - "movdqa %%xmm1, %%xmm3\n\t" /* another copy of our pixel1 value */ \ - "psubusb %%xmm1, %%xmm2\n\t" \ - "psubusb %%xmm0, %%xmm3\n\t" \ - "por %%xmm3, %%xmm2\n\t" \ - "pavgb %%xmm1, %%xmm0\n\t" /* avg of 2 pixels */ \ - "movdqa %%xmm2, %%xmm3\n\t" /* another copy of our our weights */ \ - "pxor %%xmm1, %%xmm1\n\t" \ - "psubusb %%xmm7, %%xmm3\n\t" /* nonzero where old weights lower, else 0 */ \ - "pcmpeqb %%xmm1, %%xmm3\n\t" /* now ff where new better, else 00 */ \ - "pcmpeqb %%xmm3, %%xmm1\n\t" /* here ff where old better, else 00 */ \ - "pand %%xmm3, %%xmm0\n\t" /* keep only better new pixels */ \ - "pand %%xmm3, %%xmm2\n\t" /* and weights */ \ - "pand %%xmm1, %%xmm5\n\t" /* keep only better old pixels */ \ - "pand %%xmm1, %%xmm7\n\t" \ - "por %%xmm0, %%xmm5\n\t" /* and merge new & old vals */ \ - "por %%xmm2, %%xmm7\n\t" - -#define MERGE4PIXavgH(PADDR1A, PADDR1B, PADDR2A, PADDR2B) \ - "movdqu "PADDR1A", %%xmm0\n\t" /* our 4 pixels */ \ - "movdqu "PADDR2A", %%xmm1\n\t" /* our pixel2 value */ \ - "movdqu "PADDR1B", %%xmm2\n\t" /* our 4 pixels */ \ - "movdqu "PADDR2B", %%xmm3\n\t" /* our pixel2 value */ \ - "pavgb %%xmm2, %%xmm0\n\t" \ - "pavgb %%xmm3, %%xmm1\n\t" \ - "movdqa %%xmm0, %%xmm2\n\t" /* another copy of our pixel1 value */ \ - "movdqa %%xmm1, %%xmm3\n\t" /* another copy of our pixel1 value */ \ - "psubusb %%xmm1, %%xmm2\n\t" \ - "psubusb %%xmm0, %%xmm3\n\t" \ - "por %%xmm3, %%xmm2\n\t" \ - "pavgb %%xmm1, %%xmm0\n\t" /* avg of 2 pixels */ \ - "movdqa %%xmm2, %%xmm3\n\t" /* another copy of our our weights */ \ - "pxor %%xmm1, %%xmm1\n\t" \ - "psubusb %%xmm7, %%xmm3\n\t" /* nonzero where old weights lower, else 0 */ \ - "pcmpeqb %%xmm1, %%xmm3\n\t" /* now ff where new better, else 00 */ \ - "pcmpeqb %%xmm3, %%xmm1\n\t" /* here ff where old better, else 00 */ \ - "pand %%xmm3, %%xmm0\n\t" /* keep only better new pixels */ \ - "pand %%xmm3, %%xmm2\n\t" /* and weights */ \ - "pand %%xmm1, %%xmm5\n\t" /* keep only better old pixels */ \ - "pand %%xmm1, %%xmm7\n\t" \ - "por %%xmm0, %%xmm5\n\t" /* and merge new & old vals */ \ - "por %%xmm2, %%xmm7\n\t" - -#define RESET_CHROMA "por "_UVMask", %%xmm7\n\t" - -#else // ifdef IS_SSE2 - -#define MERGE4PIXavg(PADDR1, PADDR2) \ - "movq "PADDR1", %%mm0\n\t" /* our 4 pixels */ \ - "movq "PADDR2", %%mm1\n\t" /* our pixel2 value */ \ - "movq %%mm0, %%mm2\n\t" /* another copy of our pixel1 value */ \ - "movq %%mm1, %%mm3\n\t" /* another copy of our pixel1 value */ \ - "psubusb %%mm1, %%mm2\n\t" \ - "psubusb %%mm0, %%mm3\n\t" \ - "por %%mm3, %%mm2\n\t" \ - V_PAVGB ("%%mm0", "%%mm1", "%%mm3", _ShiftMask) /* avg of 2 pixels */ \ - "movq %%mm2, %%mm3\n\t" /* another copy of our our weights */ \ - "pxor %%mm1, %%mm1\n\t" \ - "psubusb %%mm7, %%mm3\n\t" /* nonzero where old weights lower, else 0 */ \ - "pcmpeqb %%mm1, %%mm3\n\t" /* now ff where new better, else 00 */ \ - "pcmpeqb %%mm3, %%mm1\n\t" /* here ff where old better, else 00 */ \ - "pand %%mm3, %%mm0\n\t" /* keep only better new pixels */ \ - "pand %%mm3, %%mm2\n\t" /* and weights */ \ - "pand %%mm1, %%mm5\n\t" /* keep only better old pixels */ \ - "pand %%mm1, %%mm7\n\t" \ - "por %%mm0, %%mm5\n\t" /* and merge new & old vals */ \ - "por %%mm2, %%mm7\n\t" - -#define MERGE4PIXavgH(PADDR1A, PADDR1B, PADDR2A, PADDR2B) \ - "movq "PADDR1A", %%mm0\n\t" /* our 4 pixels */ \ - "movq "PADDR2A", %%mm1\n\t" /* our pixel2 value */ \ - "movq "PADDR1B", %%mm2\n\t" /* our 4 pixels */ \ - "movq "PADDR2B", %%mm3\n\t" /* our pixel2 value */ \ - V_PAVGB("%%mm0", "%%mm2", "%%mm2", _ShiftMask) \ - V_PAVGB("%%mm1", "%%mm3", "%%mm3", _ShiftMask) \ - "movq %%mm0, %%mm2\n\t" /* another copy of our pixel1 value */ \ - "movq %%mm1, %%mm3\n\t" /* another copy of our pixel1 value */ \ - "psubusb %%mm1, %%mm2\n\t" \ - "psubusb %%mm0, %%mm3\n\t" \ - "por %%mm3, %%mm2\n\t" \ - V_PAVGB("%%mm0", "%%mm1", "%%mm3", _ShiftMask) /* avg of 2 pixels */ \ - "movq %%mm2, %%mm3\n\t" /* another copy of our our weights */ \ - "pxor %%mm1, %%mm1\n\t" \ - "psubusb %%mm7, %%mm3\n\t" /* nonzero where old weights lower, else 0 */ \ - "pcmpeqb %%mm1, %%mm3\n\t" /* now ff where new better, else 00 */ \ - "pcmpeqb %%mm3, %%mm1\n\t" /* here ff where old better, else 00 */ \ - "pand %%mm3, %%mm0\n\t" /* keep only better new pixels */ \ - "pand %%mm3, %%mm2\n\t" /* and weights */ \ - "pand %%mm1, %%mm5\n\t" /* keep only better old pixels */ \ - "pand %%mm1, %%mm7\n\t" \ - "por %%mm0, %%mm5\n\t" /* and merge new & old vals */ \ - "por %%mm2, %%mm7\n\t" - -#define RESET_CHROMA "por "_UVMask", %%mm7\n\t" - -#endif - - diff --git a/gst/deinterlace2/tvtime/vfir.c b/gst/deinterlace2/tvtime/vfir.c deleted file mode 100644 index 56950459..00000000 --- a/gst/deinterlace2/tvtime/vfir.c +++ /dev/null @@ -1,187 +0,0 @@ -/* - * - * GStreamer - * Copyright (C) 2004 Billy Biggs <vektor@dumbterm.net> - * Copyright (c) 2001, 2002, 2003 Fabrice Bellard. - * Copyright (C) 2008 Sebastian Dröge <slomo@collabora.co.uk> - * - * 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. - */ - -/* - * This file contains code from ffmpeg, see http://ffmpeg.org/ (LGPL) - * and modifications by Billy Biggs. - * - * Relicensed for GStreamer from GPL to LGPL with permit from Billy Biggs. - * See: http://bugzilla.gnome.org/show_bug.cgi?id=163578 - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "_stdint.h" -#include "gstdeinterlace2.h" -#include <string.h> - -#define GST_TYPE_DEINTERLACE_METHOD_VFIR (gst_deinterlace_method_vfir_get_type ()) -#define GST_IS_DEINTERLACE_METHOD_VFIR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_DEINTERLACE_METHOD_VFIR)) -#define GST_IS_DEINTERLACE_METHOD_VFIR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_DEINTERLACE_METHOD_VFIR)) -#define GST_DEINTERLACE_METHOD_VFIR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_DEINTERLACE_METHOD_VFIR, GstDeinterlaceMethodVFIRClass)) -#define GST_DEINTERLACE_METHOD_VFIR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_DEINTERLACE_METHOD_VFIR, GstDeinterlaceMethodVFIR)) -#define GST_DEINTERLACE_METHOD_VFIR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEINTERLACE_METHOD_VFIR, GstDeinterlaceMethodVFIRClass)) -#define GST_DEINTERLACE_METHOD_VFIR_CAST(obj) ((GstDeinterlaceMethodVFIR*)(obj)) - -GType gst_deinterlace_method_vfir_get_type (void); - -typedef GstDeinterlaceSimpleMethod GstDeinterlaceMethodVFIR; - -typedef GstDeinterlaceSimpleMethodClass GstDeinterlaceMethodVFIRClass; - -/* - * The MPEG2 spec uses a slightly harsher filter, they specify - * [-1 8 2 8 -1]. ffmpeg uses a similar filter but with more of - * a tendancy to blur than to use the local information. The - * filter taps here are: [-1 4 2 4 -1]. - */ - -/** - * C implementation. - */ -static inline void -deinterlace_line_c (GstDeinterlaceMethod * self, GstDeinterlace2 * parent, - guint8 * dst, GstDeinterlaceScanlineData * scanlines, gint width) -{ - gint sum; - guint8 *lum_m4 = scanlines->tt1; - guint8 *lum_m3 = scanlines->t0; - guint8 *lum_m2 = scanlines->m1; - guint8 *lum_m1 = scanlines->b0; - guint8 *lum = scanlines->bb1; - gint size = width * 2; - - for (; size >= 0; size--) { - sum = -lum_m4[0]; - sum += lum_m3[0] << 2; - sum += lum_m2[0] << 1; - sum += lum_m1[0] << 2; - sum += -lum[0]; - dst[0] = (sum + 4) >> 3; // This needs to be clipped at 0 and 255: cm[(sum + 4) >> 3]; - lum_m4++; - lum_m3++; - lum_m2++; - lum_m1++; - lum++; - dst++; - } -} - -#ifdef BUILD_X86_ASM -#include "mmx.h" -static void -deinterlace_line_mmx (GstDeinterlaceMethod * self, GstDeinterlace2 * parent, - guint8 * dst, GstDeinterlaceScanlineData * scanlines, gint width) -{ - mmx_t rounder; - guint8 *lum_m4 = scanlines->tt1; - guint8 *lum_m3 = scanlines->t0; - guint8 *lum_m2 = scanlines->m1; - guint8 *lum_m1 = scanlines->b0; - guint8 *lum = scanlines->bb1; - - rounder.uw[0] = 4; - rounder.uw[1] = 4; - rounder.uw[2] = 4; - rounder.uw[3] = 4; - pxor_r2r (mm7, mm7); - movq_m2r (rounder, mm6); - - for (; width > 1; width -= 2) { - movd_m2r (*lum_m4, mm0); - movd_m2r (*lum_m3, mm1); - movd_m2r (*lum_m2, mm2); - movd_m2r (*lum_m1, mm3); - movd_m2r (*lum, mm4); - punpcklbw_r2r (mm7, mm0); - punpcklbw_r2r (mm7, mm1); - punpcklbw_r2r (mm7, mm2); - punpcklbw_r2r (mm7, mm3); - punpcklbw_r2r (mm7, mm4); - paddw_r2r (mm3, mm1); - psllw_i2r (1, mm2); - paddw_r2r (mm4, mm0); - psllw_i2r (2, mm1); // 2 - paddw_r2r (mm6, mm2); - paddw_r2r (mm2, mm1); - psubusw_r2r (mm0, mm1); - psrlw_i2r (3, mm1); // 3 - packuswb_r2r (mm7, mm1); - movd_r2m (mm1, *dst); - lum_m4 += 4; - lum_m3 += 4; - lum_m2 += 4; - lum_m1 += 4; - lum += 4; - dst += 4; - } - emms (); - - /* Handle odd widths */ - if (width > 0) { - scanlines->tt1 = lum_m4; - scanlines->t0 = lum_m3; - scanlines->m1 = lum_m2; - scanlines->b0 = lum_m1; - scanlines->bb1 = lum; - - deinterlace_line_c (self, parent, dst, scanlines, width); - } -} -#endif - -G_DEFINE_TYPE (GstDeinterlaceMethodVFIR, gst_deinterlace_method_vfir, - GST_TYPE_DEINTERLACE_SIMPLE_METHOD); - -static void -gst_deinterlace_method_vfir_class_init (GstDeinterlaceMethodVFIRClass * klass) -{ - GstDeinterlaceMethodClass *dim_class = (GstDeinterlaceMethodClass *) klass; - GstDeinterlaceSimpleMethodClass *dism_class = - (GstDeinterlaceSimpleMethodClass *) klass; -#ifdef BUILD_X86_ASM - guint cpu_flags = oil_cpu_get_flags (); -#endif - - dim_class->fields_required = 2; - dim_class->name = "Blur Vertical"; - dim_class->nick = "vfir"; - dim_class->latency = 0; - -#ifdef BUILD_X86_ASM - if (cpu_flags & OIL_IMPL_FLAG_MMX) { - dism_class->interpolate_scanline = deinterlace_line_mmx; - } else { - dism_class->interpolate_scanline = deinterlace_line_c; - } -#else - dism_class->interpolate_scanline = deinterlace_line_c; -#endif -} - -static void -gst_deinterlace_method_vfir_init (GstDeinterlaceMethodVFIR * self) -{ -} diff --git a/gst/deinterlace2/tvtime/weave.c b/gst/deinterlace2/tvtime/weave.c deleted file mode 100644 index 09732a3a..00000000 --- a/gst/deinterlace2/tvtime/weave.c +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Weave frames - * Copyright (C) 2002 Billy Biggs <vektor@dumbterm.net>. - * Copyright (C) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser 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 "_stdint.h" -#include "gstdeinterlace2.h" -#include <string.h> - -#define GST_TYPE_DEINTERLACE_METHOD_WEAVE (gst_deinterlace_method_weave_get_type ()) -#define GST_IS_DEINTERLACE_METHOD_WEAVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_DEINTERLACE_METHOD_WEAVE)) -#define GST_IS_DEINTERLACE_METHOD_WEAVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_DEINTERLACE_METHOD_WEAVE)) -#define GST_DEINTERLACE_METHOD_WEAVE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_DEINTERLACE_METHOD_WEAVE, GstDeinterlaceMethodWeaveClass)) -#define GST_DEINTERLACE_METHOD_WEAVE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_DEINTERLACE_METHOD_WEAVE, GstDeinterlaceMethodWeave)) -#define GST_DEINTERLACE_METHOD_WEAVE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEINTERLACE_METHOD_WEAVE, GstDeinterlaceMethodWeaveClass)) -#define GST_DEINTERLACE_METHOD_WEAVE_CAST(obj) ((GstDeinterlaceMethodWeave*)(obj)) - -GType gst_deinterlace_method_weave_get_type (void); - -typedef GstDeinterlaceSimpleMethod GstDeinterlaceMethodWeave; - -typedef GstDeinterlaceSimpleMethodClass GstDeinterlaceMethodWeaveClass; - - -static void -deinterlace_scanline_weave (GstDeinterlaceMethod * self, - GstDeinterlace2 * parent, guint8 * out, - GstDeinterlaceScanlineData * scanlines, gint width) -{ - oil_memcpy (out, scanlines->m1, parent->row_stride); -} - -static void -copy_scanline (GstDeinterlaceMethod * self, GstDeinterlace2 * parent, - guint8 * out, GstDeinterlaceScanlineData * scanlines, gint width) -{ - oil_memcpy (out, scanlines->m0, parent->row_stride); -} - -G_DEFINE_TYPE (GstDeinterlaceMethodWeave, gst_deinterlace_method_weave, - GST_TYPE_DEINTERLACE_SIMPLE_METHOD); - -static void -gst_deinterlace_method_weave_class_init (GstDeinterlaceMethodWeaveClass * klass) -{ - GstDeinterlaceMethodClass *dim_class = (GstDeinterlaceMethodClass *) klass; - GstDeinterlaceSimpleMethodClass *dism_class = - (GstDeinterlaceSimpleMethodClass *) klass; - - dim_class->fields_required = 2; - dim_class->name = "Weave"; - dim_class->nick = "weave"; - dim_class->latency = 0; - - dism_class->interpolate_scanline = deinterlace_scanline_weave; - dism_class->copy_scanline = copy_scanline; -} - -static void -gst_deinterlace_method_weave_init (GstDeinterlaceMethodWeave * self) -{ -} diff --git a/gst/deinterlace2/tvtime/weavebff.c b/gst/deinterlace2/tvtime/weavebff.c deleted file mode 100644 index 4ddf5a51..00000000 --- a/gst/deinterlace2/tvtime/weavebff.c +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Weave frames, bottom-field-first. - * Copyright (C) 2003 Billy Biggs <vektor@dumbterm.net>. - * Copyright (C) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser 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 "_stdint.h" -#include "gstdeinterlace2.h" -#include <string.h> - -#define GST_TYPE_DEINTERLACE_METHOD_WEAVE_BFF (gst_deinterlace_method_weave_bff_get_type ()) -#define GST_IS_DEINTERLACE_METHOD_WEAVE_BFF(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_DEINTERLACE_METHOD_WEAVE_BFF)) -#define GST_IS_DEINTERLACE_METHOD_WEAVE_BFF_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_DEINTERLACE_METHOD_WEAVE_BFF)) -#define GST_DEINTERLACE_METHOD_WEAVE_BFF_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_DEINTERLACE_METHOD_WEAVE_BFF, GstDeinterlaceMethodWeaveBFFClass)) -#define GST_DEINTERLACE_METHOD_WEAVE_BFF(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_DEINTERLACE_METHOD_WEAVE_BFF, GstDeinterlaceMethodWeaveBFF)) -#define GST_DEINTERLACE_METHOD_WEAVE_BFF_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEINTERLACE_METHOD_WEAVE_BFF, GstDeinterlaceMethodWeaveBFFClass)) -#define GST_DEINTERLACE_METHOD_WEAVE_BFF_CAST(obj) ((GstDeinterlaceMethodWeaveBFF*)(obj)) - -GType gst_deinterlace_method_weave_bff_get_type (void); - -typedef GstDeinterlaceSimpleMethod GstDeinterlaceMethodWeaveBFF; - -typedef GstDeinterlaceSimpleMethodClass GstDeinterlaceMethodWeaveBFFClass; - - -static void -deinterlace_scanline_weave (GstDeinterlaceMethod * self, - GstDeinterlace2 * parent, guint8 * out, - GstDeinterlaceScanlineData * scanlines, gint width) -{ - oil_memcpy (out, scanlines->m1, parent->row_stride); -} - -static void -copy_scanline (GstDeinterlaceMethod * self, GstDeinterlace2 * parent, - guint8 * out, GstDeinterlaceScanlineData * scanlines, gint width) -{ - /* FIXME: original code used m2 and m0 but this looks really bad */ - if (scanlines->bottom_field) { - oil_memcpy (out, scanlines->bb2, parent->row_stride); - } else { - oil_memcpy (out, scanlines->bb0, parent->row_stride); - } -} - -G_DEFINE_TYPE (GstDeinterlaceMethodWeaveBFF, gst_deinterlace_method_weave_bff, - GST_TYPE_DEINTERLACE_SIMPLE_METHOD); - -static void -gst_deinterlace_method_weave_bff_class_init (GstDeinterlaceMethodWeaveBFFClass * - klass) -{ - GstDeinterlaceMethodClass *dim_class = (GstDeinterlaceMethodClass *) klass; - GstDeinterlaceSimpleMethodClass *dism_class = - (GstDeinterlaceSimpleMethodClass *) klass; - - dim_class->fields_required = 3; - dim_class->name = "Progressive: Bottom Field First"; - dim_class->nick = "weavebff"; - dim_class->latency = 0; - - dism_class->interpolate_scanline = deinterlace_scanline_weave; - dism_class->copy_scanline = copy_scanline; -} - -static void -gst_deinterlace_method_weave_bff_init (GstDeinterlaceMethodWeaveBFF * self) -{ -} diff --git a/gst/deinterlace2/tvtime/weavetff.c b/gst/deinterlace2/tvtime/weavetff.c deleted file mode 100644 index 9411f51b..00000000 --- a/gst/deinterlace2/tvtime/weavetff.c +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Weave frames, top-field-first. - * Copyright (C) 2003 Billy Biggs <vektor@dumbterm.net>. - * Copyright (C) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser 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 "_stdint.h" -#include "gstdeinterlace2.h" -#include <string.h> - -#define GST_TYPE_DEINTERLACE_METHOD_WEAVE_TFF (gst_deinterlace_method_weave_tff_get_type ()) -#define GST_IS_DEINTERLACE_METHOD_WEAVE_TFF(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_DEINTERLACE_METHOD_WEAVE_TFF)) -#define GST_IS_DEINTERLACE_METHOD_WEAVE_TFF_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_DEINTERLACE_METHOD_WEAVE_TFF)) -#define GST_DEINTERLACE_METHOD_WEAVE_TFF_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_DEINTERLACE_METHOD_WEAVE_TFF, GstDeinterlaceMethodWeaveTFFClass)) -#define GST_DEINTERLACE_METHOD_WEAVE_TFF(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_DEINTERLACE_METHOD_WEAVE_TFF, GstDeinterlaceMethodWeaveTFF)) -#define GST_DEINTERLACE_METHOD_WEAVE_TFF_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEINTERLACE_METHOD_WEAVE_TFF, GstDeinterlaceMethodWeaveTFFClass)) -#define GST_DEINTERLACE_METHOD_WEAVE_TFF_CAST(obj) ((GstDeinterlaceMethodWeaveTFF*)(obj)) - -GType gst_deinterlace_method_weave_tff_get_type (void); - -typedef GstDeinterlaceSimpleMethod GstDeinterlaceMethodWeaveTFF; - -typedef GstDeinterlaceSimpleMethodClass GstDeinterlaceMethodWeaveTFFClass; - - -static void -deinterlace_scanline_weave (GstDeinterlaceMethod * self, - GstDeinterlace2 * parent, guint8 * out, - GstDeinterlaceScanlineData * scanlines, gint width) -{ - oil_memcpy (out, scanlines->m1, parent->row_stride); -} - -static void -copy_scanline (GstDeinterlaceMethod * self, GstDeinterlace2 * parent, - guint8 * out, GstDeinterlaceScanlineData * scanlines, gint width) -{ - /* FIXME: original code used m2 and m0 but this looks really bad */ - if (scanlines->bottom_field) { - oil_memcpy (out, scanlines->bb0, parent->row_stride); - } else { - oil_memcpy (out, scanlines->bb2, parent->row_stride); - } -} - -G_DEFINE_TYPE (GstDeinterlaceMethodWeaveTFF, gst_deinterlace_method_weave_tff, - GST_TYPE_DEINTERLACE_SIMPLE_METHOD); - -static void -gst_deinterlace_method_weave_tff_class_init (GstDeinterlaceMethodWeaveTFFClass * - klass) -{ - GstDeinterlaceMethodClass *dim_class = (GstDeinterlaceMethodClass *) klass; - GstDeinterlaceSimpleMethodClass *dism_class = - (GstDeinterlaceSimpleMethodClass *) klass; - - dim_class->fields_required = 3; - dim_class->name = "Progressive: Top Field First"; - dim_class->nick = "weavetff"; - dim_class->latency = 0; - - dism_class->interpolate_scanline = deinterlace_scanline_weave; - dism_class->copy_scanline = copy_scanline; -} - -static void -gst_deinterlace_method_weave_tff_init (GstDeinterlaceMethodWeaveTFF * self) -{ -} diff --git a/gst/deinterlace2/tvtime/x86-64_macros.inc b/gst/deinterlace2/tvtime/x86-64_macros.inc deleted file mode 100644 index 2e9df758..00000000 --- a/gst/deinterlace2/tvtime/x86-64_macros.inc +++ /dev/null @@ -1,82 +0,0 @@ -/* - * - * GStreamer - * Copyright (C) 2004 Dirk Ziegelmeier <dziegel@gmx.de> - * - * 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. - */ - -/* - * - * See: http://bugzilla.gnome.org/show_bug.cgi?id=163578 - */ - -/* - * This file is copied from TVTIME's sources. - * Original author: Achim Schneider <batchall@mordor.ch> - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifndef XAX - -#if defined (HAVE_CPU_I386) && !defined(HAVE_CPU_X86_64) - -#define XAX "eax" -#define XBX "ebx" -#define XCX "ecx" -#define XDX "edx" -#define XSI "esi" -#define XDI "edi" -#define XSP "esp" -#define MOVX "movl" -#define LEAX "leal" -#define DECX "decl" -#define PUSHX "pushl" -#define POPX "popl" -#define CMPX "cmpl" -#define ADDX "addl" -#define SHLX "shll" -#define SHRX "shrl" -#define SUBX "subl" - -#elif defined (HAVE_CPU_X86_64) - -#define XAX "rax" -#define XBX "rbx" -#define XCX "rcx" -#define XDX "rdx" -#define XSI "rsi" -#define XDI "rdi" -#define XSP "rsp" -#define MOVX "movq" -#define LEAX "leaq" -#define DECX "decq" -#define PUSHX "pushq" -#define POPX "popq" -#define CMPX "cmpq" -#define ADDX "addq" -#define SHLX "shlq" -#define SHRX "shrq" -#define SUBX "subq" - -#else -#error Undefined architecture. Define either ARCH_X86 or ARCH_X86_64. -#endif - -#endif diff --git a/gst/dvdspu/Makefile.am b/gst/dvdspu/Makefile.am index 4d75f76c..07a66357 100644 --- a/gst/dvdspu/Makefile.am +++ b/gst/dvdspu/Makefile.am @@ -1,13 +1,13 @@ plugin_LTLIBRARIES = libgstdvdspu.la -libgstdvdspu_la_SOURCES = gstdvdspu.c gstdvdspu-render.c +libgstdvdspu_la_SOURCES = gstdvdspu.c gstdvdspu-render.c gstspu-vobsub.c gstspu-vobsub-render.c gstspu-pgs.c libgstdvdspu_la_CFLAGS = $(GST_CFLAGS) libgstdvdspu_la_LIBADD = $(GST_LIBS) libgstdvdspu_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstdvdspu_la_LIBTOOLFLAGS = --tag=disable-static -noinst_HEADERS = gstdvdspu.h +noinst_HEADERS = gstdvdspu.h gstspu-pgs.h gstspu-vobsub.h EXTRA_DIST = Notes.txt diff --git a/gst/dvdspu/gstdvdspu-render.c b/gst/dvdspu/gstdvdspu-render.c index e3bae8b6..7731aed4 100644 --- a/gst/dvdspu/gstdvdspu-render.c +++ b/gst/dvdspu/gstdvdspu-render.c @@ -1,5 +1,6 @@ /* GStreamer DVD Sub-Picture Unit * Copyright (C) 2007 Fluendo S.A. <info@fluendo.com> + * Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -29,338 +30,38 @@ GST_DEBUG_CATEGORY_EXTERN (dvdspu_debug); #define GST_CAT_DEFAULT dvdspu_debug -static void -dvdspu_recalc_palette (GstDVDSpu * dvdspu, - SpuColour * dest, guint8 * idx, guint8 * alpha) -{ - SpuState *state = &dvdspu->spu_state; - gint i; - - for (i = 0; i < 4; i++, dest++) { - guint32 col = state->current_clut[idx[i]]; - - dest->Y = (guint16) ((col >> 16) & 0xff) * alpha[i]; - /* U/V are stored as V/U in the clut words, so switch them */ - dest->U = (guint16) (col & 0xff) * alpha[i]; - dest->V = (guint16) ((col >> 8) & 0xff) * alpha[i]; - dest->A = alpha[i]; - } -} - -/* Recalculate the main, HL & ChgCol palettes */ -static void -dvdspu_update_palettes (GstDVDSpu * dvdspu, SpuState * state) -{ - gint16 l, c; - guint8 index[4]; /* Indices for the palette */ - guint8 alpha[4]; /* Alpha values the palette */ - - if (state->main_pal_dirty) { - dvdspu_recalc_palette (dvdspu, state->main_pal, state->main_idx, - state->main_alpha); - - /* Need to refresh the hl_ctrl info copies of the main palette too */ - memcpy (state->hl_ctrl_i.pix_ctrl_i[0].pal_cache, state->main_pal, - 4 * sizeof (SpuColour)); - memcpy (state->hl_ctrl_i.pix_ctrl_i[2].pal_cache, state->main_pal, - 4 * sizeof (SpuColour)); - - state->main_pal_dirty = FALSE; - } - - if (state->hl_pal_dirty) { - dvdspu_recalc_palette (dvdspu, state->hl_ctrl_i.pix_ctrl_i[1].pal_cache, - state->hl_idx, state->hl_alpha); - state->hl_pal_dirty = FALSE; - } - - /* Update the offset positions for the highlight region */ - if (state->hl_rect.top != -1) { - state->hl_ctrl_i.top = state->hl_rect.top; - state->hl_ctrl_i.bottom = state->hl_rect.bottom; - state->hl_ctrl_i.n_changes = 3; - state->hl_ctrl_i.pix_ctrl_i[0].left = 0; - state->hl_ctrl_i.pix_ctrl_i[1].left = state->hl_rect.left; - state->hl_ctrl_i.pix_ctrl_i[2].left = state->hl_rect.right + 1; - } - - if (state->line_ctrl_i_pal_dirty) { - GST_LOG_OBJECT (dvdspu, "Updating chg-col-con palettes"); - for (l = 0; l < state->n_line_ctrl_i; l++) { - SpuLineCtrlI *cur_line_ctrl = state->line_ctrl_i + l; - - for (c = 0; c < cur_line_ctrl->n_changes; c++) { - SpuPixCtrlI *cur = cur_line_ctrl->pix_ctrl_i + c; - - index[3] = (cur->palette >> 28) & 0x0f; - index[2] = (cur->palette >> 24) & 0x0f; - index[1] = (cur->palette >> 20) & 0x0f; - index[0] = (cur->palette >> 16) & 0x0f; - - alpha[3] = (cur->palette >> 12) & 0x0f; - alpha[2] = (cur->palette >> 8) & 0x0f; - alpha[1] = (cur->palette >> 4) & 0x0f; - alpha[0] = (cur->palette) & 0x0f; - dvdspu_recalc_palette (dvdspu, cur->pal_cache, index, alpha); - } - } - state->line_ctrl_i_pal_dirty = FALSE; - } -} - -static void -dvdspu_clear_comp_buffers (SpuState * state) +void +gstspu_clear_comp_buffers (SpuState * state) { - /* The area to clear is the line inside the disp_rect, each entry 2 bytes, + /* The area to clear is the line inside the disp_rect, each entry 4 bytes, * of the sub-sampled UV planes. */ - gint16 left = state->disp_rect.left / 2; - gint16 right = state->disp_rect.right / 2; - gint16 uv_width = 2 * (right - left + 1); + gint16 left = state->comp_left / 2; + gint16 right = state->comp_right / 2; + gint16 uv_width = sizeof (guint32) * (right - left + 1); memset (state->comp_bufs[0] + left, 0, uv_width); memset (state->comp_bufs[1] + left, 0, uv_width); memset (state->comp_bufs[2] + left, 0, uv_width); - - state->comp_last_x[0] = -1; - state->comp_last_x[1] = -1; -} - -static inline guint8 -dvdspu_get_nibble (SpuState * state, guint16 * rle_offset) -{ - guint8 ret; - - if (G_UNLIKELY (*rle_offset >= state->max_offset)) - return 0; /* Overran the buffer */ - - ret = GST_BUFFER_DATA (state->pix_buf)[(*rle_offset) / 2]; - - /* If the offset is even, we shift the answer down 4 bits, otherwise not */ - if (*rle_offset & 0x01) - ret &= 0x0f; - else - ret = ret >> 4; - - (*rle_offset)++; - return ret; } -static guint16 -dvdspu_get_rle_code (SpuState * state, guint16 * rle_offset) -{ - guint16 code; - - code = dvdspu_get_nibble (state, rle_offset); - if (code < 0x4) { /* 4 .. f */ - code = (code << 4) | dvdspu_get_nibble (state, rle_offset); - if (code < 0x10) { /* 1x .. 3x */ - code = (code << 4) | dvdspu_get_nibble (state, rle_offset); - if (code < 0x40) { /* 04x .. 0fx */ - code = (code << 4) | dvdspu_get_nibble (state, rle_offset); - } - } - } - return code; -} - -static inline void -dvdspu_draw_rle_run (SpuState * state, gint16 x, gint16 end, SpuColour * colour) -{ -#if 0 - GST_LOG ("Y: %d x: %d end %d col %d %d %d %d", - state->cur_Y, x, end, colour->Y, colour->U, colour->V, colour->A); -#endif - - if (colour->A != 0) { - guint8 inv_A = 0xf - colour->A; - - /* FIXME: This could be more efficient */ - while (x < end) { - state->out_Y[x] = (inv_A * state->out_Y[x] + colour->Y) / 0xf; - state->out_U[x / 2] += colour->U; - state->out_V[x / 2] += colour->V; - state->out_A[x / 2] += colour->A; - x++; - } - /* Update the compositing buffer so we know how much to blend later */ - *(state->comp_last_x_ptr) = end; - } -} - -static inline gint16 -rle_end_x (guint16 rle_code, gint16 x, gint16 end) -{ - /* run length = rle_code >> 2 */ - if (G_UNLIKELY (((rle_code >> 2) == 0))) - return end; - else - return MIN (end, x + (rle_code >> 2)); -} - -static void dvdspu_render_line_with_chgcol (SpuState * state, - guint8 * planes[3], guint16 * rle_offset); -static gboolean dvdspu_update_chgcol (SpuState * state); - -static void -dvdspu_render_line (SpuState * state, guint8 * planes[3], guint16 * rle_offset) -{ - gint16 x, next_x, end, rle_code; - SpuColour *colour; - - /* Check for special case of chg_col info to use (either highlight or - * ChgCol command */ - if (state->cur_chg_col != NULL) { - if (dvdspu_update_chgcol (state)) { - /* Check the top & bottom, because we might not be within the region yet */ - if (state->cur_Y >= state->cur_chg_col->top && - state->cur_Y <= state->cur_chg_col->bottom) { - dvdspu_render_line_with_chgcol (state, planes, rle_offset); - return; - } - } - } - - /* No special case. Render as normal */ - - /* Set up our output pointers */ - state->out_Y = planes[0]; - state->out_U = state->comp_bufs[0]; - state->out_V = state->comp_bufs[1]; - state->out_A = state->comp_bufs[2]; - /* We always need to start our RLE decoding byte_aligned */ - *rle_offset = GST_ROUND_UP_2 (*rle_offset); - - x = state->disp_rect.left; - end = state->disp_rect.right + 1; - while (x < end) { - rle_code = dvdspu_get_rle_code (state, rle_offset); - colour = &state->main_pal[rle_code & 3]; - next_x = rle_end_x (rle_code, x, end); - /* Now draw the run between [x,next_x) */ - dvdspu_draw_rle_run (state, x, next_x, colour); - x = next_x; - } -} - -static gboolean -dvdspu_update_chgcol (SpuState * state) -{ - if (state->cur_chg_col == NULL) - return FALSE; - - if (state->cur_Y <= state->cur_chg_col->bottom) - return TRUE; - - while (state->cur_chg_col < state->cur_chg_col_end) { - if (state->cur_Y >= state->cur_chg_col->top && - state->cur_Y <= state->cur_chg_col->bottom) { -#if 0 - g_print ("Stopped @ entry %d with top %d bottom %d, cur_y %d", - (gint16) (state->cur_chg_col - state->line_ctrl_i), - state->cur_chg_col->top, state->cur_chg_col->bottom, y); -#endif - return TRUE; - } - state->cur_chg_col++; - } - - /* Finished all our cur_chg_col entries. Use the main palette from here on */ - state->cur_chg_col = NULL; - return FALSE; -} - -static void -dvdspu_render_line_with_chgcol (SpuState * state, guint8 * planes[3], - guint16 * rle_offset) -{ - SpuLineCtrlI *chg_col = state->cur_chg_col; - - gint16 x, next_x, disp_end, rle_code, run_end; - SpuColour *colour; - SpuPixCtrlI *cur_pix_ctrl; - SpuPixCtrlI *next_pix_ctrl; - SpuPixCtrlI *end_pix_ctrl; - SpuPixCtrlI dummy_pix_ctrl; - gint16 cur_reg_end; - gint i; - - state->out_Y = planes[0]; - state->out_U = state->comp_bufs[0]; - state->out_V = state->comp_bufs[1]; - state->out_A = state->comp_bufs[2]; - - /* We always need to start our RLE decoding byte_aligned */ - *rle_offset = GST_ROUND_UP_2 (*rle_offset); - - /* Our run will cover the display rect */ - x = state->disp_rect.left; - disp_end = state->disp_rect.right + 1; - - /* Work out the first pixel control info, which may point to the dummy entry if - * the global palette/alpha need using initally */ - cur_pix_ctrl = chg_col->pix_ctrl_i; - end_pix_ctrl = chg_col->pix_ctrl_i + chg_col->n_changes; - - if (cur_pix_ctrl->left != 0) { - next_pix_ctrl = cur_pix_ctrl; - cur_pix_ctrl = &dummy_pix_ctrl; - for (i = 0; i < 4; i++) /* Copy the main palette to our dummy entry */ - dummy_pix_ctrl.pal_cache[i] = state->main_pal[i]; - } else { - next_pix_ctrl = cur_pix_ctrl + 1; - } - if (next_pix_ctrl < end_pix_ctrl) - cur_reg_end = next_pix_ctrl->left; - else - cur_reg_end = disp_end; - - /* Render stuff */ - while (x < disp_end) { - rle_code = dvdspu_get_rle_code (state, rle_offset); - next_x = rle_end_x (rle_code, x, disp_end); - - /* Now draw the run between [x,next_x), crossing palette regions as needed */ - while (x < next_x) { - run_end = MIN (next_x, cur_reg_end); - - if (G_LIKELY (x < run_end)) { - colour = &cur_pix_ctrl->pal_cache[rle_code & 3]; - dvdspu_draw_rle_run (state, x, run_end, colour); - x = run_end; - } - - if (x >= cur_reg_end) { - /* Advance to next region */ - cur_pix_ctrl = next_pix_ctrl; - next_pix_ctrl++; - - if (next_pix_ctrl < end_pix_ctrl) - cur_reg_end = next_pix_ctrl->left; - else - cur_reg_end = disp_end; - } - } - } -} - -static void -dvdspu_blend_comp_buffers (SpuState * state, guint8 * planes[3]) +void +gstspu_blend_comp_buffers (SpuState * state, guint8 * planes[3]) { gint16 uv_end; gint16 left, x; guint8 *out_U; guint8 *out_V; - guint16 *in_U; - guint16 *in_V; - guint16 *in_A; - gint16 comp_last_x = MAX (state->comp_last_x[0], state->comp_last_x[1]); + guint32 *in_U; + guint32 *in_V; + guint32 *in_A; + gint16 comp_last_x = state->comp_right; - if (comp_last_x < state->disp_rect.left) + if (comp_last_x < state->comp_left) return; /* Didn't draw in the comp buffers, nothing to do... */ #if 0 - GST_LOG ("Blending comp buffers from disp_rect.left %d to x=%d", - state->disp_rect.left, comp_last_x); + GST_LOG ("Blending comp buffers from x=%d to x=%d", + state->comp_left, state->comp_right); #endif /* Set up the output pointers */ @@ -376,186 +77,18 @@ dvdspu_blend_comp_buffers (SpuState * state, guint8 * planes[3]) * drawn in the render_line function, divided by 2 (rounding up) to account * for UV sub-sampling */ uv_end = (comp_last_x + 1) / 2; - left = state->disp_rect.left / 2; + left = state->comp_left / 2; for (x = left; x < uv_end; x++) { - guint16 tmp; - guint16 inv_A = (4 * 0xf) - in_A[x]; - + guint32 tmp; /* Each entry in the compositing buffer is 4 summed pixels, so the - * inverse alpha is (4 * 0x0f) - in_A[x] */ + * inverse alpha is (4 * 0xff) - in_A[x] */ + guint16 inv_A = (4 * 0xff) - in_A[x]; + tmp = in_U[x] + inv_A * out_U[x]; - out_U[x] = (guint8) (tmp / (4 * 0xf)); + out_U[x] = (guint8) (tmp / (4 * 0xff)); tmp = in_V[x] + inv_A * out_V[x]; - out_V[x] = (guint8) (tmp / (4 * 0xf)); - } -} - -void -gst_dvd_spu_render_spu (GstDVDSpu * dvdspu, GstBuffer * buf) -{ - SpuState *state = &dvdspu->spu_state; - guint8 *planes[3]; /* YUV frame pointers */ - gint y, last_y; - - /* Set up our initial state */ - - /* Store the start of each plane */ - planes[0] = GST_BUFFER_DATA (buf); - planes[1] = planes[0] + (state->Y_height * state->Y_stride); - planes[2] = planes[1] + (state->UV_height * state->UV_stride); - - /* Sanity check */ - g_return_if_fail (planes[2] + (state->UV_height * state->UV_stride) <= - GST_BUFFER_DATA (buf) + GST_BUFFER_SIZE (buf)); - - GST_DEBUG ("Rendering SPU. disp_rect %d,%d to %d,%d. hl_rect %d,%d to %d,%d", - state->disp_rect.left, state->disp_rect.top, - state->disp_rect.right, state->disp_rect.bottom, - state->hl_rect.left, state->hl_rect.top, - state->hl_rect.right, state->hl_rect.bottom); - - GST_DEBUG ("vid_disp %d,%d", state->vid_width, state->vid_height); - - /* When reading RLE data, we track the offset in nibbles... */ - state->cur_offsets[0] = state->pix_data[0] * 2; - state->cur_offsets[1] = state->pix_data[1] * 2; - state->max_offset = GST_BUFFER_SIZE (state->pix_buf) * 2; - - /* Update all the palette caches */ - dvdspu_update_palettes (dvdspu, state); - - /* Set up HL or Change Color & Contrast rect tracking */ - if (state->hl_rect.top != -1) { - state->cur_chg_col = &state->hl_ctrl_i; - state->cur_chg_col_end = state->cur_chg_col + 1; - } else if (state->n_line_ctrl_i > 0) { - state->cur_chg_col = state->line_ctrl_i; - state->cur_chg_col_end = state->cur_chg_col + state->n_line_ctrl_i; - } else - state->cur_chg_col = NULL; - - /* We start rendering from the first line of the display rect */ - y = state->disp_rect.top; - /* start_y is always an even number and we render lines in pairs from there, - * accumulating 2 lines of chroma then blending it. We might need to render a - * single line at the end if the display rect ends on an even line too. */ - last_y = (state->disp_rect.bottom - 1) & ~(0x01); - - /* center the image when display rectangle exceeds the video width */ - if (state->vid_width < state->disp_rect.right) { - gint diff, disp_width; - - disp_width = state->disp_rect.left - state->disp_rect.right; - diff = (disp_width - state->vid_width) / 2; - - /* fixme, this is not used yet */ - state->clip_rect.left = state->disp_rect.left + diff; - state->clip_rect.right = state->disp_rect.right - diff; - - GST_DEBUG ("clipping width to %d,%d", state->clip_rect.left, - state->clip_rect.right); - } else { - state->clip_rect.left = state->disp_rect.left; - state->clip_rect.right = state->disp_rect.right; - } - - /* for the height, chop off the bottom bits of the diplay rectangle because we - * assume the picture is in the lower part. We should better check where it - * is and do something more clever. */ - state->clip_rect.bottom = state->disp_rect.bottom; - if (state->vid_height < state->disp_rect.bottom) { - state->clip_rect.top = state->disp_rect.bottom - state->vid_height; - GST_DEBUG ("clipping height to %d,%d", state->clip_rect.top, - state->clip_rect.bottom); - } else { - state->clip_rect.top = state->disp_rect.top; - /* Update our plane references to the first line of the disp_rect */ - planes[0] += state->Y_stride * y; - planes[1] += state->UV_stride * (y / 2); - planes[2] += state->UV_stride * (y / 2); - } - - for (state->cur_Y = y; state->cur_Y <= last_y; state->cur_Y++) { - gboolean clip; - - clip = (state->cur_Y < state->clip_rect.top - || state->cur_Y > state->clip_rect.bottom); - - /* Reset the compositing buffer */ - dvdspu_clear_comp_buffers (state); - /* Render even line */ - state->comp_last_x_ptr = state->comp_last_x; - dvdspu_render_line (state, planes, &state->cur_offsets[0]); - if (!clip) { - /* Advance the luminance output pointer */ - planes[0] += state->Y_stride; - } - state->cur_Y++; - - /* Render odd line */ - state->comp_last_x_ptr = state->comp_last_x + 1; - dvdspu_render_line (state, planes, &state->cur_offsets[1]); - /* Blend the accumulated UV compositing buffers onto the output */ - dvdspu_blend_comp_buffers (state, planes); - - if (!clip) { - /* Update all the output pointers */ - planes[0] += state->Y_stride; - planes[1] += state->UV_stride; - planes[2] += state->UV_stride; - } - } - if (state->cur_Y == state->disp_rect.bottom) { - g_assert ((state->disp_rect.bottom & 0x01) == 0); - - /* Render a remaining lone last even line. y already has the correct value - * after the above loop exited. */ - dvdspu_clear_comp_buffers (state); - state->comp_last_x_ptr = state->comp_last_x; - dvdspu_render_line (state, planes, &state->cur_offsets[0]); - dvdspu_blend_comp_buffers (state, planes); + out_V[x] = (guint8) (tmp / (4 * 0xff)); } - - /* for debugging purposes, draw a faint rectangle at the edges of the disp_rect */ -#if 0 - do { - guint8 *cur; - gint16 pos; - - cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->disp_rect.top; - for (pos = state->disp_rect.left + 1; pos < state->disp_rect.right; pos++) - cur[pos] = (cur[pos] / 2) + 0x8; - cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->disp_rect.bottom; - for (pos = state->disp_rect.left + 1; pos < state->disp_rect.right; pos++) - cur[pos] = (cur[pos] / 2) + 0x8; - cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->disp_rect.top; - for (pos = state->disp_rect.top; pos <= state->disp_rect.bottom; pos++) { - cur[state->disp_rect.left] = (cur[state->disp_rect.left] / 2) + 0x8; - cur[state->disp_rect.right] = (cur[state->disp_rect.right] / 2) + 0x8; - cur += state->Y_stride; - } - } while (0); -#endif - /* For debugging purposes, draw a faint rectangle around the highlight rect */ -#if 0 - if (state->hl_rect.top != -1) { - guint8 *cur; - gint16 pos; - - cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->hl_rect.top; - for (pos = state->hl_rect.left + 1; pos < state->hl_rect.right; pos++) - cur[pos] = (cur[pos] / 2) + 0x8; - cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->hl_rect.bottom; - for (pos = state->hl_rect.left + 1; pos < state->hl_rect.right; pos++) - cur[pos] = (cur[pos] / 2) + 0x8; - cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->hl_rect.top; - for (pos = state->hl_rect.top; pos <= state->hl_rect.bottom; pos++) { - cur[state->hl_rect.left] = (cur[state->hl_rect.left] / 2) + 0x8; - cur[state->hl_rect.right] = (cur[state->hl_rect.right] / 2) + 0x8; - cur += state->Y_stride; - } - } -#endif } diff --git a/gst/dvdspu/gstdvdspu.c b/gst/dvdspu/gstdvdspu.c index 3b4c1a0c..f5fce4bc 100644 --- a/gst/dvdspu/gstdvdspu.c +++ b/gst/dvdspu/gstdvdspu.c @@ -32,22 +32,17 @@ # include <config.h> #endif +#include <gst/gst-i18n-plugin.h> + #include <string.h> #include <gst/gst.h> #include "gstdvdspu.h" -#define DUMP_DCSQ 0 - -extern void gst_dvd_spu_render_spu (GstDVDSpu * dvdspu, GstBuffer * buf); - GST_DEBUG_CATEGORY (dvdspu_debug); #define GST_CAT_DEFAULT dvdspu_debug -/* Convert an STM offset in the SPU sequence to a GStreamer timestamp */ -#define STM_TO_GST(stm) ((GST_MSECOND * 1024 * (stm)) / 90) - /* Filter signals and args */ enum { @@ -73,12 +68,19 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", ); static GstStaticPadTemplate subpic_sink_factory = -GST_STATIC_PAD_TEMPLATE ("subpicture", + GST_STATIC_PAD_TEMPLATE ("subpicture", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("video/x-dvd-subpicture") + GST_STATIC_CAPS ("video/x-dvd-subpicture; subpicture/x-pgs") ); +static const guint32 default_clut[16] = { + 0xb48080, 0x248080, 0x628080, 0xd78080, + 0x808080, 0x808080, 0x808080, 0x808080, + 0x808080, 0x808080, 0x808080, 0x808080, + 0x808080, 0x808080, 0x808080, 0x808080 +}; + GST_BOILERPLATE (GstDVDSpu, gst_dvd_spu, GstElement, GST_TYPE_ELEMENT); static void gst_dvd_spu_dispose (GObject * object); @@ -94,27 +96,31 @@ static GstFlowReturn gst_dvd_spu_video_chain (GstPad * pad, GstBuffer * buf); static gboolean gst_dvd_spu_video_event (GstPad * pad, GstEvent * event); static GstFlowReturn gst_dvd_spu_buffer_alloc (GstPad * sinkpad, guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf); -static void gst_dvd_spu_redraw_still (GstDVDSpu * dvdspu); +static void gst_dvd_spu_redraw_still (GstDVDSpu * dvdspu, gboolean force); static void gst_dvd_spu_check_still_updates (GstDVDSpu * dvdspu); static GstFlowReturn gst_dvd_spu_subpic_chain (GstPad * pad, GstBuffer * buf); static gboolean gst_dvd_spu_subpic_event (GstPad * pad, GstEvent * event); +static gboolean gst_dvd_spu_subpic_set_caps (GstPad * pad, GstCaps * caps); static void gst_dvd_spu_clear (GstDVDSpu * dvdspu); -static void gst_dvd_spu_flush_spu_info (GstDVDSpu * dvdspu); +static void gst_dvd_spu_flush_spu_info (GstDVDSpu * dvdspu, + gboolean process_events); static void gst_dvd_spu_advance_spu (GstDVDSpu * dvdspu, GstClockTime new_ts); +static void gstspu_render (GstDVDSpu * dvdspu, GstBuffer * buf); static GstFlowReturn dvdspu_handle_vid_buffer (GstDVDSpu * dvdspu, GstBuffer * buf); +static void gst_dvd_spu_handle_dvd_event (GstDVDSpu * dvdspu, GstEvent * event); static void gst_dvd_spu_base_init (gpointer gclass) { static GstElementDetails element_details = - GST_ELEMENT_DETAILS ("Fluendo DVD Player Sub-picture Overlay", - "Mixer/Video/Overlay/DVD", - "Parses the DVD Sub-Picture command stream and renders the SPU overlay " + GST_ELEMENT_DETAILS ("GStreamer Sub-picture Overlay", + "Mixer/Video/Overlay/DVD/Bluray", + "Parses Sub-Picture command streams and renders the SPU overlay " "onto the video as it passes through", - "Jan Schmidt <jan@fluendo.com>"); + "Jan Schmidt <thaytan@noraisin.net>"); GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); gst_element_class_add_pad_template (element_class, @@ -164,7 +170,8 @@ gst_dvd_spu_init (GstDVDSpu * dvdspu, GstDVDSpuClass * gclass) gst_pad_new_from_static_template (&subpic_sink_factory, "subpicture"); gst_pad_set_chain_function (dvdspu->subpic_sinkpad, gst_dvd_spu_subpic_chain); gst_pad_set_event_function (dvdspu->subpic_sinkpad, gst_dvd_spu_subpic_event); - gst_pad_use_fixed_caps (dvdspu->subpic_sinkpad); + gst_pad_set_setcaps_function (dvdspu->subpic_sinkpad, + gst_dvd_spu_subpic_set_caps); gst_element_add_pad (GST_ELEMENT (dvdspu), dvdspu->videosinkpad); gst_element_add_pad (GST_ELEMENT (dvdspu), dvdspu->subpic_sinkpad); @@ -179,7 +186,10 @@ gst_dvd_spu_init (GstDVDSpu * dvdspu, GstDVDSpuClass * gclass) static void gst_dvd_spu_clear (GstDVDSpu * dvdspu) { - gst_dvd_spu_flush_spu_info (dvdspu); + gst_dvd_spu_flush_spu_info (dvdspu, FALSE); + gst_segment_init (&dvdspu->subp_seg, GST_FORMAT_UNDEFINED); + + dvdspu->spu_input_type = SPU_INPUT_TYPE_NONE; gst_buffer_replace (&dvdspu->ref_frame, NULL); gst_buffer_replace (&dvdspu->pending_frame, NULL); @@ -223,15 +233,14 @@ gst_dvd_spu_finalize (GObject * object) /* With SPU lock held, clear the queue of SPU packets */ static void -gst_dvd_spu_flush_spu_info (GstDVDSpu * dvdspu) +gst_dvd_spu_flush_spu_info (GstDVDSpu * dvdspu, gboolean keep_events) { SpuPacket *packet; SpuState *state = &dvdspu->spu_state; + GQueue tmp_q = G_QUEUE_INIT; GST_INFO_OBJECT (dvdspu, "Flushing SPU information"); - gst_segment_init (&dvdspu->subp_seg, GST_FORMAT_UNDEFINED); - if (dvdspu->partial_spu) { gst_buffer_unref (dvdspu->partial_spu); dvdspu->partial_spu = NULL; @@ -239,38 +248,37 @@ gst_dvd_spu_flush_spu_info (GstDVDSpu * dvdspu) packet = (SpuPacket *) g_queue_pop_head (dvdspu->pending_spus); while (packet != NULL) { - if (packet->buf) + if (packet->buf) { gst_buffer_unref (packet->buf); - if (packet->event) - gst_event_unref (packet->event); - g_free (packet); + g_assert (packet->event == NULL); + g_free (packet); + } else if (packet->event) { + if (keep_events) { + g_queue_push_tail (&tmp_q, packet); + } else { + gst_event_unref (packet->event); + g_free (packet); + } + } packet = (SpuPacket *) g_queue_pop_head (dvdspu->pending_spus); } + /* Push anything we decided to keep back onto the pending_spus list */ + for (packet = g_queue_pop_head (&tmp_q); packet != NULL; + packet = g_queue_pop_head (&tmp_q)) + g_queue_push_tail (dvdspu->pending_spus, packet); - if (state->buf) { - gst_buffer_unref (state->buf); - state->buf = NULL; - } - if (state->pix_buf) { - gst_buffer_unref (state->pix_buf); - state->pix_buf = NULL; - } - - state->base_ts = state->next_ts = GST_CLOCK_TIME_NONE; state->flags &= ~(SPU_STATE_FLAGS_MASK); - state->pix_data[0] = 0; - state->pix_data[1] = 0; + state->next_ts = GST_CLOCK_TIME_NONE; - state->hl_rect.top = -1; - state->hl_rect.bottom = -1; - - state->disp_rect.top = -1; - state->disp_rect.bottom = -1; - - state->n_line_ctrl_i = 0; - if (state->line_ctrl_i != NULL) { - g_free (state->line_ctrl_i); - state->line_ctrl_i = NULL; + switch (dvdspu->spu_input_type) { + case SPU_INPUT_TYPE_VOBSUB: + gstspu_vobsub_flush (dvdspu); + break; + case SPU_INPUT_TYPE_PGS: + gstspu_pgs_flush (dvdspu); + break; + default: + break; } } @@ -342,7 +350,7 @@ gst_dvd_spu_video_set_caps (GstPad * pad, GstCaps * caps) state->UV_stride = GST_ROUND_UP_4 (state->Y_stride / 2); for (i = 0; i < 3; i++) { state->comp_bufs[i] = g_realloc (state->comp_bufs[i], - sizeof (guint16) * state->UV_stride); + sizeof (guint32) * state->UV_stride); } } DVD_SPU_UNLOCK (dvdspu); @@ -413,13 +421,16 @@ gst_dvd_spu_video_event (GstPad * pad, GstEvent * event) break; } - GST_DEBUG_OBJECT (dvdspu, - "DVD event of type %s on video pad", event_type); - if (strcmp (event_type, "dvd-still") == 0) { gboolean in_still; if (gst_structure_get_boolean (structure, "still-state", &in_still)) { + GstBuffer *to_push = NULL; + + GST_DEBUG_OBJECT (dvdspu, + "DVD event of type %s on video pad: in-still = %d", event_type, + in_still); + DVD_SPU_LOCK (dvdspu); if (in_still) { state->flags |= SPU_STATE_STILL_FRAME; @@ -429,15 +440,24 @@ gst_dvd_spu_video_event (GstPad * pad, GstEvent * event) /* And re-draw the still frame to make sure it appears on * screen, otherwise the last frame might have been discarded * by QoS */ - gst_dvd_spu_redraw_still (dvdspu); - } else + gst_dvd_spu_redraw_still (dvdspu, TRUE); + to_push = dvdspu->pending_frame; + dvdspu->pending_frame = NULL; + + } else { state->flags &= ~(SPU_STATE_STILL_FRAME); + } DVD_SPU_UNLOCK (dvdspu); + if (to_push) + gst_pad_push (dvdspu->srcpad, to_push); } gst_event_unref (event); res = TRUE; - } else + } else { + GST_DEBUG_OBJECT (dvdspu, + "DVD event of type %s on video pad", event_type); res = gst_pad_event_default (pad, event); + } break; } case GST_EVENT_NEWSEGMENT: @@ -602,7 +622,7 @@ dvdspu_handle_vid_buffer (GstDVDSpu * dvdspu, GstBuffer * buf) /* Render the SPU overlay onto the buffer */ buf = gst_buffer_make_writable (buf); - gst_dvd_spu_render_spu (dvdspu, buf); + gstspu_render (dvdspu, buf); } else { if (using_ref == FALSE) { /* Not going to draw anything on this frame, just store a reference @@ -631,17 +651,35 @@ no_ref_frame: return GST_FLOW_OK; } + +static void +gstspu_render (GstDVDSpu * dvdspu, GstBuffer * buf) +{ + switch (dvdspu->spu_input_type) { + case SPU_INPUT_TYPE_VOBSUB: + gstspu_vobsub_render (dvdspu, buf); + break; + case SPU_INPUT_TYPE_PGS: + gstspu_pgs_render (dvdspu, buf); + break; + default: + break; + } +} + /* With SPU LOCK */ static void -gst_dvd_spu_redraw_still (GstDVDSpu * dvdspu) +gst_dvd_spu_redraw_still (GstDVDSpu * dvdspu, gboolean force) { /* If we have an active SPU command set and a reference frame, copy the * frame, redraw the SPU and store it as the pending frame for output */ if (dvdspu->ref_frame) { - if ((dvdspu->spu_state.flags & SPU_STATE_FORCED_DSP) || - ((dvdspu->spu_state.flags & SPU_STATE_FORCED_ONLY) == 0 && - (dvdspu->spu_state.flags & SPU_STATE_DISPLAY))) { - GstBuffer *buf = gst_buffer_copy (dvdspu->ref_frame); + gboolean redraw = (dvdspu->spu_state.flags & SPU_STATE_FORCED_DSP); + redraw |= (dvdspu->spu_state.flags & SPU_STATE_FORCED_ONLY) == 0 && + (dvdspu->spu_state.flags & SPU_STATE_DISPLAY); + + if (redraw) { + GstBuffer *buf = gst_buffer_ref (dvdspu->ref_frame); buf = gst_buffer_make_writable (buf); @@ -652,11 +690,23 @@ gst_dvd_spu_redraw_still (GstDVDSpu * dvdspu) GST_BUFFER_DURATION (buf) = GST_CLOCK_TIME_NONE; /* Render the SPU overlay onto the buffer */ - gst_dvd_spu_render_spu (dvdspu, buf); + gstspu_render (dvdspu, buf); gst_buffer_replace (&dvdspu->pending_frame, buf); + gst_buffer_unref (buf); + } else if (force) { + /* Simply output the reference frame */ + GstBuffer *buf = gst_buffer_ref (dvdspu->ref_frame); + buf = gst_buffer_make_metadata_writable (buf); + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); + GST_BUFFER_TIMESTAMP (buf) = GST_CLOCK_TIME_NONE; + GST_BUFFER_DURATION (buf) = GST_CLOCK_TIME_NONE; + + GST_DEBUG_OBJECT (dvdspu, "Pushing reference frame at start of still"); + + gst_buffer_replace (&dvdspu->pending_frame, buf); + gst_buffer_unref (buf); } else { - GST_LOG_OBJECT (dvdspu, - "Redraw due to Still Frame skipped - no SPU to draw"); + GST_LOG_OBJECT (dvdspu, "Redraw due to Still Frame skipped"); } } else { GST_LOG_OBJECT (dvdspu, "Not redrawing still frame - no ref frame"); @@ -664,392 +714,47 @@ gst_dvd_spu_redraw_still (GstDVDSpu * dvdspu) } static void -gst_dvd_spu_parse_chg_colcon (GstDVDSpu * dvdspu, guint8 * data, guint8 * end) -{ - SpuState *state = &dvdspu->spu_state; - guint8 *cur; - gint16 n_entries; - gint16 i; - - /* Clear any existing chg colcon info */ - state->n_line_ctrl_i = 0; - if (state->line_ctrl_i != NULL) { - g_free (state->line_ctrl_i); - state->line_ctrl_i = NULL; - } - GST_DEBUG_OBJECT (dvdspu, "Change Color & Contrast. Pixel data = %d bytes", - (gint16) (end - data)); - - /* Count the number of entries we'll need */ - n_entries = 0; - for (cur = data; cur < end;) { - guint8 n_changes; - guint32 code; - - if (cur + 4 > end) - break; - - code = GST_READ_UINT32_BE (cur); - if (code == 0x0fffffff) - break; /* Termination code */ - - n_changes = CLAMP ((cur[2] >> 4), 1, 8); - cur += 4 + (6 * n_changes); - - if (cur > end) - break; /* Invalid entry overrunning buffer */ - - n_entries++; - } - - state->n_line_ctrl_i = n_entries; - state->line_ctrl_i = g_new (SpuLineCtrlI, n_entries); - - cur = data; - for (i = 0; i < n_entries; i++) { - SpuLineCtrlI *cur_line_ctrl = state->line_ctrl_i + i; - guint8 n_changes = CLAMP ((cur[2] >> 4), 1, 8); - guint8 c; - - cur_line_ctrl->n_changes = n_changes; - cur_line_ctrl->top = ((cur[0] << 8) & 0x300) | cur[1]; - cur_line_ctrl->bottom = ((cur[2] << 8) & 0x300) | cur[3]; - - GST_LOG_OBJECT (dvdspu, "ChgColcon Entry %d Top: %d Bottom: %d Changes: %d", - i, cur_line_ctrl->top, cur_line_ctrl->bottom, n_changes); - cur += 4; - - for (c = 0; c < n_changes; c++) { - SpuPixCtrlI *cur_pix_ctrl = cur_line_ctrl->pix_ctrl_i + c; - - cur_pix_ctrl->left = ((cur[0] << 8) & 0x300) | cur[1]; - cur_pix_ctrl->palette = GST_READ_UINT32_BE (cur + 2); - GST_LOG_OBJECT (dvdspu, " %d: left: %d palette 0x%x", c, - cur_pix_ctrl->left, cur_pix_ctrl->palette); - cur += 6; - } - } -} - -static void -gst_dvd_spu_exec_cmd_blk (GstDVDSpu * dvdspu, guint8 * data, guint8 * end) -{ - SpuState *state = &dvdspu->spu_state; - - while (data < end) { - guint8 cmd; - - cmd = data[0]; - - switch (cmd) { - case SPU_CMD_FSTA_DSP: - GST_DEBUG_OBJECT (dvdspu, " Forced Display"); - state->flags |= SPU_STATE_FORCED_DSP; - data += 1; - break; - case SPU_CMD_DSP: - GST_DEBUG_OBJECT (dvdspu, " Display On"); - state->flags |= SPU_STATE_DISPLAY; - data += 1; - break; - case SPU_CMD_STP_DSP: - GST_DEBUG_OBJECT (dvdspu, " Display Off"); - state->flags &= ~(SPU_STATE_FORCED_DSP | SPU_STATE_DISPLAY); - data += 1; - break; - case SPU_CMD_SET_COLOR:{ - if (G_UNLIKELY (data + 3 >= end)) - return; /* Invalid SET_COLOR cmd at the end of the blk */ - - state->main_idx[3] = data[1] >> 4; - state->main_idx[2] = data[1] & 0x0f; - state->main_idx[1] = data[2] >> 4; - state->main_idx[0] = data[2] & 0x0f; - - state->main_pal_dirty = TRUE; - - GST_DEBUG_OBJECT (dvdspu, - " Set Color bg %u pattern %u emph-1 %u emph-2 %u", - state->main_idx[0], state->main_idx[1], state->main_idx[2], - state->main_idx[3]); - data += 3; - break; - } - case SPU_CMD_SET_ALPHA:{ - if (G_UNLIKELY (data + 3 >= end)) - return; /* Invalid SET_ALPHA cmd at the end of the blk */ - - state->main_alpha[3] = data[1] >> 4; - state->main_alpha[2] = data[1] & 0x0f; - state->main_alpha[1] = data[2] >> 4; - state->main_alpha[0] = data[2] & 0x0f; - - state->main_pal_dirty = TRUE; - - GST_DEBUG_OBJECT (dvdspu, - " Set Alpha bg %u pattern %u emph-1 %u emph-2 %u", - state->main_alpha[0], state->main_alpha[1], state->main_alpha[2], - state->main_alpha[3]); - data += 3; - break; - } - case SPU_CMD_SET_DAREA:{ - SpuRect *r = &state->disp_rect; - - if (G_UNLIKELY (data + 7 >= end)) - return; /* Invalid SET_DAREA cmd at the end of the blk */ - - r->top = ((data[4] & 0x3f) << 4) | ((data[5] & 0xe0) >> 4); - r->left = ((data[1] & 0x3f) << 4) | ((data[2] & 0xf0) >> 4); - r->right = ((data[2] & 0x03) << 8) | data[3]; - r->bottom = ((data[5] & 0x03) << 8) | data[6]; - - GST_DEBUG_OBJECT (dvdspu, - " Set Display Area top %u left %u bottom %u right %u", r->top, - r->left, r->bottom, r->right); - - data += 7; - break; - } - case SPU_CMD_DSPXA:{ - if (G_UNLIKELY (data + 5 >= end)) - return; /* Invalid SET_DSPXE cmd at the end of the blk */ - - state->pix_data[0] = GST_READ_UINT16_BE (data + 1); - state->pix_data[1] = GST_READ_UINT16_BE (data + 3); - /* Store a reference to the current command buffer, as that's where - * we'll need to take our pixel data from */ - gst_buffer_replace (&state->pix_buf, state->buf); - - GST_DEBUG_OBJECT (dvdspu, " Set Pixel Data Offsets top: %u bot: %u", - state->pix_data[0], state->pix_data[1]); - - data += 5; - break; - } - case SPU_CMD_CHG_COLCON:{ - guint16 field_size; - - GST_DEBUG_OBJECT (dvdspu, " Set Color & Contrast Change"); - if (G_UNLIKELY (data + 3 >= end)) - return; /* Invalid CHG_COLCON cmd at the end of the blk */ - - data++; - field_size = GST_READ_UINT16_BE (data); - - if (G_UNLIKELY (data + field_size >= end)) - return; /* Invalid CHG_COLCON cmd at the end of the blk */ - - gst_dvd_spu_parse_chg_colcon (dvdspu, data + 2, data + field_size); - state->line_ctrl_i_pal_dirty = TRUE; - data += field_size; - break; - } - case SPU_CMD_END: - default: - GST_DEBUG_OBJECT (dvdspu, " END"); - data = end; - break; - } - } -} - -static void -gst_dvd_spu_finish_spu_buf (GstDVDSpu * dvdspu) -{ - SpuState *state = &dvdspu->spu_state; - - state->next_ts = state->base_ts = GST_CLOCK_TIME_NONE; - gst_buffer_replace (&state->buf, NULL); - - GST_DEBUG_OBJECT (dvdspu, "Finished SPU buffer"); -} - -static gboolean -gst_dvd_spu_setup_cmd_blk (GstDVDSpu * dvdspu, guint16 cmd_blk_offset, - guint8 * start, guint8 * end) -{ - SpuState *state = &dvdspu->spu_state; - guint16 delay; - guint8 *cmd_blk = start + cmd_blk_offset; - - if (G_UNLIKELY (cmd_blk + 5 >= end)) - return FALSE; /* No valid command block to read */ - - delay = GST_READ_UINT16_BE (cmd_blk); - state->next_ts = state->base_ts + STM_TO_GST (delay); - state->cur_cmd_blk = cmd_blk_offset; - - GST_DEBUG_OBJECT (dvdspu, "Setup CMD Block @ %u with TS %" GST_TIME_FORMAT, - state->cur_cmd_blk, GST_TIME_ARGS (state->next_ts)); - return TRUE; -} - -static void -gst_dvd_spu_handle_new_spu_buf (GstDVDSpu * dvdspu, SpuPacket * packet) -{ - guint8 *start, *end; - SpuState *state = &dvdspu->spu_state; - - if (G_UNLIKELY (GST_BUFFER_SIZE (packet->buf) < 4)) - goto invalid; - - if (state->buf != NULL) { - gst_buffer_unref (state->buf); - state->buf = NULL; - } - state->buf = packet->buf; - state->base_ts = packet->event_ts; - - start = GST_BUFFER_DATA (state->buf); - end = start + GST_BUFFER_SIZE (state->buf); - - /* Configure the first command block in this buffer as our initial blk */ - state->cur_cmd_blk = GST_READ_UINT16_BE (start + 2); - gst_dvd_spu_setup_cmd_blk (dvdspu, state->cur_cmd_blk, start, end); - /* Clear existing chg-colcon info */ - state->n_line_ctrl_i = 0; - if (state->line_ctrl_i != NULL) { - g_free (state->line_ctrl_i); - state->line_ctrl_i = NULL; - } - return; - -invalid: - /* Invalid buffer */ - gst_dvd_spu_finish_spu_buf (dvdspu); -} - -static void gst_dvd_spu_handle_dvd_event (GstDVDSpu * dvdspu, GstEvent * event) { - const gchar *event_type; const GstStructure *structure = gst_event_get_structure (event); - SpuState *state = &dvdspu->spu_state; + const gchar *event_type = gst_structure_get_string (structure, "event"); gboolean hl_change = FALSE; - event_type = gst_structure_get_string (structure, "event"); GST_INFO_OBJECT (dvdspu, "DVD event of type %s on subp pad OOB=%d", event_type, (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB)); - if (strcmp (event_type, "dvd-spu-clut-change") == 0) { - gchar prop_name[32]; - gint i; - gint entry; - - for (i = 0; i < 16; i++) { - g_snprintf (prop_name, 32, "clut%02d", i); - if (!gst_structure_get_int (structure, prop_name, &entry)) - entry = 0; - state->current_clut[i] = (guint32) entry; - } - - state->main_pal_dirty = TRUE; - state->hl_pal_dirty = TRUE; - state->line_ctrl_i_pal_dirty = TRUE; - hl_change = TRUE; - } else if (strcmp (event_type, "dvd-spu-highlight") == 0) { - gint val; - - if (gst_structure_get_int (structure, "palette", &val)) { - state->hl_idx[3] = ((guint32) (val) >> 28) & 0x0f; - state->hl_idx[2] = ((guint32) (val) >> 24) & 0x0f; - state->hl_idx[1] = ((guint32) (val) >> 20) & 0x0f; - state->hl_idx[0] = ((guint32) (val) >> 16) & 0x0f; - - state->hl_alpha[3] = ((guint32) (val) >> 12) & 0x0f; - state->hl_alpha[2] = ((guint32) (val) >> 8) & 0x0f; - state->hl_alpha[1] = ((guint32) (val) >> 4) & 0x0f; - state->hl_alpha[0] = ((guint32) (val) >> 0) & 0x0f; - - state->hl_pal_dirty = TRUE; - } - if (gst_structure_get_int (structure, "sx", &val)) - state->hl_rect.left = (gint16) val; - if (gst_structure_get_int (structure, "sy", &val)) - state->hl_rect.top = (gint16) val; - if (gst_structure_get_int (structure, "ex", &val)) - state->hl_rect.right = (gint16) val; - if (gst_structure_get_int (structure, "ey", &val)) - state->hl_rect.bottom = (gint16) val; - - GST_INFO_OBJECT (dvdspu, "Highlight rect is now (%d,%d) to (%d,%d)", - state->hl_rect.left, state->hl_rect.top, - state->hl_rect.right, state->hl_rect.bottom); - hl_change = TRUE; - } else if (strcmp (event_type, "dvd-spu-reset-highlight") == 0) { - if (state->hl_rect.top != -1 || state->hl_rect.bottom != -1) - hl_change = TRUE; - state->hl_rect.top = -1; - state->hl_rect.bottom = -1; - GST_INFO_OBJECT (dvdspu, "Highlight off"); - } else if (strcmp (event_type, "dvd-set-subpicture-track") == 0) { - gboolean forced_only; - - if (gst_structure_get_boolean (structure, "forced-only", &forced_only)) { - gboolean was_forced = (state->flags & SPU_STATE_FORCED_ONLY); - - if (forced_only) - state->flags |= SPU_STATE_FORCED_ONLY; - else - state->flags &= ~(SPU_STATE_FORCED_ONLY); - - if ((was_forced && !forced_only) || (!was_forced && forced_only)) - hl_change = TRUE; - } + switch (dvdspu->spu_input_type) { + case SPU_INPUT_TYPE_VOBSUB: + hl_change = gstspu_vobsub_handle_dvd_event (dvdspu, event); + break; + case SPU_INPUT_TYPE_PGS: + hl_change = gstspu_pgs_handle_dvd_event (dvdspu, event); + break; + default: + break; } - if (hl_change && (state->flags & SPU_STATE_STILL_FRAME)) { - gst_dvd_spu_redraw_still (dvdspu); + if (hl_change && (dvdspu->spu_state.flags & SPU_STATE_STILL_FRAME)) { + gst_dvd_spu_redraw_still (dvdspu, FALSE); } - - gst_event_unref (event); } -#if DUMP_DCSQ -static void -gst_dvd_spu_dump_dcsq (GstDVDSpu * dvdspu, - GstClockTime start_ts, GstBuffer * spu_buf) +static gboolean +gstspu_execute_event (GstDVDSpu * dvdspu) { - guint16 cmd_blk_offset; - guint16 next_blk; - guint8 *start, *end; - - start = GST_BUFFER_DATA (spu_buf); - end = start + GST_BUFFER_SIZE (spu_buf); - - g_return_if_fail (start != NULL); - - /* First command */ - next_blk = GST_READ_UINT16_BE (start + 2); - cmd_blk_offset = 0; - - /* Loop through all commands */ - g_print ("SPU begins @ %" GST_TIME_FORMAT " offset %u\n", - GST_TIME_ARGS (start_ts), next_blk); - - while (cmd_blk_offset != next_blk) { - guint8 *data; - GstClockTime cmd_blk_ts; - - cmd_blk_offset = next_blk; - - if (G_UNLIKELY (start + cmd_blk_offset + 5 >= end)) - break; /* No valid command to read */ - - data = start + cmd_blk_offset; - - cmd_blk_ts = start_ts + STM_TO_GST (GST_READ_UINT16_BE (data)); - next_blk = GST_READ_UINT16_BE (data + 2); - - g_print ("Cmd Blk @ offset %u next %u ts %" GST_TIME_FORMAT "\n", - cmd_blk_offset, next_blk, GST_TIME_ARGS (cmd_blk_ts)); - - data += 4; - gst_dvd_spu_exec_cmd_blk (dvdspu, data, end); + switch (dvdspu->spu_input_type) { + case SPU_INPUT_TYPE_VOBSUB: + return gstspu_vobsub_execute_event (dvdspu); + break; + case SPU_INPUT_TYPE_PGS: + return gstspu_pgs_execute_event (dvdspu); + break; + default: + g_assert_not_reached (); + break; } + return FALSE; } -#endif /* Advance the SPU packet/command queue to a time. new_ts is in running time */ static void @@ -1057,11 +762,15 @@ gst_dvd_spu_advance_spu (GstDVDSpu * dvdspu, GstClockTime new_ts) { SpuState *state = &dvdspu->spu_state; + if (G_UNLIKELY (dvdspu->spu_input_type == SPU_INPUT_TYPE_NONE)) + return; + while (state->next_ts == GST_CLOCK_TIME_NONE || state->next_ts <= new_ts) { - guint8 *start, *cmd_blk, *end; - guint16 next_blk; + GST_DEBUG_OBJECT (dvdspu, + "Advancing SPU from TS %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT, + GST_TIME_ARGS (state->next_ts), GST_TIME_ARGS (new_ts)); - if (state->buf == NULL) { + if (!gstspu_execute_event (dvdspu)) { GstClockTime vid_run_ts; /* No current command buffer, try and get one */ @@ -1082,51 +791,25 @@ gst_dvd_spu_advance_spu (GstDVDSpu * dvdspu, GstClockTime new_ts) packet->buf ? "buffer" : "event"); if (packet->buf) { -#if DUMP_DCSQ - gst_dvd_spu_dump_dcsq (dvdspu, packet->event_ts, packet->buf); -#endif - gst_dvd_spu_handle_new_spu_buf (dvdspu, packet); - } - if (packet->event) + switch (dvdspu->spu_input_type) { + case SPU_INPUT_TYPE_VOBSUB: + gstspu_vobsub_handle_new_buf (dvdspu, packet->event_ts, + packet->buf); + break; + case SPU_INPUT_TYPE_PGS: + gstspu_pgs_handle_new_buf (dvdspu, packet->event_ts, packet->buf); + break; + default: + g_assert_not_reached (); + break; + } + g_assert (packet->event == NULL); + } else if (packet->event) gst_dvd_spu_handle_dvd_event (dvdspu, packet->event); g_free (packet); continue; } - - GST_DEBUG_OBJECT (dvdspu, - "Advancing SPU from TS %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT, - GST_TIME_ARGS (state->next_ts), GST_TIME_ARGS (new_ts)); - - /* If we get here, we have an SPU buffer, and it's time to process the - * next cmd */ - g_assert (state->buf != NULL); - - GST_DEBUG_OBJECT (dvdspu, "Executing cmd blk with TS %" GST_TIME_FORMAT - " @ offset %u", GST_TIME_ARGS (state->next_ts), state->cur_cmd_blk); - - start = GST_BUFFER_DATA (state->buf); - end = start + GST_BUFFER_SIZE (state->buf); - - cmd_blk = start + state->cur_cmd_blk; - - if (G_UNLIKELY (cmd_blk + 5 >= end)) { - /* Invalid. Finish the buffer and loop again */ - gst_dvd_spu_finish_spu_buf (dvdspu); - continue; - } - - gst_dvd_spu_exec_cmd_blk (dvdspu, cmd_blk + 4, end); - - next_blk = GST_READ_UINT16_BE (cmd_blk + 2); - if (next_blk != state->cur_cmd_blk) { - /* Advance to the next block of commands */ - gst_dvd_spu_setup_cmd_blk (dvdspu, next_blk, start, end); - } else { - /* Next Block points to the current block, so we're finished with this - * SPU buffer */ - gst_dvd_spu_finish_spu_buf (dvdspu); - } } } @@ -1152,10 +835,66 @@ gst_dvd_spu_check_still_updates (GstDVDSpu * dvdspu) } } +static void +submit_new_spu_packet (GstDVDSpu * dvdspu, GstBuffer * buf) +{ + SpuPacket *spu_packet; + GstClockTime ts; + GstClockTime run_ts = GST_CLOCK_TIME_NONE; + + GST_DEBUG_OBJECT (dvdspu, + "Complete subpicture buffer of %u bytes with TS %" GST_TIME_FORMAT, + GST_BUFFER_SIZE (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); + + /* Decide whether to pass this buffer through to the rendering code */ + ts = GST_BUFFER_TIMESTAMP (buf); + if (GST_CLOCK_TIME_IS_VALID (ts)) { + if (ts < (GstClockTime) dvdspu->subp_seg.start) { + GstClockTimeDiff diff = dvdspu->subp_seg.start - ts; + + /* Buffer starts before segment, see if we can calculate a running time */ + run_ts = + gst_segment_to_running_time (&dvdspu->subp_seg, GST_FORMAT_TIME, + dvdspu->subp_seg.start); + if (run_ts >= (GstClockTime) diff) + run_ts -= diff; + else + run_ts = GST_CLOCK_TIME_NONE; /* No running time possible for this subpic */ + } else { + /* TS within segment, convert to running time */ + run_ts = + gst_segment_to_running_time (&dvdspu->subp_seg, GST_FORMAT_TIME, ts); + } + } + + if (GST_CLOCK_TIME_IS_VALID (run_ts)) { + /* Complete SPU packet, push it onto the queue for processing when + * video packets come past */ + spu_packet = g_new0 (SpuPacket, 1); + spu_packet->buf = buf; + + /* Store the activation time of this buffer in running time */ + spu_packet->event_ts = run_ts; + GST_INFO_OBJECT (dvdspu, + "Pushing SPU buf with TS %" GST_TIME_FORMAT " running time %" + GST_TIME_FORMAT, GST_TIME_ARGS (ts), + GST_TIME_ARGS (spu_packet->event_ts)); + + g_queue_push_tail (dvdspu->pending_spus, spu_packet); + + /* In a still frame condition, advance the SPU to make sure the state is + * up to date */ + gst_dvd_spu_check_still_updates (dvdspu); + } else { + gst_buffer_unref (buf); + } +} + static GstFlowReturn gst_dvd_spu_subpic_chain (GstPad * pad, GstBuffer * buf) { GstDVDSpu *dvdspu = (GstDVDSpu *) (gst_object_get_parent (GST_OBJECT (pad))); + GstFlowReturn ret = GST_FLOW_OK; g_return_val_if_fail (dvdspu != NULL, GST_FLOW_ERROR); @@ -1170,7 +909,15 @@ gst_dvd_spu_subpic_chain (GstPad * pad, GstBuffer * buf) GST_BUFFER_TIMESTAMP (buf)); } + if (GST_BUFFER_IS_DISCONT (buf) && dvdspu->partial_spu) { + gst_buffer_unref (dvdspu->partial_spu); + dvdspu->partial_spu = NULL; + } + if (dvdspu->partial_spu != NULL) { + if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) + GST_WARNING_OBJECT (dvdspu, + "Joining subpicture buffer with timestamp to previous"); dvdspu->partial_spu = gst_buffer_join (dvdspu->partial_spu, buf); } else { /* If we don't yet have a buffer, wait for one with a timestamp, @@ -1181,85 +928,88 @@ gst_dvd_spu_subpic_chain (GstPad * pad, GstBuffer * buf) gst_buffer_unref (buf); } - if (dvdspu->partial_spu != NULL && GST_BUFFER_SIZE (dvdspu->partial_spu) > 4) { - guint16 packet_size; - guint8 *data; - - data = GST_BUFFER_DATA (dvdspu->partial_spu); - packet_size = GST_READ_UINT16_BE (data); - - if (packet_size == GST_BUFFER_SIZE (dvdspu->partial_spu)) { - SpuPacket *spu_packet; - GstClockTime ts; - GstClockTime run_ts = GST_CLOCK_TIME_NONE; - - GST_DEBUG_OBJECT (dvdspu, - "Complete subpicture buffer of %u bytes with TS %" GST_TIME_FORMAT, - GST_BUFFER_SIZE (dvdspu->partial_spu), - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (dvdspu->partial_spu))); - - /* Decide whether to pass this buffer through to the rendering code */ - ts = GST_BUFFER_TIMESTAMP (dvdspu->partial_spu); - if (GST_CLOCK_TIME_IS_VALID (ts)) { - if (ts < (GstClockTime) dvdspu->subp_seg.start) { - GstClockTimeDiff diff = dvdspu->subp_seg.start - ts; - - /* Buffer starts before segment, see if we can calculate a running time */ - run_ts = - gst_segment_to_running_time (&dvdspu->subp_seg, GST_FORMAT_TIME, - dvdspu->subp_seg.start); - if (run_ts >= (GstClockTime) diff) - run_ts -= diff; - else - run_ts = GST_CLOCK_TIME_NONE; /* No running time possible for this subpic */ + if (dvdspu->partial_spu == NULL) + goto done; + + switch (dvdspu->spu_input_type) { + case SPU_INPUT_TYPE_VOBSUB: + if (GST_BUFFER_SIZE (dvdspu->partial_spu) > 4) { + guint16 packet_size; + guint8 *data; + + data = GST_BUFFER_DATA (dvdspu->partial_spu); + packet_size = GST_READ_UINT16_BE (data); + + if (packet_size == GST_BUFFER_SIZE (dvdspu->partial_spu)) { + submit_new_spu_packet (dvdspu, dvdspu->partial_spu); + dvdspu->partial_spu = NULL; + } else if (packet_size < GST_BUFFER_SIZE (dvdspu->partial_spu)) { + /* Somehow we collected too much - something is wrong. Drop the + * packet entirely and wait for a new one */ + GST_DEBUG_OBJECT (dvdspu, "Discarding invalid SPU buffer of size %u", + GST_BUFFER_SIZE (dvdspu->partial_spu)); + + gst_buffer_unref (dvdspu->partial_spu); + dvdspu->partial_spu = NULL; } else { - /* TS within segment, convert to running time */ - run_ts = - gst_segment_to_running_time (&dvdspu->subp_seg, GST_FORMAT_TIME, - ts); + GST_LOG_OBJECT (dvdspu, + "SPU buffer claims to be of size %u. Collected %u so far.", + packet_size, GST_BUFFER_SIZE (dvdspu->partial_spu)); + } + } + break; + case SPU_INPUT_TYPE_PGS:{ + /* Collect until we have a command buffer that ends exactly at the size + * we've collected */ + guint8 packet_type; + guint16 packet_size; + guint8 *data = GST_BUFFER_DATA (dvdspu->partial_spu); + guint8 *end = data + GST_BUFFER_SIZE (dvdspu->partial_spu); + + /* FIXME: There's no need to walk the command set each time. We can set a + * marker and resume where we left off next time */ + /* FIXME: Move the packet parsing and sanity checking into the format-specific modules */ + while (data != end) { + if (data + 3 > end) + break; + packet_type = *data++; + packet_size = GST_READ_UINT16_BE (data); + data += 2; + if (data + packet_size > end) + break; + data += packet_size; + /* 0x80 is the END command for PGS packets */ + if (packet_type == 0x80 && data != end) { + /* Extra cruft on the end of the packet -> assume invalid */ + gst_buffer_unref (dvdspu->partial_spu); + dvdspu->partial_spu = NULL; + break; } } - if (GST_CLOCK_TIME_IS_VALID (run_ts)) { - /* Complete SPU packet, push it onto the queue for processing when - * video packets come past */ - spu_packet = g_new0 (SpuPacket, 1); - spu_packet->buf = dvdspu->partial_spu; - - /* Store the activation time of this buffer in running time */ - spu_packet->event_ts = - gst_segment_to_running_time (&dvdspu->subp_seg, GST_FORMAT_TIME, - ts); - GST_INFO_OBJECT (dvdspu, - "Pushing SPU buf with TS %" GST_TIME_FORMAT " running time %" - GST_TIME_FORMAT, GST_TIME_ARGS (ts), - GST_TIME_ARGS (spu_packet->event_ts)); - - g_queue_push_tail (dvdspu->pending_spus, spu_packet); - dvdspu->partial_spu = NULL; - - /* In a still frame condition, advance the SPU to make sure the state is - * up to date */ - gst_dvd_spu_check_still_updates (dvdspu); - } else { - gst_buffer_unref (dvdspu->partial_spu); + if (dvdspu->partial_spu && data == end) { + GST_DEBUG_OBJECT (dvdspu, + "Have complete PGS packet of size %u. Enqueueing.", + GST_BUFFER_SIZE (dvdspu->partial_spu)); + submit_new_spu_packet (dvdspu, dvdspu->partial_spu); dvdspu->partial_spu = NULL; } - } else if (packet_size < GST_BUFFER_SIZE (dvdspu->partial_spu)) { - /* Somehow we collected too much - something is wrong. Drop the - * packet entirely and wait for a new one */ - GST_DEBUG_OBJECT (dvdspu, "Discarding invalid SPU buffer of size %u", - GST_BUFFER_SIZE (dvdspu->partial_spu)); - - gst_buffer_unref (dvdspu->partial_spu); - dvdspu->partial_spu = NULL; + break; } + default: + GST_ERROR_OBJECT (dvdspu, "Input type not configured before SPU passing"); + goto caps_not_set; } +done: DVD_SPU_UNLOCK (dvdspu); - gst_object_unref (dvdspu); - return GST_FLOW_OK; + return ret; +caps_not_set: + GST_ELEMENT_ERROR (dvdspu, RESOURCE, NO_SPACE_LEFT, + (_("Subpicture format was not configured before data flow")), (NULL)); + ret = GST_FLOW_ERROR; + goto done; } static gboolean @@ -1287,7 +1037,8 @@ gst_dvd_spu_subpic_event (GstPad * pad, GstEvent * event) DVD_SPU_LOCK (dvdspu); if (GST_EVENT_IS_SERIALIZED (event)) { SpuPacket *spu_packet = g_new0 (SpuPacket, 1); - + GST_DEBUG_OBJECT (dvdspu, + "Enqueueing DVD event on subpicture pad for later"); spu_packet->event = event; g_queue_push_tail (dvdspu->pending_spus, spu_packet); } else { @@ -1348,8 +1099,11 @@ gst_dvd_spu_subpic_event (GstPad * pad, GstEvent * event) GST_TIME_ARGS (stop), GST_TIME_ARGS (time)); DVD_SPU_LOCK (dvdspu); + gst_segment_set_newsegment_full (&dvdspu->subp_seg, update, rate, arate, format, start, stop, time); + GST_LOG_OBJECT (dvdspu, "Subpicture segment now: %" GST_SEGMENT_FORMAT, + &dvdspu->subp_seg); DVD_SPU_UNLOCK (dvdspu); gst_event_unref (event); @@ -1359,8 +1113,10 @@ gst_dvd_spu_subpic_event (GstPad * pad, GstEvent * event) gst_event_unref (event); goto done; case GST_EVENT_FLUSH_STOP: + GST_DEBUG_OBJECT (dvdspu, "Have flush-stop event on SPU pad"); DVD_SPU_LOCK (dvdspu); - gst_dvd_spu_flush_spu_info (dvdspu); + gst_segment_init (&dvdspu->subp_seg, GST_FORMAT_UNDEFINED); + gst_dvd_spu_flush_spu_info (dvdspu, TRUE); DVD_SPU_UNLOCK (dvdspu); /* We don't forward flushes on the spu pad */ @@ -1383,6 +1139,39 @@ done: return res; } +static gboolean +gst_dvd_spu_subpic_set_caps (GstPad * pad, GstCaps * caps) +{ + GstDVDSpu *dvdspu = GST_DVD_SPU (gst_pad_get_parent (pad)); + gboolean res = FALSE; + GstStructure *s; + SpuInputType input_type; + + s = gst_caps_get_structure (caps, 0); + + if (gst_structure_has_name (s, "video/x-dvd-subpicture")) { + input_type = SPU_INPUT_TYPE_VOBSUB; + } else if (gst_structure_has_name (s, "subpicture/x-pgs")) { + input_type = SPU_INPUT_TYPE_PGS; + } else { + goto done; + } + + DVD_SPU_LOCK (dvdspu); + if (dvdspu->spu_input_type != input_type) { + GST_INFO_OBJECT (dvdspu, "Incoming SPU packet type changed to %u", + input_type); + dvdspu->spu_input_type = input_type; + gst_dvd_spu_flush_spu_info (dvdspu, TRUE); + } + + DVD_SPU_UNLOCK (dvdspu); + res = TRUE; +done: + gst_object_unref (dvdspu); + return res; +} + static GstStateChangeReturn gst_dvd_spu_change_state (GstElement * element, GstStateChange transition) { @@ -1407,8 +1196,8 @@ gst_dvd_spu_change_state (GstElement * element, GstStateChange transition) gboolean gst_dvd_spu_plugin_init (GstPlugin * plugin) { - GST_DEBUG_CATEGORY_INIT (dvdspu_debug, "gstdvdspu", - 0, "DVD Sub-picture Overlay decoder/renderer"); + GST_DEBUG_CATEGORY_INIT (dvdspu_debug, "gstspu", + 0, "Sub-picture Overlay decoder/renderer"); return gst_element_register (plugin, "dvdspu", GST_RANK_NONE, GST_TYPE_DVD_SPU); diff --git a/gst/dvdspu/gstdvdspu.h b/gst/dvdspu/gstdvdspu.h index dfc51f9e..22b48d1d 100644 --- a/gst/dvdspu/gstdvdspu.h +++ b/gst/dvdspu/gstdvdspu.h @@ -16,11 +16,15 @@ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ -#ifndef __DVD_SPU_H__ -#define __DVD_SPU_H__ +#ifndef __GST_DVD_SPU_H__ +#define __GST_DVD_SPU_H__ #include <gst/gst.h> +#include "gstspu-common.h" +#include "gstspu-vobsub.h" +#include "gstspu-pgs.h" + G_BEGIN_DECLS #define GST_TYPE_DVD_SPU \ @@ -37,63 +41,16 @@ G_BEGIN_DECLS #define DVD_SPU_LOCK(s) g_mutex_lock ((s)->spu_lock); #define DVD_SPU_UNLOCK(s) g_mutex_unlock ((s)->spu_lock); -typedef struct _GstDVDSpu GstDVDSpu; typedef struct _GstDVDSpuClass GstDVDSpuClass; -typedef struct SpuRect SpuRect; -typedef struct SpuPixCtrlI SpuPixCtrlI; -typedef struct SpuLineCtrlI SpuLineCtrlI; -typedef struct SpuColour SpuColour; typedef enum SpuStateFlags SpuStateFlags; -typedef struct SpuState SpuState; +typedef enum SpuInputType SpuInputType; typedef struct SpuPacket SpuPacket; -typedef enum SpuCmd SpuCmd; - -/* Describe the limits of a rectangle */ -struct SpuRect { - gint16 left; - gint16 top; - gint16 right; - gint16 bottom; -}; - -/* Store a pre-multiplied colour value. The YUV fields hold the YUV values - * multiplied by the 8-bit alpha, to save computing it while rendering */ -struct SpuColour { - guint16 Y; - guint16 U; - guint16 V; - guint8 A; -}; - -/* Pixel Control Info from a Change Color Contrast command */ -struct SpuPixCtrlI { - gint16 left; - guint32 palette; - - /* Pre-multiplied palette values, updated as - * needed */ - SpuColour pal_cache[4]; -}; - -struct SpuLineCtrlI { - guint8 n_changes; /* 1 to 8 */ - SpuPixCtrlI pix_ctrl_i[8]; - - gint16 top; - gint16 bottom; -}; -enum SpuCmd { - SPU_CMD_FSTA_DSP = 0x00, /* Forced Display */ - SPU_CMD_DSP = 0x01, /* Display Start */ - SPU_CMD_STP_DSP = 0x02, /* Display Off */ - SPU_CMD_SET_COLOR = 0x03, /* Set the color indexes for the palette */ - SPU_CMD_SET_ALPHA = 0x04, /* Set the alpha indexes for the palette */ - SPU_CMD_SET_DAREA = 0x05, /* Set the display area for the SPU */ - SPU_CMD_DSPXA = 0x06, /* Pixel data addresses */ - SPU_CMD_CHG_COLCON = 0x07, /* Change Color & Contrast */ - SPU_CMD_END = 0xff +enum SpuInputType { + SPU_INPUT_TYPE_NONE = 0x00, + SPU_INPUT_TYPE_VOBSUB = 0x01, + SPU_INPUT_TYPE_PGS = 0x02 }; enum SpuStateFlags { @@ -110,69 +67,19 @@ enum SpuStateFlags { struct SpuState { GstClockTime next_ts; /* Next event TS in running time */ - - GstClockTime base_ts; /* base TS for cmd blk delays in running time */ - GstBuffer *buf; /* Current SPU packet we're executing commands from */ - guint16 cur_cmd_blk; /* Offset into the buf for the current cmd block */ - SpuStateFlags flags; - - /* Top + Bottom field offsets in the buffer. 0 = not set */ - guint16 pix_data[2]; - GstBuffer *pix_buf; /* Current SPU packet the pix_data references */ - - SpuRect disp_rect; - SpuRect clip_rect; - SpuRect hl_rect; - - guint32 current_clut[16]; /* Colour lookup table from incoming events */ - - guint8 main_idx[4]; /* Indices for current main palette */ - guint8 main_alpha[4]; /* Alpha values for main palette */ - - guint8 hl_idx[4]; /* Indices for current highlight palette */ - guint8 hl_alpha[4]; /* Alpha values for highlight palette */ - - /* Pre-multiplied colour palette for the main palette */ - SpuColour main_pal[4]; - gboolean main_pal_dirty; - - /* Line control info for rendering the highlight palette */ - SpuLineCtrlI hl_ctrl_i; - gboolean hl_pal_dirty; /* Indicates that the HL palette info needs refreshing */ - - /* LineCtrlI Info from a Change Color & Contrast command */ - SpuLineCtrlI *line_ctrl_i; - gint16 n_line_ctrl_i; - gboolean line_ctrl_i_pal_dirty; /* Indicates that the palettes for the line_ctrl_i - * need recalculating */ - - /* Rendering state vars below */ - guint16 *comp_bufs[3]; /* Compositing buffers for U+V & A */ - gint16 comp_last_x[2]; /* Maximum X values we rendered into the comp buffer (odd & even) */ - gint16 *comp_last_x_ptr; /* Ptr to the current comp_last_x value to be updated by the render */ + + gint fps_n, fps_d; gint16 vid_width, vid_height; gint16 Y_stride, UV_stride; gint16 Y_height, UV_height; - gint fps_n, fps_d; - - /* Current Y Position */ - gint16 cur_Y; - - /* Current offset in nibbles into the pix_data */ - guint16 cur_offsets[2]; - guint16 max_offset; - - /* current ChgColCon Line Info */ - SpuLineCtrlI *cur_chg_col; - SpuLineCtrlI *cur_chg_col_end; + guint32 *comp_bufs[3]; /* Compositing buffers for U+V & A */ + guint16 comp_left; + guint16 comp_right; - /* Output position tracking */ - guint8 *out_Y; - guint16 *out_U; - guint16 *out_V; - guint16 *out_A; + SpuVobsubState vobsub; + SpuPgsState pgs; }; /* Structure used to store the queue of pending SPU packets. The start_ts is @@ -198,6 +105,7 @@ struct _GstDVDSpu { GstSegment subp_seg; SpuState spu_state; + SpuInputType spu_input_type; /* GQueue of SpuBuf structures */ GQueue *pending_spus; @@ -221,4 +129,4 @@ GType gst_dvd_spu_get_type (void); G_END_DECLS -#endif /* __DVD_SPU_H__ */ +#endif /* __GST_DVD_SPU_H__ */ diff --git a/gst/dvdspu/gstspu-common.h b/gst/dvdspu/gstspu-common.h new file mode 100644 index 00000000..206e8820 --- /dev/null +++ b/gst/dvdspu/gstspu-common.h @@ -0,0 +1,56 @@ +/* GStreamer DVD Sub-Picture Unit + * Copyright (C) 2007 Fluendo S.A. <info@fluendo.com> + * + * 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 __GSTSPU_COMMON_H__ +#define __GSTSPU_COMMON_H__ + +#include <glib.h> + +G_BEGIN_DECLS + +/* FIXME: Move this back to gstdvdspu.h when the renderers no longer use it: */ +typedef struct _GstDVDSpu GstDVDSpu; + +typedef struct SpuState SpuState; +typedef struct SpuColour SpuColour; +typedef struct SpuRect SpuRect; + +/* Describe the limits of a rectangle */ +struct SpuRect { + gint16 left; + gint16 top; + gint16 right; + gint16 bottom; +}; + +/* Store a pre-multiplied colour value. The YUV fields hold the YUV values + * multiplied by the 8-bit alpha, to save computing it while rendering */ +struct SpuColour { + guint16 Y; + guint16 U; + guint16 V; + guint8 A; +}; + +void gstspu_clear_comp_buffers (SpuState * state); +void gstspu_blend_comp_buffers (SpuState * state, guint8 * planes[3]); + + +G_END_DECLS + +#endif /* __GSTSPU_COMMON_H__ */ diff --git a/gst/dvdspu/gstspu-pgs.c b/gst/dvdspu/gstspu-pgs.c new file mode 100644 index 00000000..d1d4de18 --- /dev/null +++ b/gst/dvdspu/gstspu-pgs.c @@ -0,0 +1,762 @@ +/* GStreamer Sub-Picture Unit - PGS handling + * Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net> + * + * 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 <string.h> +#include <gst/gst.h> + +#include "gstdvdspu.h" +#include "gstspu-pgs.h" + +const struct PgsFrameRateEntry +{ + guint8 id; + guint fps_n; + guint fps_d; +} PgsFrameRates[] = { + { + 64, 30000, 1001} /* 29.97 FPS */ +}; + +typedef enum PgsCommandType PgsCommandType; + +enum PgsCommandType +{ + PGS_COMMAND_SET_PALETTE = 0x14, + PGS_COMMAND_SET_OBJECT_DATA = 0x15, + PGS_COMMAND_PRESENTATION_SEGMENT = 0x16, + PGS_COMMAND_SET_WINDOW = 0x17, + PGS_COMMAND_INTERACTIVE_SEGMENT = 0x18, + + PGS_COMMAND_END_DISPLAY = 0x80, + + PGS_COMMAND_INVALID = 0xFFFF +}; + +static gint gstspu_exec_pgs_buffer (GstDVDSpu * dvdspu, GstBuffer * buf); + +#define DUMP_CMDS 0 +#define DUMP_FULL_IMAGE 0 +#define DUMP_FULL_PALETTE 0 + +#if DUMP_CMDS +#define PGS_DUMP(...) g_print(__VA_ARGS__) +#else +#define PGS_DUMP(...) +#endif + +static void +dump_bytes (guint8 * data, guint16 len) +{ + gint i; + + /* Dump the numbers */ + for (i = 0; i < len; i++) { + PGS_DUMP ("0x%02x ", data[i]); + if (!((i + 1) % 16)) + PGS_DUMP ("\n"); + } + if (len > 0 && (i % 16)) + PGS_DUMP ("\n"); +} + +static void +dump_rle_data (GstDVDSpu * dvdspu, guint8 * data, guint32 len) +{ + guint8 *end = data + len; + guint16 obj_w, obj_h; + guint x = 0; + + if (data + 4 > end) + return; + + /* RLE data: */ + obj_w = GST_READ_UINT16_BE (data); + obj_h = GST_READ_UINT16_BE (data + 2); + data += 4; + PGS_DUMP ("RLE image is %ux%u\n", obj_w, obj_h); + + while (data < end) { + guint8 pal_id; + guint16 run_len; + + pal_id = *data++; + if (pal_id != 0) { + // PGS_DUMP ("data 0x%02x\n", data[0]); + run_len = 1; + } else { + if (data + 1 > end) + return; + switch (data[0] & 0xC0) { + case 0x00: + //PGS_DUMP ("data 0x%02x\n", data[0]); + run_len = (data[0] & 0x3f); + data++; + break; + case 0x40: + if (data + 2 > end) + return; + //PGS_DUMP ("data 0x%02x 0x%02x\n", data[0], data[1]); + run_len = ((data[0] << 8) | data[1]) & 0x3fff; + data += 2; + break; + case 0x80: + if (data + 2 > end) + return; + //PGS_DUMP ("data 0x%02x 0x%02x\n", data[0], data[1]); + run_len = (data[0] & 0x3f); + pal_id = data[1]; + data += 2; + break; + case 0xC0: + if (data + 3 > end) + return; + //PGS_DUMP ("data 0x%02x 0x%02x 0x%02x\n", data[0], data[1], data[2]); + run_len = ((data[0] << 8) | data[1]) & 0x3fff; + pal_id = data[2]; + data += 3; + break; + } + } + +#if DUMP_FULL_IMAGE + { + gint i; +#if 1 + if (dvdspu->spu_state.pgs.palette[pal_id].A) { + guint8 val = dvdspu->spu_state.pgs.palette[pal_id].A; + for (i = 0; i < run_len; i++) + PGS_DUMP ("%02x ", val); + } else { + for (i = 0; i < run_len; i++) + PGS_DUMP (" "); + } + if (!run_len || (x + run_len) > obj_w) + PGS_DUMP ("\n"); +#else + PGS_DUMP ("Run x: %d pix: %d col: %d\n", x, run_len, pal_id); +#endif + } +#endif + + x += run_len; + if (!run_len || x > obj_w) + x = 0; + }; + + PGS_DUMP ("\n"); +} + +static void +pgs_composition_object_render (PgsCompositionObject * obj, SpuState * state, + GstBuffer * dest_buf) +{ + SpuColour *colour; + guint8 *planes[3]; /* YUV frame pointers */ + guint8 *data, *end; + guint16 obj_w, obj_h; + guint x, y, i, max_x; + + if (G_UNLIKELY (obj->rle_data == NULL || obj->rle_data_size == 0 + || obj->rle_data_used != obj->rle_data_size)) + return; + + data = obj->rle_data; + end = data + obj->rle_data_used; + + if (data + 4 > end) + return; + + /* FIXME: Calculate and use the cropping window for the output, as the + * intersection of the crop rectangle for this object (if any) and the + * window specified by the object's window_id */ + + /* Store the start of each plane */ + planes[0] = GST_BUFFER_DATA (dest_buf); + planes[1] = planes[0] + (state->Y_height * state->Y_stride); + planes[2] = planes[1] + (state->UV_height * state->UV_stride); + + /* Sanity check */ + g_return_if_fail (planes[2] + (state->UV_height * state->UV_stride) <= + GST_BUFFER_DATA (dest_buf) + GST_BUFFER_SIZE (dest_buf)); + + x = obj->x; + y = obj->y; + + planes[0] += state->Y_stride * y; + planes[1] += state->UV_stride * (y / 2); + planes[2] += state->UV_stride * (y / 2); + + /* RLE data: */ + obj_w = GST_READ_UINT16_BE (data); + obj_h = GST_READ_UINT16_BE (data + 2); + data += 4; + + max_x = x + obj_w; + + state->comp_left = x; + state->comp_right = max_x; + + gstspu_clear_comp_buffers (state); + + while (data < end) { + guint8 pal_id; + guint16 run_len; + + pal_id = *data++; + if (pal_id != 0) { + run_len = 1; + } else { + if (data + 1 > end) + return; + switch (data[0] & 0xC0) { + case 0x00: + run_len = (data[0] & 0x3f); + data++; + break; + case 0x40: + if (data + 2 > end) + return; + run_len = ((data[0] << 8) | data[1]) & 0x3fff; + data += 2; + break; + case 0x80: + if (data + 2 > end) + return; + run_len = (data[0] & 0x3f); + pal_id = data[1]; + data += 2; + break; + case 0xC0: + if (data + 3 > end) + return; + run_len = ((data[0] << 8) | data[1]) & 0x3fff; + pal_id = data[2]; + data += 3; + break; + } + } + + colour = &state->pgs.palette[pal_id]; + if (colour->A) { + guint32 inv_A = 0xff - colour->A; + + for (i = 0; i < run_len; i++) { + planes[0][x] = (inv_A * planes[0][x] + colour->Y) / 0xff; + + state->comp_bufs[0][x / 2] += colour->U; + state->comp_bufs[1][x / 2] += colour->V; + state->comp_bufs[2][x / 2] += colour->A; + x++; + } + } else { + x += run_len; + } + + if (!run_len || x > max_x) { + x = state->pgs.win_x; + planes[0] += state->Y_stride; + + if (y % 2) { + gstspu_blend_comp_buffers (state, planes); + gstspu_clear_comp_buffers (state); + + planes[1] += state->UV_stride; + planes[2] += state->UV_stride; + } + y++; + } + } + + if (y % 2) + gstspu_blend_comp_buffers (state, planes); +} + +static void +pgs_composition_object_clear (PgsCompositionObject * obj) +{ + if (obj->rle_data) { + g_free (obj->rle_data); + obj->rle_data = NULL; + } + obj->rle_data_size = obj->rle_data_used = 0; +} + +static void +pgs_presentation_segment_set_object_count (PgsPresentationSegment * ps, + guint8 n_objects) +{ + if (ps->objects == NULL) { + ps->objects = + g_array_sized_new (FALSE, TRUE, sizeof (PgsCompositionObject), + n_objects); + g_array_set_size (ps->objects, n_objects); + return; + } + + /* Clear memory in any extraneous objects */ + if (ps->objects->len > n_objects) { + guint i; + for (i = n_objects; i < ps->objects->len; i++) { + PgsCompositionObject *cur = + &g_array_index (ps->objects, PgsCompositionObject, i); + pgs_composition_object_clear (cur); + } + } + + g_array_set_size (ps->objects, n_objects); + + if (n_objects == 0) { + g_array_free (ps->objects, TRUE); + ps->objects = NULL; + } +} + +static PgsCompositionObject * +pgs_presentation_segment_find_object (PgsPresentationSegment * ps, + guint16 obj_id) +{ + guint i; + if (ps->objects == NULL) + return NULL; + + for (i = 0; i < ps->objects->len; i++) { + PgsCompositionObject *cur = + &g_array_index (ps->objects, PgsCompositionObject, i); + if (cur->id == obj_id) + return cur; + } + + return NULL; +} + +static int +parse_presentation_segment (GstDVDSpu * dvdspu, guint8 type, guint8 * payload, + guint16 len) +{ + guint8 *end = payload + len; + PgsPresentationSegment *ps = &dvdspu->spu_state.pgs.pres_seg; + guint8 n_objects, palette_id; + gint i; + + /* Parse video descriptor */ + if (payload + 5 > end) + return 0; + + ps->vid_w = GST_READ_UINT16_BE (payload); + ps->vid_h = GST_READ_UINT16_BE (payload + 2); + ps->vid_fps_code = payload[4]; + payload += 5; + + /* Parse composition descriptor */ + if (payload + 3 > end) + return 0; + ps->composition_no = GST_READ_UINT16_BE (payload); + ps->composition_state = payload[2]; + payload += 3; + + /* Parse other bits */ + if (payload + 3 > end) + return 0; + + ps->flags = payload[0]; + + palette_id = payload[1]; + n_objects = payload[2]; + payload += 3; + + if (ps->flags & PGS_PRES_SEGMENT_FLAG_UPDATE_PALETTE) + ps->palette_id = palette_id; + + PGS_DUMP ("Video width %u height %u fps code %u\n", ps->vid_w, ps->vid_h, + ps->vid_fps_code); + PGS_DUMP + ("Composition num %u state 0x%02x flags 0x%02x palette id %u n_objects %u\n", + ps->composition_no, ps->composition_state, ps->flags, ps->palette_id, + n_objects); + + pgs_presentation_segment_set_object_count (ps, n_objects); + + for (i = 0; i < (gint) n_objects; i++) { + PgsCompositionObject *obj = + &g_array_index (ps->objects, PgsCompositionObject, i); + + if (payload + 8 > end) + break; + obj->id = GST_READ_UINT16_BE (payload); + obj->win_id = payload[2]; + obj->flags = payload[3]; + obj->x = GST_READ_UINT16_BE (payload + 4); + obj->y = GST_READ_UINT16_BE (payload + 6); + obj->rle_data_size = obj->rle_data_used = 0; + + payload += 8; + + PGS_DUMP ("Composition object %d Object ID %u Window ID %u flags 0x%02x " + "x %u y %u\n", i, obj->id, obj->win_id, obj->flags, obj->x, obj->y); + + if (obj->flags & PGS_COMPOSITION_OBJECT_FLAG_CROPPED) { + if (payload + 8 > end) + break; + + obj->crop_x = GST_READ_UINT16_BE (payload); + obj->crop_y = GST_READ_UINT16_BE (payload + 2); + obj->crop_w = GST_READ_UINT16_BE (payload + 4); + obj->crop_h = GST_READ_UINT16_BE (payload + 6); + + payload += 8; + + PGS_DUMP ("Cropping window x %u y %u w %u h %u\n", + obj->crop_x, obj->crop_y, obj->crop_w, obj->crop_h); + } + + if (obj->flags & ~(PGS_COMPOSITION_OBJECT_FLAG_CROPPED | + PGS_COMPOSITION_OBJECT_FLAG_FORCED)) + g_warning ("PGS Composition Object has unknown flags: 0x%02x", + obj->flags); + } + + if (payload != end) { + g_warning ("PGS Composition Object: %ld bytes not consumed", end - payload); + dump_bytes (payload, end - payload); + } + + return 0; +} + +static int +parse_set_palette (GstDVDSpu * dvdspu, guint8 type, guint8 * payload, + guint16 len) +{ + SpuState *state = &dvdspu->spu_state; + + const gint PGS_PALETTE_ENTRY_SIZE = 5; + guint8 *end = payload + len; + guint8 palette_id; + guint8 palette_version; + gint n_entries, i; + + if (len < 2) /* Palette command too short */ + return 0; + palette_id = payload[0]; + palette_version = payload[1]; + payload += 2; + + n_entries = (len - 2) / PGS_PALETTE_ENTRY_SIZE; + + PGS_DUMP ("Palette ID %u version %u. %d entries\n", + palette_id, palette_version, n_entries); + for (i = 0; i < 256; i++) + state->pgs.palette[i].A = 0; + for (i = 0; i < n_entries; i++) { + guint8 n, Y, U, V, A; + n = payload[0]; + Y = payload[1]; + U = payload[2]; + V = payload[3]; + A = payload[4]; + +#if DUMP_FULL_PALETTE + PGS_DUMP ("Entry %3d: Y %3d U %3d V %3d A %3d ", n, Y, U, V, A); + if (((i + 1) % 2) == 0) + PGS_DUMP ("\n"); +#endif + + /* Premultiply the palette entries by the alpha */ + state->pgs.palette[n].Y = Y * A; + state->pgs.palette[n].U = U * A; + state->pgs.palette[n].V = V * A; + state->pgs.palette[n].A = A; + + payload += PGS_PALETTE_ENTRY_SIZE; + } + +#if DUMP_FULL_PALETTE + if (n_entries > 0 && (i % 2)) + PGS_DUMP ("\n"); +#endif + + if (payload != end) { + g_warning ("PGS Set Palette: %ld bytes not consumed", end - payload); + dump_bytes (payload, end - payload); + } + + return 0; +} + +static int +parse_set_window (GstDVDSpu * dvdspu, guint8 type, guint8 * payload, + guint16 len) +{ + SpuState *state = &dvdspu->spu_state; + guint8 *end = payload + len; + guint8 win_id, win_ver; + + if (payload + 10 > end) + return 0; + + dump_bytes (payload, len); + + /* FIXME: This is just a guess as to what the numbers mean: */ + win_id = payload[0]; + win_ver = payload[1]; + state->pgs.win_x = GST_READ_UINT16_BE (payload + 2); + state->pgs.win_y = GST_READ_UINT16_BE (payload + 4); + state->pgs.win_w = GST_READ_UINT16_BE (payload + 6); + state->pgs.win_h = GST_READ_UINT16_BE (payload + 8); + payload += 10; + + PGS_DUMP ("Win ID %u version %d x %d y %d w %d h %d\n", + win_id, win_ver, state->pgs.win_x, state->pgs.win_y, state->pgs.win_w, + state->pgs.win_h); + + if (payload != end) { + g_warning ("PGS Set Window: %ld bytes not consumed", end - payload); + dump_bytes (payload, end - payload); + } + + return 0; +} + +static int +parse_set_object_data (GstDVDSpu * dvdspu, guint8 type, guint8 * payload, + guint16 len) +{ + SpuPgsState *pgs_state = &dvdspu->spu_state.pgs; + PgsCompositionObject *obj; + guint8 *end = payload + len; + guint16 obj_id; + guint8 obj_ver, flags; + + if (payload + 4 > end) + return 0; + + obj_id = GST_READ_UINT16_BE (payload); + obj_ver = payload[2]; + flags = payload[3]; + payload += 4; + + obj = pgs_presentation_segment_find_object (&(pgs_state->pres_seg), obj_id); + + PGS_DUMP ("Object ID %d ver %u flags 0x%02x\n", obj_id, obj_ver, flags); + + if (flags & PGS_OBJECT_UPDATE_FLAG_START_RLE) { + obj->rle_data_ver = obj_ver; + + if (payload + 3 > end) + return 0; + + obj->rle_data_size = GST_READ_UINT24_BE (payload); + payload += 3; + + PGS_DUMP ("%d bytes of RLE data, of %d bytes total.\n", + end - payload, obj->rle_data_size); + + obj->rle_data = g_realloc (obj->rle_data, obj->rle_data_size); + obj->rle_data_used = end - payload; + memcpy (obj->rle_data, payload, end - payload); + payload = end; + } else { + PGS_DUMP ("%d bytes of additional RLE data\n", end - payload); + /* Check that the data chunk is for this object version, and fits in the buffer */ + if (obj->rle_data_ver == obj_ver && + obj->rle_data_used + end - payload <= obj->rle_data_size) { + + memcpy (obj->rle_data + obj->rle_data_used, payload, end - payload); + obj->rle_data_used += end - payload; + payload = end; + } + } + + if (obj->rle_data_size == obj->rle_data_used) + dump_rle_data (dvdspu, obj->rle_data, obj->rle_data_size); + + if (payload != end) { + g_warning ("PGS Set Object Data: %ld bytes not consumed", end - payload); + dump_bytes (payload, end - payload); + } + + return 0; +} + +static int +parse_pgs_packet (GstDVDSpu * dvdspu, guint8 type, guint8 * payload, + guint16 len) +{ + SpuPgsState *pgs_state = &dvdspu->spu_state.pgs; + int ret = 0; + + if (!pgs_state->in_presentation_segment + && type != PGS_COMMAND_PRESENTATION_SEGMENT) { + PGS_DUMP ("Expected BEGIN PRESENTATION SEGMENT command. " + "Got command type 0x%02x len %u. Skipping\n", type, len); + return 0; + } + + switch (type) { + case PGS_COMMAND_PRESENTATION_SEGMENT: + PGS_DUMP ("*******************************************\n" + "Begin PRESENTATION_SEGMENT (0x%02x) packet len %u\n", type, len); + pgs_state->in_presentation_segment = + pgs_state->have_presentation_segment = TRUE; + ret = parse_presentation_segment (dvdspu, type, payload, len); + break; + case PGS_COMMAND_SET_OBJECT_DATA: + PGS_DUMP ("*** Set Object Data (0x%02x) packet len %u\n", type, len); + ret = parse_set_object_data (dvdspu, type, payload, len); + break; + case PGS_COMMAND_SET_PALETTE: + PGS_DUMP ("*** Set Palette (0x%02x) packet len %u\n", type, len); + ret = parse_set_palette (dvdspu, type, payload, len); + break; + case PGS_COMMAND_SET_WINDOW: + PGS_DUMP ("*** Set Window command (0x%02x) packet len %u\n", type, len); + ret = parse_set_window (dvdspu, type, payload, len); + break; + case PGS_COMMAND_INTERACTIVE_SEGMENT: + PGS_DUMP ("*** Interactive Segment command(0x%02x) packet len %u\n", + type, len); + dump_bytes (payload, len); + break; + case PGS_COMMAND_END_DISPLAY: + PGS_DUMP ("*** End Display command (0x%02x) packet len %u\n", type, + len); + pgs_state->in_presentation_segment = FALSE; + break; + default: + g_warning ("Unknown PGS command: type 0x%02x len %u", type, len); + dump_bytes (payload, len); + break; + } + PGS_DUMP ("\n"); + + return ret; +} + +gint +gstspu_exec_pgs_buffer (GstDVDSpu * dvdspu, GstBuffer * buf) +{ + guint8 *pos, *end; + guint8 type; + guint16 packet_len; + + pos = GST_BUFFER_DATA (buf); + end = pos + GST_BUFFER_SIZE (buf); + + /* Need at least 3 bytes */ + if (pos + 3 > end) { + PGS_DUMP ("Not enough bytes to be a PGS packet\n"); + return -1; + } + + PGS_DUMP ("Begin dumping command buffer of size %u ts %" GST_TIME_FORMAT "\n", + end - pos, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); + do { + type = *pos++; + packet_len = GST_READ_UINT16_BE (pos); + pos += 2; + + if (pos + packet_len > end) { + PGS_DUMP ("Invalid packet length %u (only have %u bytes)\n", packet_len, + end - pos); + return -1; + } + + if (parse_pgs_packet (dvdspu, type, pos, packet_len)) + return -1; + + pos += packet_len; + } while (pos + 3 <= end); + + PGS_DUMP ("End dumping command buffer with %u bytes remaining\n", end - pos); + return (pos - GST_BUFFER_DATA (buf)); +} + +void +gstspu_pgs_handle_new_buf (GstDVDSpu * dvdspu, GstClockTime event_ts, + GstBuffer * buf) +{ + SpuState *state = &dvdspu->spu_state; + + state->next_ts = event_ts; + state->pgs.pending_cmd = buf; +} + +gboolean +gstspu_pgs_execute_event (GstDVDSpu * dvdspu) +{ + SpuState *state = &dvdspu->spu_state; + + if (state->pgs.pending_cmd) { + gstspu_exec_pgs_buffer (dvdspu, state->pgs.pending_cmd); + gst_buffer_unref (state->pgs.pending_cmd); + state->pgs.pending_cmd = NULL; + } + + state->next_ts = GST_CLOCK_TIME_NONE; + + state->flags &= ~SPU_STATE_DISPLAY; + if (state->pgs.have_presentation_segment) { + if (state->pgs.pres_seg.objects && state->pgs.pres_seg.objects->len > 0) + state->flags |= SPU_STATE_DISPLAY; + } + return FALSE; +} + +void +gstspu_pgs_render (GstDVDSpu * dvdspu, GstBuffer * buf) +{ + SpuState *state = &dvdspu->spu_state; + PgsPresentationSegment *ps = &state->pgs.pres_seg; + guint i; + + if (ps->objects == NULL) + return; + + for (i = 0; i < ps->objects->len; i++) { + PgsCompositionObject *cur = + &g_array_index (ps->objects, PgsCompositionObject, i); + pgs_composition_object_render (cur, state, buf); + } +} + +gboolean +gstspu_pgs_handle_dvd_event (GstDVDSpu * dvdspu, GstEvent * event) +{ + return FALSE; +} + +void +gstspu_pgs_flush (GstDVDSpu * dvdspu) +{ + SpuPgsState *pgs_state = &dvdspu->spu_state.pgs; + + if (pgs_state->pending_cmd) { + gst_buffer_unref (pgs_state->pending_cmd); + pgs_state->pending_cmd = NULL; + } + + pgs_state->have_presentation_segment = FALSE; + pgs_state->in_presentation_segment = FALSE; + pgs_presentation_segment_set_object_count (&pgs_state->pres_seg, 0); + + pgs_state->win_x = pgs_state->win_y = pgs_state->win_w = pgs_state->win_h = 0; +} diff --git a/gst/dvdspu/gstspu-pgs.h b/gst/dvdspu/gstspu-pgs.h new file mode 100644 index 00000000..164f4d81 --- /dev/null +++ b/gst/dvdspu/gstspu-pgs.h @@ -0,0 +1,106 @@ +/* GStreamer Sub-Picture Unit - PGS handling + * Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net> + * + * 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 __GSTSPU_PGS_H__ +#define __GSTSPU_PGS_H__ + +#include "gstspu-common.h" + +typedef struct SpuPgsState SpuPgsState; +typedef enum PgsCompositionObjectFlags PgsCompositionObjectFlags; +typedef enum PgsPresentationSegmentFlags PgsPresentationSegmentFlags; +typedef enum PgsObjectUpdateFlags PgsObjectUpdateFlags; + +typedef struct PgsPresentationSegment PgsPresentationSegment; +typedef struct PgsCompositionObject PgsCompositionObject; + +enum PgsPresentationSegmentFlags +{ + PGS_PRES_SEGMENT_FLAG_UPDATE_PALETTE = 0x80 +}; + +enum PgsCompositionObjectFlags +{ + PGS_COMPOSITION_OBJECT_FLAG_CROPPED = 0x80, + PGS_COMPOSITION_OBJECT_FLAG_FORCED = 0x40 +}; + +enum PgsObjectUpdateFlags +{ + /* Set in an object_update if this is the beginning of new RLE data. + * If not set, the data is a continuation to be appended */ + PGS_OBJECT_UPDATE_FLAG_START_RLE = 0x80, + PGS_OBJECT_UPDATE_FLAG_END_RLE = 0x40 /* This one is a guess */ +}; + +struct PgsPresentationSegment +{ + guint16 composition_no; + guint8 composition_state; + + PgsPresentationSegmentFlags flags; + + guint8 palette_id; + + guint16 vid_w, vid_h; + guint8 vid_fps_code; + + GArray *objects; +}; + +struct PgsCompositionObject +{ + guint16 id; + guint8 version; + PgsCompositionObjectFlags flags; + + guint8 win_id; + + guint8 rle_data_ver; + guint8 *rle_data; + guint32 rle_data_size; + guint32 rle_data_used; + + /* Top left corner of this object */ + guint16 x, y; + + /* Only valid if PGS_COMPOSITION_OBJECT_FLAG_CROPPED is set */ + guint16 crop_x, crop_y, crop_w, crop_h; +}; + +struct SpuPgsState { + GstBuffer *pending_cmd; + + gboolean in_presentation_segment; + gboolean have_presentation_segment; + + PgsPresentationSegment pres_seg; + + SpuColour palette[256]; + + guint16 win_x, win_y, win_w, win_h; +}; + +void gstspu_pgs_handle_new_buf (GstDVDSpu * dvdspu, GstClockTime event_ts, GstBuffer *buf); +gboolean gstspu_pgs_execute_event (GstDVDSpu *dvdspu); +void gstspu_pgs_render (GstDVDSpu *dvdspu, GstBuffer *buf); +gboolean gstspu_pgs_handle_dvd_event (GstDVDSpu *dvdspu, GstEvent *event); +void gstspu_pgs_flush (GstDVDSpu *dvdspu); + +#endif diff --git a/gst/dvdspu/gstspu-vobsub-render.c b/gst/dvdspu/gstspu-vobsub-render.c new file mode 100644 index 00000000..07abff22 --- /dev/null +++ b/gst/dvdspu/gstspu-vobsub-render.c @@ -0,0 +1,536 @@ +/* GStreamer DVD Sub-Picture Unit + * Copyright (C) 2007 Fluendo S.A. <info@fluendo.com> + * Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net> + * + * 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 <string.h> + +#include <gst/gst.h> + +#include "gstdvdspu.h" + +GST_DEBUG_CATEGORY_EXTERN (dvdspu_debug); +#define GST_CAT_DEFAULT dvdspu_debug + +static void +gstspu_vobsub_recalc_palette (GstDVDSpu * dvdspu, + SpuColour * dest, guint8 * idx, guint8 * alpha) +{ + SpuState *state = &dvdspu->spu_state; + gint i; + + for (i = 0; i < 4; i++, dest++) { + guint32 col = state->vobsub.current_clut[idx[i]]; + + /* Convert incoming 4-bit alpha to 8 bit for blending */ + dest->A = (alpha[i] << 4) | alpha[i]; + dest->Y = ((guint16) ((col >> 16) & 0xff)) * dest->A; + /* U/V are stored as V/U in the clut words, so switch them */ + dest->V = ((guint16) ((col >> 8) & 0xff)) * dest->A; + dest->U = ((guint16) (col & 0xff)) * dest->A; + } +} + +/* Recalculate the main, HL & ChgCol palettes */ +static void +gstspu_vobsub_update_palettes (GstDVDSpu * dvdspu, SpuState * state) +{ + guint8 index[4]; /* Indices for the palette */ + guint8 alpha[4]; /* Alpha values the palette */ + + if (state->vobsub.main_pal_dirty) { + gstspu_vobsub_recalc_palette (dvdspu, state->vobsub.main_pal, + state->vobsub.main_idx, state->vobsub.main_alpha); + + /* Need to refresh the hl_ctrl info copies of the main palette too */ + memcpy (state->vobsub.hl_ctrl_i.pix_ctrl_i[0].pal_cache, + state->vobsub.main_pal, 4 * sizeof (SpuColour)); + memcpy (state->vobsub.hl_ctrl_i.pix_ctrl_i[2].pal_cache, + state->vobsub.main_pal, 4 * sizeof (SpuColour)); + + state->vobsub.main_pal_dirty = FALSE; + } + + if (state->vobsub.hl_pal_dirty) { + gstspu_vobsub_recalc_palette (dvdspu, + state->vobsub.hl_ctrl_i.pix_ctrl_i[1].pal_cache, state->vobsub.hl_idx, + state->vobsub.hl_alpha); + state->vobsub.hl_pal_dirty = FALSE; + } + + /* Update the offset positions for the highlight region */ + if (state->vobsub.hl_rect.top != -1) { + state->vobsub.hl_ctrl_i.top = state->vobsub.hl_rect.top; + state->vobsub.hl_ctrl_i.bottom = state->vobsub.hl_rect.bottom; + state->vobsub.hl_ctrl_i.n_changes = 3; + state->vobsub.hl_ctrl_i.pix_ctrl_i[0].left = 0; + state->vobsub.hl_ctrl_i.pix_ctrl_i[1].left = state->vobsub.hl_rect.left; + state->vobsub.hl_ctrl_i.pix_ctrl_i[2].left = + state->vobsub.hl_rect.right + 1; + } + + if (state->vobsub.line_ctrl_i_pal_dirty) { + gint16 l, c; + GST_LOG_OBJECT (dvdspu, "Updating chg-col-con palettes"); + for (l = 0; l < state->vobsub.n_line_ctrl_i; l++) { + SpuVobsubLineCtrlI *cur_line_ctrl = state->vobsub.line_ctrl_i + l; + + for (c = 0; c < cur_line_ctrl->n_changes; c++) { + SpuVobsubPixCtrlI *cur = cur_line_ctrl->pix_ctrl_i + c; + + index[3] = (cur->palette >> 28) & 0x0f; + index[2] = (cur->palette >> 24) & 0x0f; + index[1] = (cur->palette >> 20) & 0x0f; + index[0] = (cur->palette >> 16) & 0x0f; + + alpha[3] = (cur->palette >> 12) & 0x0f; + alpha[2] = (cur->palette >> 8) & 0x0f; + alpha[1] = (cur->palette >> 4) & 0x0f; + alpha[0] = (cur->palette) & 0x0f; + gstspu_vobsub_recalc_palette (dvdspu, cur->pal_cache, index, alpha); + } + } + state->vobsub.line_ctrl_i_pal_dirty = FALSE; + } +} + +static inline guint8 +gstspu_vobsub_get_nibble (SpuState * state, guint16 * rle_offset) +{ + guint8 ret; + + if (G_UNLIKELY (*rle_offset >= state->vobsub.max_offset)) + return 0; /* Overran the buffer */ + + ret = GST_BUFFER_DATA (state->vobsub.pix_buf)[(*rle_offset) / 2]; + + /* If the offset is even, we shift the answer down 4 bits, otherwise not */ + if (*rle_offset & 0x01) + ret &= 0x0f; + else + ret = ret >> 4; + + (*rle_offset)++; + return ret; +} + +static guint16 +gstspu_vobsub_get_rle_code (SpuState * state, guint16 * rle_offset) +{ + guint16 code; + + code = gstspu_vobsub_get_nibble (state, rle_offset); + if (code < 0x4) { /* 4 .. f */ + code = (code << 4) | gstspu_vobsub_get_nibble (state, rle_offset); + if (code < 0x10) { /* 1x .. 3x */ + code = (code << 4) | gstspu_vobsub_get_nibble (state, rle_offset); + if (code < 0x40) { /* 04x .. 0fx */ + code = (code << 4) | gstspu_vobsub_get_nibble (state, rle_offset); + } + } + } + return code; +} + +static inline void +gstspu_vobsub_draw_rle_run (SpuState * state, gint16 x, gint16 end, + SpuColour * colour) +{ +#if 0 + GST_LOG ("Y: %d x: %d end %d col %d %d %d %d", + state->vobsub.cur_Y, x, end, colour->Y, colour->U, colour->V, colour->A); +#endif + + if (colour->A != 0) { + guint32 inv_A = 0xff - colour->A; + + /* FIXME: This could be more efficient */ + while (x < end) { + state->vobsub.out_Y[x] = + (inv_A * state->vobsub.out_Y[x] + colour->Y) / 0xff; + state->vobsub.out_U[x / 2] += colour->U; + state->vobsub.out_V[x / 2] += colour->V; + state->vobsub.out_A[x / 2] += colour->A; + x++; + } + /* Update the compositing buffer so we know how much to blend later */ + *(state->vobsub.comp_last_x_ptr) = end; + } +} + +static inline gint16 +rle_end_x (guint16 rle_code, gint16 x, gint16 end) +{ + /* run length = rle_code >> 2 */ + if (G_UNLIKELY (((rle_code >> 2) == 0))) + return end; + else + return MIN (end, x + (rle_code >> 2)); +} + +static void gstspu_vobsub_render_line_with_chgcol (SpuState * state, + guint8 * planes[3], guint16 * rle_offset); +static gboolean gstspu_vobsub_update_chgcol (SpuState * state); + +static void +gstspu_vobsub_render_line (SpuState * state, guint8 * planes[3], + guint16 * rle_offset) +{ + gint16 x, next_x, end, rle_code; + SpuColour *colour; + + /* Check for special case of chg_col info to use (either highlight or + * ChgCol command */ + if (state->vobsub.cur_chg_col != NULL) { + if (gstspu_vobsub_update_chgcol (state)) { + /* Check the top & bottom, because we might not be within the region yet */ + if (state->vobsub.cur_Y >= state->vobsub.cur_chg_col->top && + state->vobsub.cur_Y <= state->vobsub.cur_chg_col->bottom) { + gstspu_vobsub_render_line_with_chgcol (state, planes, rle_offset); + return; + } + } + } + + /* No special case. Render as normal */ + + /* Set up our output pointers */ + state->vobsub.out_Y = planes[0]; + state->vobsub.out_U = state->comp_bufs[0]; + state->vobsub.out_V = state->comp_bufs[1]; + state->vobsub.out_A = state->comp_bufs[2]; + /* We always need to start our RLE decoding byte_aligned */ + *rle_offset = GST_ROUND_UP_2 (*rle_offset); + + x = state->vobsub.disp_rect.left; + end = state->vobsub.disp_rect.right + 1; + while (x < end) { + rle_code = gstspu_vobsub_get_rle_code (state, rle_offset); + colour = &state->vobsub.main_pal[rle_code & 3]; + next_x = rle_end_x (rle_code, x, end); + /* Now draw the run between [x,next_x) */ + gstspu_vobsub_draw_rle_run (state, x, next_x, colour); + x = next_x; + } +} + +static gboolean +gstspu_vobsub_update_chgcol (SpuState * state) +{ + if (state->vobsub.cur_chg_col == NULL) + return FALSE; + + if (state->vobsub.cur_Y <= state->vobsub.cur_chg_col->bottom) + return TRUE; + + while (state->vobsub.cur_chg_col < state->vobsub.cur_chg_col_end) { + if (state->vobsub.cur_Y >= state->vobsub.cur_chg_col->top && + state->vobsub.cur_Y <= state->vobsub.cur_chg_col->bottom) { +#if 0 + g_print ("Stopped @ entry %d with top %d bottom %d, cur_y %d", + (gint16) (state->vobsub.cur_chg_col - state->vobsub.line_ctrl_i), + state->vobsub.cur_chg_col->top, state->vobsub.cur_chg_col->bottom, y); +#endif + return TRUE; + } + state->vobsub.cur_chg_col++; + } + + /* Finished all our cur_chg_col entries. Use the main palette from here on */ + state->vobsub.cur_chg_col = NULL; + return FALSE; +} + +static void +gstspu_vobsub_render_line_with_chgcol (SpuState * state, guint8 * planes[3], + guint16 * rle_offset) +{ + SpuVobsubLineCtrlI *chg_col = state->vobsub.cur_chg_col; + + gint16 x, next_x, disp_end, rle_code, run_end; + SpuColour *colour; + SpuVobsubPixCtrlI *cur_pix_ctrl; + SpuVobsubPixCtrlI *next_pix_ctrl; + SpuVobsubPixCtrlI *end_pix_ctrl; + SpuVobsubPixCtrlI dummy_pix_ctrl; + gint16 cur_reg_end; + gint i; + + state->vobsub.out_Y = planes[0]; + state->vobsub.out_U = state->comp_bufs[0]; + state->vobsub.out_V = state->comp_bufs[1]; + state->vobsub.out_A = state->comp_bufs[2]; + + /* We always need to start our RLE decoding byte_aligned */ + *rle_offset = GST_ROUND_UP_2 (*rle_offset); + + /* Our run will cover the display rect */ + x = state->vobsub.disp_rect.left; + disp_end = state->vobsub.disp_rect.right + 1; + + /* Work out the first pixel control info, which may point to the dummy entry if + * the global palette/alpha need using initally */ + cur_pix_ctrl = chg_col->pix_ctrl_i; + end_pix_ctrl = chg_col->pix_ctrl_i + chg_col->n_changes; + + if (cur_pix_ctrl->left != 0) { + next_pix_ctrl = cur_pix_ctrl; + cur_pix_ctrl = &dummy_pix_ctrl; + for (i = 0; i < 4; i++) /* Copy the main palette to our dummy entry */ + dummy_pix_ctrl.pal_cache[i] = state->vobsub.main_pal[i]; + } else { + next_pix_ctrl = cur_pix_ctrl + 1; + } + if (next_pix_ctrl < end_pix_ctrl) + cur_reg_end = next_pix_ctrl->left; + else + cur_reg_end = disp_end; + + /* Render stuff */ + while (x < disp_end) { + rle_code = gstspu_vobsub_get_rle_code (state, rle_offset); + next_x = rle_end_x (rle_code, x, disp_end); + + /* Now draw the run between [x,next_x), crossing palette regions as needed */ + while (x < next_x) { + run_end = MIN (next_x, cur_reg_end); + + if (G_LIKELY (x < run_end)) { + colour = &cur_pix_ctrl->pal_cache[rle_code & 3]; + gstspu_vobsub_draw_rle_run (state, x, run_end, colour); + x = run_end; + } + + if (x >= cur_reg_end) { + /* Advance to next region */ + cur_pix_ctrl = next_pix_ctrl; + next_pix_ctrl++; + + if (next_pix_ctrl < end_pix_ctrl) + cur_reg_end = next_pix_ctrl->left; + else + cur_reg_end = disp_end; + } + } + } +} + +static void +gstspu_vobsub_blend_comp_buffers (SpuState * state, guint8 * planes[3]) +{ + state->comp_left = state->vobsub.disp_rect.left; + state->comp_right = + MAX (state->vobsub.comp_last_x[0], state->vobsub.comp_last_x[1]); + + gstspu_blend_comp_buffers (state, planes); +} + +void +gstspu_vobsub_clear_comp_buffers (SpuState * state) +{ + state->comp_left = state->vobsub.disp_rect.left; + state->comp_right = state->vobsub.disp_rect.right; + + gstspu_clear_comp_buffers (state); + + state->vobsub.comp_last_x[0] = -1; + state->vobsub.comp_last_x[1] = -1; +} + +void +gstspu_vobsub_render (GstDVDSpu * dvdspu, GstBuffer * buf) +{ + SpuState *state = &dvdspu->spu_state; + guint8 *planes[3]; /* YUV frame pointers */ + gint y, last_y; + + /* Set up our initial state */ + if (G_UNLIKELY (state->vobsub.pix_buf == NULL)) + return; + + /* Store the start of each plane */ + planes[0] = GST_BUFFER_DATA (buf); + planes[1] = planes[0] + (state->Y_height * state->Y_stride); + planes[2] = planes[1] + (state->UV_height * state->UV_stride); + + /* Sanity check */ + g_return_if_fail (planes[2] + (state->UV_height * state->UV_stride) <= + GST_BUFFER_DATA (buf) + GST_BUFFER_SIZE (buf)); + + GST_DEBUG ("Rendering SPU. disp_rect %d,%d to %d,%d. hl_rect %d,%d to %d,%d", + state->vobsub.disp_rect.left, state->vobsub.disp_rect.top, + state->vobsub.disp_rect.right, state->vobsub.disp_rect.bottom, + state->vobsub.hl_rect.left, state->vobsub.hl_rect.top, + state->vobsub.hl_rect.right, state->vobsub.hl_rect.bottom); + + GST_DEBUG ("vid_disp %d,%d", state->vid_width, state->vid_height); + + /* When reading RLE data, we track the offset in nibbles... */ + state->vobsub.cur_offsets[0] = state->vobsub.pix_data[0] * 2; + state->vobsub.cur_offsets[1] = state->vobsub.pix_data[1] * 2; + state->vobsub.max_offset = GST_BUFFER_SIZE (state->vobsub.pix_buf) * 2; + + /* Update all the palette caches */ + gstspu_vobsub_update_palettes (dvdspu, state); + + /* Set up HL or Change Color & Contrast rect tracking */ + if (state->vobsub.hl_rect.top != -1) { + state->vobsub.cur_chg_col = &state->vobsub.hl_ctrl_i; + state->vobsub.cur_chg_col_end = state->vobsub.cur_chg_col + 1; + } else if (state->vobsub.n_line_ctrl_i > 0) { + state->vobsub.cur_chg_col = state->vobsub.line_ctrl_i; + state->vobsub.cur_chg_col_end = + state->vobsub.cur_chg_col + state->vobsub.n_line_ctrl_i; + } else + state->vobsub.cur_chg_col = NULL; + + /* We start rendering from the first line of the display rect */ + y = state->vobsub.disp_rect.top; + /* start_y is always an even number and we render lines in pairs from there, + * accumulating 2 lines of chroma then blending it. We might need to render a + * single line at the end if the display rect ends on an even line too. */ + last_y = (state->vobsub.disp_rect.bottom - 1) & ~(0x01); + + /* center the image when display rectangle exceeds the video width */ + if (state->vid_width < state->vobsub.disp_rect.right) { + gint diff, disp_width; + + disp_width = state->vobsub.disp_rect.left - state->vobsub.disp_rect.right; + diff = (disp_width - state->vid_width) / 2; + + /* fixme, this is not used yet */ + state->vobsub.clip_rect.left = state->vobsub.disp_rect.left + diff; + state->vobsub.clip_rect.right = state->vobsub.disp_rect.right - diff; + + GST_DEBUG ("clipping width to %d,%d", state->vobsub.clip_rect.left, + state->vobsub.clip_rect.right); + } else { + state->vobsub.clip_rect.left = state->vobsub.disp_rect.left; + state->vobsub.clip_rect.right = state->vobsub.disp_rect.right; + } + + /* for the height, chop off the bottom bits of the diplay rectangle because we + * assume the picture is in the lower part. We should better check where it + * is and do something more clever. */ + state->vobsub.clip_rect.bottom = state->vobsub.disp_rect.bottom; + if (state->vid_height < state->vobsub.disp_rect.bottom) { + state->vobsub.clip_rect.top = + state->vobsub.disp_rect.bottom - state->vid_height; + GST_DEBUG ("clipping height to %d,%d", state->vobsub.clip_rect.top, + state->vobsub.clip_rect.bottom); + } else { + state->vobsub.clip_rect.top = state->vobsub.disp_rect.top; + /* Update our plane references to the first line of the disp_rect */ + planes[0] += state->Y_stride * y; + planes[1] += state->UV_stride * (y / 2); + planes[2] += state->UV_stride * (y / 2); + } + + for (state->vobsub.cur_Y = y; state->vobsub.cur_Y <= last_y; + state->vobsub.cur_Y++) { + gboolean clip; + + clip = (state->vobsub.cur_Y < state->vobsub.clip_rect.top + || state->vobsub.cur_Y > state->vobsub.clip_rect.bottom); + + /* Reset the compositing buffer */ + gstspu_clear_comp_buffers (state); + /* Render even line */ + state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x; + gstspu_vobsub_render_line (state, planes, &state->vobsub.cur_offsets[0]); + if (!clip) { + /* Advance the luminance output pointer */ + planes[0] += state->Y_stride; + } + state->vobsub.cur_Y++; + + /* Render odd line */ + state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x + 1; + gstspu_vobsub_render_line (state, planes, &state->vobsub.cur_offsets[1]); + /* Blend the accumulated UV compositing buffers onto the output */ + gstspu_vobsub_blend_comp_buffers (state, planes); + + if (!clip) { + /* Update all the output pointers */ + planes[0] += state->Y_stride; + planes[1] += state->UV_stride; + planes[2] += state->UV_stride; + } + } + if (state->vobsub.cur_Y == state->vobsub.disp_rect.bottom) { + g_assert ((state->vobsub.disp_rect.bottom & 0x01) == 0); + + /* Render a remaining lone last even line. y already has the correct value + * after the above loop exited. */ + gstspu_clear_comp_buffers (state); + state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x; + gstspu_vobsub_render_line (state, planes, &state->vobsub.cur_offsets[0]); + gstspu_vobsub_blend_comp_buffers (state, planes); + } + + /* for debugging purposes, draw a faint rectangle at the edges of the disp_rect */ +#if 0 + do { + guint8 *cur; + gint16 pos; + + cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->vobsub.disp_rect.top; + for (pos = state->vobsub.disp_rect.left + 1; + pos < state->vobsub.disp_rect.right; pos++) + cur[pos] = (cur[pos] / 2) + 0x8; + cur = + GST_BUFFER_DATA (buf) + + state->Y_stride * state->vobsub.disp_rect.bottom; + for (pos = state->vobsub.disp_rect.left + 1; + pos < state->vobsub.disp_rect.right; pos++) + cur[pos] = (cur[pos] / 2) + 0x8; + cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->vobsub.disp_rect.top; + for (pos = state->vobsub.disp_rect.top; + pos <= state->vobsub.disp_rect.bottom; pos++) { + cur[state->vobsub.disp_rect.left] = + (cur[state->vobsub.disp_rect.left] / 2) + 0x8; + cur[state->vobsub.disp_rect.right] = + (cur[state->vobsub.disp_rect.right] / 2) + 0x8; + cur += state->Y_stride; + } + } while (0); +#endif + /* For debugging purposes, draw a faint rectangle around the highlight rect */ +#if 0 + if (state->hl_rect.top != -1) { + guint8 *cur; + gint16 pos; + + cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->hl_rect.top; + for (pos = state->hl_rect.left + 1; pos < state->hl_rect.right; pos++) + cur[pos] = (cur[pos] / 2) + 0x8; + cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->hl_rect.bottom; + for (pos = state->hl_rect.left + 1; pos < state->hl_rect.right; pos++) + cur[pos] = (cur[pos] / 2) + 0x8; + cur = GST_BUFFER_DATA (buf) + state->Y_stride * state->hl_rect.top; + for (pos = state->hl_rect.top; pos <= state->hl_rect.bottom; pos++) { + cur[state->hl_rect.left] = (cur[state->hl_rect.left] / 2) + 0x8; + cur[state->hl_rect.right] = (cur[state->hl_rect.right] / 2) + 0x8; + cur += state->Y_stride; + } + } +#endif +} diff --git a/gst/dvdspu/gstspu-vobsub.c b/gst/dvdspu/gstspu-vobsub.c new file mode 100644 index 00000000..1757feb7 --- /dev/null +++ b/gst/dvdspu/gstspu-vobsub.c @@ -0,0 +1,513 @@ +/* GStreamer Sub-Picture Unit - VobSub/DVD handling + * Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net> + * + * 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 <string.h> + +#include <gst/gst.h> + +#include "gstdvdspu.h" +#include "gstspu-vobsub.h" + +GST_DEBUG_CATEGORY_EXTERN (dvdspu_debug); +#define GST_CAT_DEFAULT dvdspu_debug + +/* Define to dump out a text description of the incoming SPU commands */ +#define DUMP_DCSQ 0 + +/* Convert an STM offset in the SPU sequence to a GStreamer timestamp */ +#define STM_TO_GST(stm) ((GST_MSECOND * 1024 * (stm)) / 90) + +typedef enum SpuVobsubCmd SpuVobsubCmd; + +enum SpuVobsubCmd +{ + SPU_CMD_FSTA_DSP = 0x00, /* Forced Display */ + SPU_CMD_DSP = 0x01, /* Display Start */ + SPU_CMD_STP_DSP = 0x02, /* Display Off */ + SPU_CMD_SET_COLOR = 0x03, /* Set the color indexes for the palette */ + SPU_CMD_SET_ALPHA = 0x04, /* Set the alpha indexes for the palette */ + SPU_CMD_SET_DAREA = 0x05, /* Set the display area for the SPU */ + SPU_CMD_DSPXA = 0x06, /* Pixel data addresses */ + SPU_CMD_CHG_COLCON = 0x07, /* Change Color & Contrast */ + SPU_CMD_END = 0xff +}; + +static void +gst_dvd_spu_parse_chg_colcon (GstDVDSpu * dvdspu, guint8 * data, guint8 * end) +{ + SpuState *state = &dvdspu->spu_state; + guint8 *cur; + gint16 n_entries; + gint16 i; + + /* Clear any existing chg colcon info */ + state->vobsub.n_line_ctrl_i = 0; + if (state->vobsub.line_ctrl_i != NULL) { + g_free (state->vobsub.line_ctrl_i); + state->vobsub.line_ctrl_i = NULL; + } + GST_DEBUG_OBJECT (dvdspu, "Change Color & Contrast. Pixel data = %d bytes", + (gint16) (end - data)); + + /* Count the number of entries we'll need */ + n_entries = 0; + for (cur = data; cur < end;) { + guint8 n_changes; + guint32 code; + + if (cur + 4 > end) + break; + + code = GST_READ_UINT32_BE (cur); + if (code == 0x0fffffff) + break; /* Termination code */ + + n_changes = CLAMP ((cur[2] >> 4), 1, 8); + cur += 4 + (6 * n_changes); + + if (cur > end) + break; /* Invalid entry overrunning buffer */ + + n_entries++; + } + + state->vobsub.n_line_ctrl_i = n_entries; + state->vobsub.line_ctrl_i = g_new (SpuVobsubLineCtrlI, n_entries); + + cur = data; + for (i = 0; i < n_entries; i++) { + SpuVobsubLineCtrlI *cur_line_ctrl = state->vobsub.line_ctrl_i + i; + guint8 n_changes = CLAMP ((cur[2] >> 4), 1, 8); + guint8 c; + + cur_line_ctrl->n_changes = n_changes; + cur_line_ctrl->top = ((cur[0] << 8) & 0x300) | cur[1]; + cur_line_ctrl->bottom = ((cur[2] << 8) & 0x300) | cur[3]; + + GST_LOG_OBJECT (dvdspu, "ChgColcon Entry %d Top: %d Bottom: %d Changes: %d", + i, cur_line_ctrl->top, cur_line_ctrl->bottom, n_changes); + cur += 4; + + for (c = 0; c < n_changes; c++) { + SpuVobsubPixCtrlI *cur_pix_ctrl = cur_line_ctrl->pix_ctrl_i + c; + + cur_pix_ctrl->left = ((cur[0] << 8) & 0x300) | cur[1]; + cur_pix_ctrl->palette = GST_READ_UINT32_BE (cur + 2); + GST_LOG_OBJECT (dvdspu, " %d: left: %d palette 0x%x", c, + cur_pix_ctrl->left, cur_pix_ctrl->palette); + cur += 6; + } + } +} + +static void +gst_dvd_spu_exec_cmd_blk (GstDVDSpu * dvdspu, guint8 * data, guint8 * end) +{ + SpuState *state = &dvdspu->spu_state; + + while (data < end) { + guint8 cmd; + + cmd = data[0]; + + switch (cmd) { + case SPU_CMD_FSTA_DSP: + GST_DEBUG_OBJECT (dvdspu, " Forced Display"); + state->flags |= SPU_STATE_FORCED_DSP; + data += 1; + break; + case SPU_CMD_DSP: + GST_DEBUG_OBJECT (dvdspu, " Display On"); + state->flags |= SPU_STATE_DISPLAY; + data += 1; + break; + case SPU_CMD_STP_DSP: + GST_DEBUG_OBJECT (dvdspu, " Display Off"); + state->flags &= ~(SPU_STATE_FORCED_DSP | SPU_STATE_DISPLAY); + data += 1; + break; + case SPU_CMD_SET_COLOR:{ + if (G_UNLIKELY (data + 3 >= end)) + return; /* Invalid SET_COLOR cmd at the end of the blk */ + + state->vobsub.main_idx[3] = data[1] >> 4; + state->vobsub.main_idx[2] = data[1] & 0x0f; + state->vobsub.main_idx[1] = data[2] >> 4; + state->vobsub.main_idx[0] = data[2] & 0x0f; + + state->vobsub.main_pal_dirty = TRUE; + + GST_DEBUG_OBJECT (dvdspu, + " Set Color bg %u pattern %u emph-1 %u emph-2 %u", + state->vobsub.main_idx[0], state->vobsub.main_idx[1], + state->vobsub.main_idx[2], state->vobsub.main_idx[3]); + data += 3; + break; + } + case SPU_CMD_SET_ALPHA:{ + if (G_UNLIKELY (data + 3 >= end)) + return; /* Invalid SET_ALPHA cmd at the end of the blk */ + + state->vobsub.main_alpha[3] = data[1] >> 4; + state->vobsub.main_alpha[2] = data[1] & 0x0f; + state->vobsub.main_alpha[1] = data[2] >> 4; + state->vobsub.main_alpha[0] = data[2] & 0x0f; + + state->vobsub.main_pal_dirty = TRUE; + + GST_DEBUG_OBJECT (dvdspu, + " Set Alpha bg %u pattern %u emph-1 %u emph-2 %u", + state->vobsub.main_alpha[0], state->vobsub.main_alpha[1], + state->vobsub.main_alpha[2], state->vobsub.main_alpha[3]); + data += 3; + break; + } + case SPU_CMD_SET_DAREA:{ + SpuRect *r = &state->vobsub.disp_rect; + + if (G_UNLIKELY (data + 7 >= end)) + return; /* Invalid SET_DAREA cmd at the end of the blk */ + + r->top = ((data[4] & 0x3f) << 4) | ((data[5] & 0xe0) >> 4); + r->left = ((data[1] & 0x3f) << 4) | ((data[2] & 0xf0) >> 4); + r->right = ((data[2] & 0x03) << 8) | data[3]; + r->bottom = ((data[5] & 0x03) << 8) | data[6]; + + GST_DEBUG_OBJECT (dvdspu, + " Set Display Area top %u left %u bottom %u right %u", r->top, + r->left, r->bottom, r->right); + + data += 7; + break; + } + case SPU_CMD_DSPXA:{ + if (G_UNLIKELY (data + 5 >= end)) + return; /* Invalid SET_DSPXE cmd at the end of the blk */ + + state->vobsub.pix_data[0] = GST_READ_UINT16_BE (data + 1); + state->vobsub.pix_data[1] = GST_READ_UINT16_BE (data + 3); + /* Store a reference to the current command buffer, as that's where + * we'll need to take our pixel data from */ + gst_buffer_replace (&state->vobsub.pix_buf, state->vobsub.buf); + + GST_DEBUG_OBJECT (dvdspu, " Set Pixel Data Offsets top: %u bot: %u", + state->vobsub.pix_data[0], state->vobsub.pix_data[1]); + + data += 5; + break; + } + case SPU_CMD_CHG_COLCON:{ + guint16 field_size; + + GST_DEBUG_OBJECT (dvdspu, " Set Color & Contrast Change"); + if (G_UNLIKELY (data + 3 >= end)) + return; /* Invalid CHG_COLCON cmd at the end of the blk */ + + data++; + field_size = GST_READ_UINT16_BE (data); + + if (G_UNLIKELY (data + field_size >= end)) + return; /* Invalid CHG_COLCON cmd at the end of the blk */ + + gst_dvd_spu_parse_chg_colcon (dvdspu, data + 2, data + field_size); + state->vobsub.line_ctrl_i_pal_dirty = TRUE; + data += field_size; + break; + } + case SPU_CMD_END: + default: + GST_DEBUG_OBJECT (dvdspu, " END"); + data = end; + break; + } + } +} + +static void +gst_dvd_spu_finish_spu_buf (GstDVDSpu * dvdspu) +{ + SpuState *state = &dvdspu->spu_state; + + state->next_ts = state->vobsub.base_ts = GST_CLOCK_TIME_NONE; + gst_buffer_replace (&state->vobsub.buf, NULL); + + GST_DEBUG_OBJECT (dvdspu, "Finished SPU buffer"); +} + +static gboolean +gst_dvd_spu_setup_cmd_blk (GstDVDSpu * dvdspu, guint16 cmd_blk_offset, + guint8 * start, guint8 * end) +{ + SpuState *state = &dvdspu->spu_state; + guint16 delay; + guint8 *cmd_blk = start + cmd_blk_offset; + + if (G_UNLIKELY (cmd_blk + 5 >= end)) + return FALSE; /* No valid command block to read */ + + delay = GST_READ_UINT16_BE (cmd_blk); + state->next_ts = state->vobsub.base_ts + STM_TO_GST (delay); + state->vobsub.cur_cmd_blk = cmd_blk_offset; + + GST_DEBUG_OBJECT (dvdspu, "Setup CMD Block @ %u with TS %" GST_TIME_FORMAT, + state->vobsub.cur_cmd_blk, GST_TIME_ARGS (state->next_ts)); + return TRUE; +} + +#if DUMP_DCSQ +static void +gst_dvd_spu_dump_dcsq (GstDVDSpu * dvdspu, + GstClockTime start_ts, GstBuffer * spu_buf) +{ + guint16 cmd_blk_offset; + guint16 next_blk; + guint8 *start, *end; + + start = GST_BUFFER_DATA (spu_buf); + end = start + GST_BUFFER_SIZE (spu_buf); + + g_return_if_fail (start != NULL); + + /* First command */ + next_blk = GST_READ_UINT16_BE (start + 2); + cmd_blk_offset = 0; + + /* Loop through all commands */ + g_print ("SPU begins @ %" GST_TIME_FORMAT " offset %u\n", + GST_TIME_ARGS (start_ts), next_blk); + + while (cmd_blk_offset != next_blk) { + guint8 *data; + GstClockTime cmd_blk_ts; + + cmd_blk_offset = next_blk; + + if (G_UNLIKELY (start + cmd_blk_offset + 5 >= end)) + break; /* No valid command to read */ + + data = start + cmd_blk_offset; + + cmd_blk_ts = start_ts + STM_TO_GST (GST_READ_UINT16_BE (data)); + next_blk = GST_READ_UINT16_BE (data + 2); + + g_print ("Cmd Blk @ offset %u next %u ts %" GST_TIME_FORMAT "\n", + cmd_blk_offset, next_blk, GST_TIME_ARGS (cmd_blk_ts)); + + data += 4; + gst_dvd_spu_exec_cmd_blk (dvdspu, data, end); + } +} +#endif + +void +gstspu_vobsub_handle_new_buf (GstDVDSpu * dvdspu, GstClockTime event_ts, + GstBuffer * buf) +{ + guint8 *start, *end; + SpuState *state = &dvdspu->spu_state; + +#if DUMP_DCSQ + gst_dvd_spu_dump_dcsq (dvdspu, event_ts, buf); +#endif + + if (G_UNLIKELY (GST_BUFFER_SIZE (buf) < 4)) + goto invalid; + + if (state->vobsub.buf != NULL) { + gst_buffer_unref (state->vobsub.buf); + state->vobsub.buf = NULL; + } + state->vobsub.buf = buf; + state->vobsub.base_ts = event_ts; + + start = GST_BUFFER_DATA (state->vobsub.buf); + end = start + GST_BUFFER_SIZE (state->vobsub.buf); + + /* Configure the first command block in this buffer as our initial blk */ + state->vobsub.cur_cmd_blk = GST_READ_UINT16_BE (start + 2); + gst_dvd_spu_setup_cmd_blk (dvdspu, state->vobsub.cur_cmd_blk, start, end); + /* Clear existing chg-colcon info */ + state->vobsub.n_line_ctrl_i = 0; + if (state->vobsub.line_ctrl_i != NULL) { + g_free (state->vobsub.line_ctrl_i); + state->vobsub.line_ctrl_i = NULL; + } + return; + +invalid: + /* Invalid buffer */ + gst_dvd_spu_finish_spu_buf (dvdspu); +} + +gboolean +gstspu_vobsub_execute_event (GstDVDSpu * dvdspu) +{ + guint8 *start, *cmd_blk, *end; + guint16 next_blk; + SpuState *state = &dvdspu->spu_state; + + if (state->vobsub.buf == NULL) + return FALSE; + + GST_DEBUG_OBJECT (dvdspu, "Executing cmd blk with TS %" GST_TIME_FORMAT + " @ offset %u", GST_TIME_ARGS (state->next_ts), + state->vobsub.cur_cmd_blk); + + start = GST_BUFFER_DATA (state->vobsub.buf); + end = start + GST_BUFFER_SIZE (state->vobsub.buf); + + cmd_blk = start + state->vobsub.cur_cmd_blk; + + if (G_UNLIKELY (cmd_blk + 5 >= end)) { + /* Invalid. Finish the buffer and loop again */ + gst_dvd_spu_finish_spu_buf (dvdspu); + return FALSE; + } + + gst_dvd_spu_exec_cmd_blk (dvdspu, cmd_blk + 4, end); + + next_blk = GST_READ_UINT16_BE (cmd_blk + 2); + if (next_blk != state->vobsub.cur_cmd_blk) { + /* Advance to the next block of commands */ + gst_dvd_spu_setup_cmd_blk (dvdspu, next_blk, start, end); + } else { + /* Next Block points to the current block, so we're finished with this + * SPU buffer */ + gst_dvd_spu_finish_spu_buf (dvdspu); + return FALSE; + } + + return TRUE; +} + +gboolean +gstspu_vobsub_handle_dvd_event (GstDVDSpu * dvdspu, GstEvent * event) +{ + const gchar *event_type; + const GstStructure *structure = gst_event_get_structure (event); + SpuState *state = &dvdspu->spu_state; + gboolean hl_change = FALSE; + + event_type = gst_structure_get_string (structure, "event"); + + if (strcmp (event_type, "dvd-spu-clut-change") == 0) { + gchar prop_name[32]; + gint i; + gint entry; + + for (i = 0; i < 16; i++) { + g_snprintf (prop_name, 32, "clut%02d", i); + if (!gst_structure_get_int (structure, prop_name, &entry)) + entry = 0; + state->vobsub.current_clut[i] = (guint32) entry; + } + + state->vobsub.main_pal_dirty = TRUE; + state->vobsub.hl_pal_dirty = TRUE; + state->vobsub.line_ctrl_i_pal_dirty = TRUE; + hl_change = TRUE; + } else if (strcmp (event_type, "dvd-spu-highlight") == 0) { + gint val; + + if (gst_structure_get_int (structure, "palette", &val)) { + state->vobsub.hl_idx[3] = ((guint32) (val) >> 28) & 0x0f; + state->vobsub.hl_idx[2] = ((guint32) (val) >> 24) & 0x0f; + state->vobsub.hl_idx[1] = ((guint32) (val) >> 20) & 0x0f; + state->vobsub.hl_idx[0] = ((guint32) (val) >> 16) & 0x0f; + + state->vobsub.hl_alpha[3] = ((guint32) (val) >> 12) & 0x0f; + state->vobsub.hl_alpha[2] = ((guint32) (val) >> 8) & 0x0f; + state->vobsub.hl_alpha[1] = ((guint32) (val) >> 4) & 0x0f; + state->vobsub.hl_alpha[0] = ((guint32) (val) >> 0) & 0x0f; + + state->vobsub.hl_pal_dirty = TRUE; + } + if (gst_structure_get_int (structure, "sx", &val)) + state->vobsub.hl_rect.left = (gint16) val; + if (gst_structure_get_int (structure, "sy", &val)) + state->vobsub.hl_rect.top = (gint16) val; + if (gst_structure_get_int (structure, "ex", &val)) + state->vobsub.hl_rect.right = (gint16) val; + if (gst_structure_get_int (structure, "ey", &val)) + state->vobsub.hl_rect.bottom = (gint16) val; + + GST_INFO_OBJECT (dvdspu, "Highlight rect is now (%d,%d) to (%d,%d)", + state->vobsub.hl_rect.left, state->vobsub.hl_rect.top, + state->vobsub.hl_rect.right, state->vobsub.hl_rect.bottom); + hl_change = TRUE; + } else if (strcmp (event_type, "dvd-spu-reset-highlight") == 0) { + if (state->vobsub.hl_rect.top != -1 || state->vobsub.hl_rect.bottom != -1) + hl_change = TRUE; + state->vobsub.hl_rect.top = -1; + state->vobsub.hl_rect.bottom = -1; + GST_INFO_OBJECT (dvdspu, "Highlight off"); + } else if (strcmp (event_type, "dvd-set-subpicture-track") == 0) { + gboolean forced_only; + + if (gst_structure_get_boolean (structure, "forced-only", &forced_only)) { + gboolean was_forced = (state->flags & SPU_STATE_FORCED_ONLY); + + if (forced_only) + state->flags |= SPU_STATE_FORCED_ONLY; + else + state->flags &= ~(SPU_STATE_FORCED_ONLY); + + if (was_forced != forced_only) + hl_change = TRUE; + } + } + + gst_event_unref (event); + + return hl_change; +} + +void +gstspu_vobsub_flush (GstDVDSpu * dvdspu) +{ + SpuState *state = &dvdspu->spu_state; + + if (state->vobsub.buf) { + gst_buffer_unref (state->vobsub.buf); + state->vobsub.buf = NULL; + } + if (state->vobsub.pix_buf) { + gst_buffer_unref (state->vobsub.pix_buf); + state->vobsub.pix_buf = NULL; + } + + state->vobsub.base_ts = GST_CLOCK_TIME_NONE; + state->vobsub.pix_data[0] = 0; + state->vobsub.pix_data[1] = 0; + + state->vobsub.hl_rect.top = -1; + state->vobsub.hl_rect.bottom = -1; + + state->vobsub.disp_rect.top = -1; + state->vobsub.disp_rect.bottom = -1; + + state->vobsub.n_line_ctrl_i = 0; + if (state->vobsub.line_ctrl_i != NULL) { + g_free (state->vobsub.line_ctrl_i); + state->vobsub.line_ctrl_i = NULL; + } +} diff --git a/gst/dvdspu/gstspu-vobsub.h b/gst/dvdspu/gstspu-vobsub.h new file mode 100644 index 00000000..9b4196bc --- /dev/null +++ b/gst/dvdspu/gstspu-vobsub.h @@ -0,0 +1,110 @@ +/* GStreamer Sub-Picture Unit - VobSub/DVD handling + * Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net> + * + * 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 __GSTSPU_VOBSUB_H__ +#define __GSTSPU_VOBSUB_H__ + +#include "gstspu-common.h" + +typedef struct SpuVobsubState SpuVobsubState; +typedef struct SpuVobsubPixCtrlI SpuVobsubPixCtrlI; +typedef struct SpuVobsubLineCtrlI SpuVobsubLineCtrlI; + +/* Pixel Control Info from a Change Color Contrast command */ +struct SpuVobsubPixCtrlI { + gint16 left; + guint32 palette; + + /* Pre-multiplied palette values, updated as + * needed */ + SpuColour pal_cache[4]; +}; + +struct SpuVobsubLineCtrlI { + guint8 n_changes; /* 1 to 8 */ + SpuVobsubPixCtrlI pix_ctrl_i[8]; + + gint16 top; + gint16 bottom; +}; + +struct SpuVobsubState { + GstClockTime base_ts; /* base TS for cmd blk delays in running time */ + GstBuffer *buf; /* Current SPU packet we're executing commands from */ + guint16 cur_cmd_blk; /* Offset into the buf for the current cmd block */ + + /* Top + Bottom field offsets in the buffer. 0 = not set */ + guint16 pix_data[2]; + GstBuffer *pix_buf; /* Current SPU packet the pix_data references */ + + SpuRect disp_rect; + SpuRect clip_rect; + SpuRect hl_rect; + + guint32 current_clut[16]; /* Colour lookup table from incoming events */ + + guint8 main_idx[4]; /* Indices for current main palette */ + guint8 main_alpha[4]; /* Alpha values for main palette */ + + guint8 hl_idx[4]; /* Indices for current highlight palette */ + guint8 hl_alpha[4]; /* Alpha values for highlight palette */ + + /* Pre-multiplied colour palette for the main palette */ + SpuColour main_pal[4]; + gboolean main_pal_dirty; + + /* Line control info for rendering the highlight palette */ + SpuVobsubLineCtrlI hl_ctrl_i; + gboolean hl_pal_dirty; /* Indicates that the HL palette info needs refreshing */ + + /* LineCtrlI Info from a Change Color & Contrast command */ + SpuVobsubLineCtrlI *line_ctrl_i; + gint16 n_line_ctrl_i; + gboolean line_ctrl_i_pal_dirty; /* Indicates that the palettes for the line_ctrl_i + * need recalculating */ + + /* Rendering state vars below */ + gint16 comp_last_x[2]; /* Maximum X values we rendered into the comp buffer (odd & even) */ + gint16 *comp_last_x_ptr; /* Ptr to the current comp_last_x value to be updated by the render */ + + /* Current Y Position */ + gint16 cur_Y; + + /* Current offset in nibbles into the pix_data */ + guint16 cur_offsets[2]; + guint16 max_offset; + + /* current ChgColCon Line Info */ + SpuVobsubLineCtrlI *cur_chg_col; + SpuVobsubLineCtrlI *cur_chg_col_end; + + /* Output position tracking */ + guint8 *out_Y; + guint32 *out_U; + guint32 *out_V; + guint32 *out_A; +}; + +void gstspu_vobsub_handle_new_buf (GstDVDSpu * dvdspu, GstClockTime event_ts, GstBuffer *buf); +gboolean gstspu_vobsub_execute_event (GstDVDSpu *dvdspu); +void gstspu_vobsub_render (GstDVDSpu *dvdspu, GstBuffer *buf); +gboolean gstspu_vobsub_handle_dvd_event (GstDVDSpu *dvdspu, GstEvent *event); +void gstspu_vobsub_flush (GstDVDSpu *dvdspu); + +#endif diff --git a/gst/flv/Makefile.am b/gst/flv/Makefile.am deleted file mode 100644 index 6e1a58b2..00000000 --- a/gst/flv/Makefile.am +++ /dev/null @@ -1,9 +0,0 @@ -plugin_LTLIBRARIES = libgstflv.la - -libgstflv_la_CFLAGS = $(GST_BASE_CFLAGS) $(GST_CFLAGS) -libgstflv_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) -libgstflv_la_LDFLAGS = ${GST_PLUGIN_LDFLAGS} -libgstflv_la_SOURCES = gstflvdemux.c gstflvparse.c gstflvmux.c -libgstflv_la_LIBTOOLFLAGS = --tag=disable-static - -noinst_HEADERS = gstflvdemux.h gstflvparse.h gstflvmux.h diff --git a/gst/flv/gstflvdemux.c b/gst/flv/gstflvdemux.c deleted file mode 100644 index f31a22e8..00000000 --- a/gst/flv/gstflvdemux.c +++ /dev/null @@ -1,1323 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Julien Moutte <julien@moutte.net> - * - * 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. - */ - -/** - * SECTION:element-flvdemux - * - * flvdemux demuxes an FLV file into the different contained streams. - * - * <refsect2> - * <title>Example launch line</title> - * |[ - * gst-launch -v filesrc location=/path/to/flv ! flvdemux ! audioconvert ! autoaudiosink - * ]| This pipeline demuxes an FLV file and outputs the contained raw audio streams. - * </refsect2> - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstflvdemux.h" -#include "gstflvparse.h" -#include "gstflvmux.h" - -#include <string.h> - -static GstStaticPadTemplate flv_sink_template = GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("video/x-flv") - ); - -static GstStaticPadTemplate audio_src_template = -GST_STATIC_PAD_TEMPLATE ("audio", - GST_PAD_SRC, - GST_PAD_SOMETIMES, - GST_STATIC_CAPS_ANY); - -static GstStaticPadTemplate video_src_template = -GST_STATIC_PAD_TEMPLATE ("video", - GST_PAD_SRC, - GST_PAD_SOMETIMES, - GST_STATIC_CAPS_ANY); - -GST_DEBUG_CATEGORY (flvdemux_debug); -#define GST_CAT_DEFAULT flvdemux_debug - -GST_BOILERPLATE (GstFLVDemux, gst_flv_demux, GstElement, GST_TYPE_ELEMENT); - -/* 9 bytes of header + 4 bytes of first previous tag size */ -#define FLV_HEADER_SIZE 13 -/* 1 byte of tag type + 3 bytes of tag data size */ -#define FLV_TAG_TYPE_SIZE 4 - -static void -gst_flv_demux_flush (GstFLVDemux * demux, gboolean discont) -{ - GST_DEBUG_OBJECT (demux, "flushing queued data in the FLV demuxer"); - - gst_adapter_clear (demux->adapter); - - demux->audio_need_discont = TRUE; - demux->video_need_discont = TRUE; - - demux->flushing = FALSE; - - /* Only in push mode */ - if (!demux->random_access) { - /* After a flush we expect a tag_type */ - demux->state = FLV_STATE_TAG_TYPE; - /* We reset the offset and will get one from first push */ - demux->offset = 0; - } -} - -static void -gst_flv_demux_cleanup (GstFLVDemux * demux) -{ - GST_DEBUG_OBJECT (demux, "cleaning up FLV demuxer"); - - demux->state = FLV_STATE_HEADER; - - demux->flushing = FALSE; - demux->need_header = TRUE; - demux->audio_need_segment = TRUE; - demux->video_need_segment = TRUE; - demux->audio_need_discont = TRUE; - demux->video_need_discont = TRUE; - - /* By default we consider them as linked */ - demux->audio_linked = TRUE; - demux->video_linked = TRUE; - - demux->has_audio = FALSE; - demux->has_video = FALSE; - demux->push_tags = FALSE; - demux->got_par = FALSE; - - gst_segment_init (&demux->segment, GST_FORMAT_TIME); - - demux->w = demux->h = 0; - demux->par_x = demux->par_y = 1; - demux->video_offset = 0; - demux->audio_offset = 0; - demux->offset = demux->cur_tag_offset = 0; - demux->tag_size = demux->tag_data_size = 0; - demux->duration = GST_CLOCK_TIME_NONE; - - if (demux->new_seg_event) { - gst_event_unref (demux->new_seg_event); - demux->new_seg_event = NULL; - } - - if (demux->close_seg_event) { - gst_event_unref (demux->close_seg_event); - demux->close_seg_event = NULL; - } - - gst_adapter_clear (demux->adapter); - - if (demux->audio_codec_data) { - gst_buffer_unref (demux->audio_codec_data); - demux->audio_codec_data = NULL; - } - - if (demux->video_codec_data) { - gst_buffer_unref (demux->video_codec_data); - demux->video_codec_data = NULL; - } - - if (demux->audio_pad) { - gst_element_remove_pad (GST_ELEMENT (demux), demux->audio_pad); - gst_object_unref (demux->audio_pad); - demux->audio_pad = NULL; - } - - if (demux->video_pad) { - gst_element_remove_pad (GST_ELEMENT (demux), demux->video_pad); - gst_object_unref (demux->video_pad); - demux->video_pad = NULL; - } - - if (demux->times) { - g_array_free (demux->times, TRUE); - demux->times = NULL; - } - - if (demux->filepositions) { - g_array_free (demux->filepositions, TRUE); - demux->filepositions = NULL; - } -} - -static GstFlowReturn -gst_flv_demux_chain (GstPad * pad, GstBuffer * buffer) -{ - GstFlowReturn ret = GST_FLOW_OK; - GstFLVDemux *demux = NULL; - - demux = GST_FLV_DEMUX (gst_pad_get_parent (pad)); - - GST_LOG_OBJECT (demux, "received buffer of %d bytes at offset %" - G_GUINT64_FORMAT, GST_BUFFER_SIZE (buffer), GST_BUFFER_OFFSET (buffer)); - - if (G_UNLIKELY (GST_BUFFER_OFFSET (buffer) == 0)) { - GST_DEBUG_OBJECT (demux, "beginning of file, expect header"); - demux->state = FLV_STATE_HEADER; - demux->offset = 0; - } - - if (G_UNLIKELY (demux->offset == 0 && GST_BUFFER_OFFSET (buffer) != 0)) { - GST_DEBUG_OBJECT (demux, "offset was zero, synchronizing with buffer's"); - demux->offset = GST_BUFFER_OFFSET (buffer); - } - - gst_adapter_push (demux->adapter, buffer); - -parse: - if (G_UNLIKELY (ret != GST_FLOW_OK)) { - if (ret == GST_FLOW_NOT_LINKED && (demux->audio_linked - || demux->video_linked)) { - ret = GST_FLOW_OK; - } else { - GST_DEBUG_OBJECT (demux, "got flow return %s", gst_flow_get_name (ret)); - goto beach; - } - } - - if (G_UNLIKELY (demux->flushing)) { - GST_DEBUG_OBJECT (demux, "we are now flushing, exiting parser loop"); - ret = GST_FLOW_WRONG_STATE; - goto beach; - } - - switch (demux->state) { - case FLV_STATE_HEADER: - { - if (gst_adapter_available (demux->adapter) >= FLV_HEADER_SIZE) { - GstBuffer *buffer; - - buffer = gst_adapter_take_buffer (demux->adapter, FLV_HEADER_SIZE); - - ret = gst_flv_parse_header (demux, buffer); - - gst_buffer_unref (buffer); - demux->offset += FLV_HEADER_SIZE; - - demux->state = FLV_STATE_TAG_TYPE; - goto parse; - } else { - goto beach; - } - } - case FLV_STATE_TAG_TYPE: - { - if (gst_adapter_available (demux->adapter) >= FLV_TAG_TYPE_SIZE) { - GstBuffer *buffer; - - /* Remember the tag offset in bytes */ - demux->cur_tag_offset = demux->offset; - - buffer = gst_adapter_take_buffer (demux->adapter, FLV_TAG_TYPE_SIZE); - - ret = gst_flv_parse_tag_type (demux, buffer); - - gst_buffer_unref (buffer); - demux->offset += FLV_TAG_TYPE_SIZE; - - goto parse; - } else { - goto beach; - } - } - case FLV_STATE_TAG_VIDEO: - { - if (gst_adapter_available (demux->adapter) >= demux->tag_size) { - GstBuffer *buffer; - - buffer = gst_adapter_take_buffer (demux->adapter, demux->tag_size); - - ret = gst_flv_parse_tag_video (demux, buffer); - - gst_buffer_unref (buffer); - demux->offset += demux->tag_size; - - demux->state = FLV_STATE_TAG_TYPE; - goto parse; - } else { - goto beach; - } - } - case FLV_STATE_TAG_AUDIO: - { - if (gst_adapter_available (demux->adapter) >= demux->tag_size) { - GstBuffer *buffer; - - buffer = gst_adapter_take_buffer (demux->adapter, demux->tag_size); - - ret = gst_flv_parse_tag_audio (demux, buffer); - - gst_buffer_unref (buffer); - demux->offset += demux->tag_size; - - demux->state = FLV_STATE_TAG_TYPE; - goto parse; - } else { - goto beach; - } - } - case FLV_STATE_TAG_SCRIPT: - { - if (gst_adapter_available (demux->adapter) >= demux->tag_size) { - GstBuffer *buffer; - - buffer = gst_adapter_take_buffer (demux->adapter, demux->tag_size); - - ret = gst_flv_parse_tag_script (demux, buffer); - - gst_buffer_unref (buffer); - demux->offset += demux->tag_size; - - demux->state = FLV_STATE_TAG_TYPE; - goto parse; - } else { - goto beach; - } - } - default: - GST_DEBUG_OBJECT (demux, "unexpected demuxer state"); - } - -beach: - if (G_UNLIKELY (ret == GST_FLOW_NOT_LINKED)) { - /* If either audio or video is linked we return GST_FLOW_OK */ - if (demux->audio_linked || demux->video_linked) { - ret = GST_FLOW_OK; - } - } - - gst_object_unref (demux); - - return ret; -} - -static GstFlowReturn -gst_flv_demux_pull_range (GstFLVDemux * demux, GstPad * pad, guint64 offset, - guint size, GstBuffer ** buffer) -{ - GstFlowReturn ret; - - ret = gst_pad_pull_range (pad, offset, size, buffer); - if (G_UNLIKELY (ret != GST_FLOW_OK)) { - GST_WARNING_OBJECT (demux, - "failed when pulling %d bytes from offset %" G_GUINT64_FORMAT ": %s", - size, offset, gst_flow_get_name (ret)); - *buffer = NULL; - return ret; - } - - if (G_UNLIKELY (*buffer && GST_BUFFER_SIZE (*buffer) != size)) { - GST_WARNING_OBJECT (demux, - "partial pull got %d when expecting %d from offset %" G_GUINT64_FORMAT, - GST_BUFFER_SIZE (*buffer), size, offset); - gst_buffer_unref (*buffer); - ret = GST_FLOW_UNEXPECTED; - *buffer = NULL; - return ret; - } - - return ret; -} - -static GstFlowReturn -gst_flv_demux_pull_tag (GstPad * pad, GstFLVDemux * demux) -{ - GstBuffer *buffer = NULL; - GstFlowReturn ret = GST_FLOW_OK; - - /* Store tag offset */ - demux->cur_tag_offset = demux->offset; - - /* Get the first 4 bytes to identify tag type and size */ - if (G_UNLIKELY ((ret = gst_flv_demux_pull_range (demux, pad, demux->offset, - FLV_TAG_TYPE_SIZE, &buffer)) != GST_FLOW_OK)) - goto beach; - - /* Identify tag type */ - ret = gst_flv_parse_tag_type (demux, buffer); - - gst_buffer_unref (buffer); - - if (G_UNLIKELY (ret != GST_FLOW_OK)) - goto beach; - - /* Jump over tag type + size */ - demux->offset += FLV_TAG_TYPE_SIZE; - - /* Pull the whole tag */ - if (G_UNLIKELY ((ret = gst_flv_demux_pull_range (demux, pad, demux->offset, - demux->tag_size, &buffer)) != GST_FLOW_OK)) - goto beach; - - switch (demux->state) { - case FLV_STATE_TAG_VIDEO: - ret = gst_flv_parse_tag_video (demux, buffer); - break; - case FLV_STATE_TAG_AUDIO: - ret = gst_flv_parse_tag_audio (demux, buffer); - break; - case FLV_STATE_TAG_SCRIPT: - ret = gst_flv_parse_tag_script (demux, buffer); - break; - default: - GST_WARNING_OBJECT (demux, "unexpected state %d", demux->state); - } - - gst_buffer_unref (buffer); - - /* Jump over that part we've just parsed */ - demux->offset += demux->tag_size; - - /* Make sure we reinitialize the tag size */ - demux->tag_size = 0; - - /* Ready for the next tag */ - demux->state = FLV_STATE_TAG_TYPE; - - if (G_UNLIKELY (ret == GST_FLOW_NOT_LINKED)) { - /* If either audio or video is linked we return GST_FLOW_OK */ - if (demux->audio_linked || demux->video_linked) { - ret = GST_FLOW_OK; - } else { - GST_WARNING_OBJECT (demux, "parsing this tag returned not-linked and " - "neither video nor audio are linked"); - } - } - -beach: - return ret; -} - -static GstFlowReturn -gst_flv_demux_pull_header (GstPad * pad, GstFLVDemux * demux) -{ - GstBuffer *buffer = NULL; - GstFlowReturn ret = GST_FLOW_OK; - - /* Get the first 9 bytes */ - if (G_UNLIKELY ((ret = gst_flv_demux_pull_range (demux, pad, demux->offset, - FLV_HEADER_SIZE, &buffer)) != GST_FLOW_OK)) - goto beach; - - ret = gst_flv_parse_header (demux, buffer); - - gst_buffer_unref (buffer); - - /* Jump over the header now */ - demux->offset += FLV_HEADER_SIZE; - demux->state = FLV_STATE_TAG_TYPE; - -beach: - return ret; -} - -static GstFlowReturn -gst_flv_demux_seek_to_prev_keyframe (GstFLVDemux * demux) -{ - return GST_FLOW_OK; -} - -static gboolean -gst_flv_demux_push_src_event (GstFLVDemux * demux, GstEvent * event) -{ - gboolean ret = TRUE; - - if (demux->audio_pad) - ret |= gst_pad_push_event (demux->audio_pad, gst_event_ref (event)); - - if (demux->video_pad) - ret |= gst_pad_push_event (demux->video_pad, gst_event_ref (event)); - - gst_event_unref (event); - - return ret; -} - -static void -gst_flv_demux_create_index (GstFLVDemux * demux) -{ - gint64 size; - GstFormat fmt = GST_FORMAT_BYTES; - size_t tag_size; - guint64 old_offset; - GstBuffer *buffer; - GstFlowReturn ret; - - if (!gst_pad_query_peer_duration (demux->sinkpad, &fmt, &size) || - fmt != GST_FORMAT_BYTES) - return; - - old_offset = demux->offset; - - while ((ret = - gst_flv_demux_pull_range (demux, demux->sinkpad, demux->offset, 12, - &buffer)) == GST_FLOW_OK) { - if (gst_flv_parse_tag_timestamp (demux, buffer, - &tag_size) == GST_CLOCK_TIME_NONE) { - gst_buffer_unref (buffer); - break; - } - - gst_buffer_unref (buffer); - demux->offset += tag_size; - } - - demux->offset = old_offset; -} - -static void -gst_flv_demux_loop (GstPad * pad) -{ - GstFLVDemux *demux = NULL; - GstFlowReturn ret = GST_FLOW_OK; - - demux = GST_FLV_DEMUX (gst_pad_get_parent (pad)); - - if (demux->segment.rate >= 0) { - /* pull in data */ - switch (demux->state) { - case FLV_STATE_TAG_TYPE: - ret = gst_flv_demux_pull_tag (pad, demux); - break; - case FLV_STATE_DONE: - ret = GST_FLOW_UNEXPECTED; - break; - default: - ret = gst_flv_demux_pull_header (pad, demux); - if (ret == GST_FLOW_OK) - gst_flv_demux_create_index (demux); - - } - - /* pause if something went wrong */ - if (G_UNLIKELY (ret != GST_FLOW_OK)) - goto pause; - - /* check EOS condition */ - if ((demux->segment.flags & GST_SEEK_FLAG_SEGMENT) && - (demux->segment.stop != -1) && - (demux->segment.last_stop >= demux->segment.stop)) { - ret = GST_FLOW_UNEXPECTED; - goto pause; - } - } else { /* Reverse playback */ - /* pull in data */ - switch (demux->state) { - case FLV_STATE_TAG_TYPE: - ret = gst_flv_demux_pull_tag (pad, demux); - /* When packet parsing returns UNEXPECTED that means we ve reached the - point where we want to go to the previous keyframe. This is either - the last FLV tag or the keyframe we used last time */ - if (ret == GST_FLOW_UNEXPECTED) { - ret = gst_flv_demux_seek_to_prev_keyframe (demux); - demux->state = FLV_STATE_TAG_TYPE; - } - break; - default: - ret = gst_flv_demux_pull_header (pad, demux); - if (ret == GST_FLOW_OK) - gst_flv_demux_create_index (demux); - } - - /* pause if something went wrong */ - if (G_UNLIKELY (ret != GST_FLOW_OK)) - goto pause; - - /* check EOS condition */ - if (demux->segment.last_stop <= demux->segment.start) { - ret = GST_FLOW_UNEXPECTED; - goto pause; - } - } - - gst_object_unref (demux); - - return; - -pause: - { - const gchar *reason = gst_flow_get_name (ret); - - GST_LOG_OBJECT (demux, "pausing task, reason %s", reason); - gst_pad_pause_task (pad); - - if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) { - if (ret == GST_FLOW_UNEXPECTED) { - /* perform EOS logic */ - gst_element_no_more_pads (GST_ELEMENT_CAST (demux)); - if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT) { - gint64 stop; - - /* for segment playback we need to post when (in stream time) - * we stopped, this is either stop (when set) or the duration. */ - if ((stop = demux->segment.stop) == -1) - stop = demux->segment.duration; - - if (demux->segment.rate >= 0) { - GST_LOG_OBJECT (demux, "Sending segment done, at end of segment"); - gst_element_post_message (GST_ELEMENT_CAST (demux), - gst_message_new_segment_done (GST_OBJECT_CAST (demux), - GST_FORMAT_TIME, stop)); - } else { /* Reverse playback */ - GST_LOG_OBJECT (demux, "Sending segment done, at beginning of " - "segment"); - gst_element_post_message (GST_ELEMENT_CAST (demux), - gst_message_new_segment_done (GST_OBJECT_CAST (demux), - GST_FORMAT_TIME, demux->segment.start)); - } - } else { - /* normal playback, send EOS to all linked pads */ - gst_element_no_more_pads (GST_ELEMENT (demux)); - GST_LOG_OBJECT (demux, "Sending EOS, at end of stream"); - if (!gst_flv_demux_push_src_event (demux, gst_event_new_eos ())) - GST_WARNING_OBJECT (demux, "failed pushing EOS on streams"); - } - } else { - GST_ELEMENT_ERROR (demux, STREAM, FAILED, - ("Internal data stream error."), - ("stream stopped, reason %s", reason)); - gst_flv_demux_push_src_event (demux, gst_event_new_eos ()); - } - } - gst_object_unref (demux); - return; - } -} - -static guint64 -gst_flv_demux_find_offset (GstFLVDemux * demux, GstSegment * segment) -{ - gint64 bytes = 0; - gint64 time = 0; - GstIndexEntry *entry; - - g_return_val_if_fail (segment != NULL, 0); - - time = segment->start; - - if (demux->index) { - /* Let's check if we have an index entry for that seek time */ - entry = gst_index_get_assoc_entry (demux->index, demux->index_id, - GST_INDEX_LOOKUP_BEFORE, GST_ASSOCIATION_FLAG_KEY_UNIT, - GST_FORMAT_TIME, time); - - if (entry) { - gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &bytes); - gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &time); - - GST_DEBUG_OBJECT (demux, "found index entry for %" GST_TIME_FORMAT - " at %" GST_TIME_FORMAT ", seeking to %" G_GINT64_FORMAT, - GST_TIME_ARGS (segment->start), GST_TIME_ARGS (time), bytes); - - /* Key frame seeking */ - if (segment->flags & GST_SEEK_FLAG_KEY_UNIT) { - /* Adjust the segment so that the keyframe fits in */ - if (time < segment->start) { - segment->start = segment->time = time; - } - segment->last_stop = time; - } - } else { - GST_DEBUG_OBJECT (demux, "no index entry found for %" GST_TIME_FORMAT, - GST_TIME_ARGS (segment->start)); - } - } - - return bytes; -} - -static gboolean -gst_flv_demux_handle_seek_push (GstFLVDemux * demux, GstEvent * event) -{ - GstFormat format; - GstSeekFlags flags; - GstSeekType start_type, stop_type; - gint64 start, stop; - gdouble rate; - gboolean update, flush, keyframe, ret; - GstSegment seeksegment; - - gst_event_parse_seek (event, &rate, &format, &flags, - &start_type, &start, &stop_type, &stop); - - if (format != GST_FORMAT_TIME) - goto wrong_format; - - flush = !!(flags & GST_SEEK_FLAG_FLUSH); - keyframe = !!(flags & GST_SEEK_FLAG_KEY_UNIT); - - /* Work on a copy until we are sure the seek succeeded. */ - memcpy (&seeksegment, &demux->segment, sizeof (GstSegment)); - - GST_DEBUG_OBJECT (demux, "segment before configure %" GST_SEGMENT_FORMAT, - &demux->segment); - - /* Apply the seek to our segment */ - gst_segment_set_seek (&seeksegment, rate, format, flags, - start_type, start, stop_type, stop, &update); - - GST_DEBUG_OBJECT (demux, "segment configured %" GST_SEGMENT_FORMAT, - &seeksegment); - - if (flush || seeksegment.last_stop != demux->segment.last_stop) { - /* Do the actual seeking */ - guint64 offset = gst_flv_demux_find_offset (demux, &seeksegment); - - GST_DEBUG_OBJECT (demux, "generating an upstream seek at position %" - G_GUINT64_FORMAT, offset); - ret = gst_pad_push_event (demux->sinkpad, - gst_event_new_seek (seeksegment.rate, GST_FORMAT_BYTES, - seeksegment.flags | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, - offset, GST_SEEK_TYPE_NONE, 0)); - if (G_UNLIKELY (!ret)) { - GST_WARNING_OBJECT (demux, "upstream seek failed"); - } - - /* Tell all the stream we moved to a different position (discont) */ - demux->audio_need_discont = TRUE; - demux->video_need_discont = TRUE; - } else { - ret = TRUE; - } - - if (ret) { - /* Ok seek succeeded, take the newly configured segment */ - memcpy (&demux->segment, &seeksegment, sizeof (GstSegment)); - - /* Tell all the stream a new segment is needed */ - demux->audio_need_segment = TRUE; - demux->video_need_segment = TRUE; - /* Clean any potential newsegment event kept for the streams. The first - * stream needing a new segment will create a new one. */ - if (G_UNLIKELY (demux->new_seg_event)) { - gst_event_unref (demux->new_seg_event); - demux->new_seg_event = NULL; - } - gst_event_unref (event); - } else { - ret = gst_pad_push_event (demux->sinkpad, event); - } - - return ret; - -/* ERRORS */ -wrong_format: - { - GST_WARNING_OBJECT (demux, "we only support seeking in TIME format"); - return gst_pad_push_event (demux->sinkpad, event); - } -} - -static gboolean -gst_flv_demux_handle_seek_pull (GstFLVDemux * demux, GstEvent * event) -{ - GstFormat format; - GstSeekFlags flags; - GstSeekType start_type, stop_type; - gint64 start, stop; - gdouble rate; - gboolean update, flush, keyframe, ret; - GstSegment seeksegment; - - gst_event_parse_seek (event, &rate, &format, &flags, - &start_type, &start, &stop_type, &stop); - - gst_event_unref (event); - - if (format != GST_FORMAT_TIME) - goto wrong_format; - - flush = !!(flags & GST_SEEK_FLAG_FLUSH); - keyframe = !!(flags & GST_SEEK_FLAG_KEY_UNIT); - - if (flush) { - /* Flush start up and downstream to make sure data flow and loops are - idle */ - gst_flv_demux_push_src_event (demux, gst_event_new_flush_start ()); - gst_pad_push_event (demux->sinkpad, gst_event_new_flush_start ()); - } else { - /* Pause the pulling task */ - gst_pad_pause_task (demux->sinkpad); - } - - /* Take the stream lock */ - GST_PAD_STREAM_LOCK (demux->sinkpad); - - if (flush) { - /* Stop flushing upstream we need to pull */ - gst_pad_push_event (demux->sinkpad, gst_event_new_flush_stop ()); - } - - /* Work on a copy until we are sure the seek succeeded. */ - memcpy (&seeksegment, &demux->segment, sizeof (GstSegment)); - - GST_DEBUG_OBJECT (demux, "segment before configure %" GST_SEGMENT_FORMAT, - &demux->segment); - - /* Apply the seek to our segment */ - gst_segment_set_seek (&seeksegment, rate, format, flags, - start_type, start, stop_type, stop, &update); - - GST_DEBUG_OBJECT (demux, "segment configured %" GST_SEGMENT_FORMAT, - &seeksegment); - - if (flush || seeksegment.last_stop != demux->segment.last_stop) { - /* Do the actual seeking */ - demux->offset = gst_flv_demux_find_offset (demux, &seeksegment); - - /* Tell all the stream we moved to a different position (discont) */ - demux->audio_need_discont = TRUE; - demux->video_need_discont = TRUE; - - /* If we seeked at the beginning of the file parse the header again */ - if (G_UNLIKELY (!demux->offset)) { - demux->state = FLV_STATE_HEADER; - } else { /* or parse a tag */ - demux->state = FLV_STATE_TAG_TYPE; - } - ret = TRUE; - } else { - ret = TRUE; - } - - if (G_UNLIKELY (demux->close_seg_event)) { - gst_event_unref (demux->close_seg_event); - demux->close_seg_event = NULL; - } - - if (flush) { - /* Stop flushing, the sinks are at time 0 now */ - gst_flv_demux_push_src_event (demux, gst_event_new_flush_stop ()); - } else { - GST_DEBUG_OBJECT (demux, "closing running segment %" GST_SEGMENT_FORMAT, - &demux->segment); - - /* Close the current segment for a linear playback */ - if (demux->segment.rate >= 0) { - /* for forward playback, we played from start to last_stop */ - demux->close_seg_event = gst_event_new_new_segment (TRUE, - demux->segment.rate, demux->segment.format, - demux->segment.start, demux->segment.last_stop, demux->segment.time); - } else { - gint64 stop; - - if ((stop = demux->segment.stop) == -1) - stop = demux->segment.duration; - - /* for reverse playback, we played from stop to last_stop. */ - demux->close_seg_event = gst_event_new_new_segment (TRUE, - demux->segment.rate, demux->segment.format, - demux->segment.last_stop, stop, demux->segment.last_stop); - } - } - - if (ret) { - /* Ok seek succeeded, take the newly configured segment */ - memcpy (&demux->segment, &seeksegment, sizeof (GstSegment)); - - /* Notify about the start of a new segment */ - if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT) { - gst_element_post_message (GST_ELEMENT (demux), - gst_message_new_segment_start (GST_OBJECT (demux), - demux->segment.format, demux->segment.last_stop)); - } - - /* Tell all the stream a new segment is needed */ - demux->audio_need_segment = TRUE; - demux->video_need_segment = TRUE; - /* Clean any potential newsegment event kept for the streams. The first - * stream needing a new segment will create a new one. */ - if (G_UNLIKELY (demux->new_seg_event)) { - gst_event_unref (demux->new_seg_event); - demux->new_seg_event = NULL; - } - } - - gst_pad_start_task (demux->sinkpad, - (GstTaskFunction) gst_flv_demux_loop, demux->sinkpad); - - GST_PAD_STREAM_UNLOCK (demux->sinkpad); - - return ret; - - /* ERRORS */ -wrong_format: - { - GST_WARNING_OBJECT (demux, "we only support seeking in TIME format"); - return FALSE; - } -} - -/* If we can pull that's prefered */ -static gboolean -gst_flv_demux_sink_activate (GstPad * sinkpad) -{ - if (gst_pad_check_pull_range (sinkpad)) { - return gst_pad_activate_pull (sinkpad, TRUE); - } else { - return gst_pad_activate_push (sinkpad, TRUE); - } -} - -/* This function gets called when we activate ourselves in push mode. - * We cannot seek (ourselves) in the stream */ -static gboolean -gst_flv_demux_sink_activate_push (GstPad * sinkpad, gboolean active) -{ - GstFLVDemux *demux; - - demux = GST_FLV_DEMUX (gst_pad_get_parent (sinkpad)); - - demux->random_access = FALSE; - - gst_object_unref (demux); - - return TRUE; -} - -/* this function gets called when we activate ourselves in pull mode. - * We can perform random access to the resource and we start a task - * to start reading */ -static gboolean -gst_flv_demux_sink_activate_pull (GstPad * sinkpad, gboolean active) -{ - GstFLVDemux *demux; - - demux = GST_FLV_DEMUX (gst_pad_get_parent (sinkpad)); - - if (active) { - demux->random_access = TRUE; - gst_object_unref (demux); - return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_flv_demux_loop, - sinkpad); - } else { - demux->random_access = FALSE; - gst_object_unref (demux); - return gst_pad_stop_task (sinkpad); - } -} - -static gboolean -gst_flv_demux_sink_event (GstPad * pad, GstEvent * event) -{ - GstFLVDemux *demux; - gboolean ret = FALSE; - - demux = GST_FLV_DEMUX (gst_pad_get_parent (pad)); - - GST_DEBUG_OBJECT (demux, "handling event %s", GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_START: - GST_DEBUG_OBJECT (demux, "trying to force chain function to exit"); - demux->flushing = TRUE; - ret = gst_flv_demux_push_src_event (demux, event); - break; - case GST_EVENT_FLUSH_STOP: - GST_DEBUG_OBJECT (demux, "flushing FLV demuxer"); - gst_flv_demux_flush (demux, TRUE); - ret = gst_flv_demux_push_src_event (demux, event); - break; - case GST_EVENT_EOS: - GST_DEBUG_OBJECT (demux, "received EOS"); - if (demux->index) { - GST_DEBUG_OBJECT (demux, "committing index"); - gst_index_commit (demux->index, demux->index_id); - } - gst_element_no_more_pads (GST_ELEMENT (demux)); - if (!gst_flv_demux_push_src_event (demux, event)) - GST_WARNING_OBJECT (demux, "failed pushing EOS on streams"); - ret = TRUE; - break; - case GST_EVENT_NEWSEGMENT: - { - GstFormat format; - gdouble rate; - gint64 start, stop, time; - gboolean update; - - GST_DEBUG_OBJECT (demux, "received new segment"); - - gst_event_parse_new_segment (event, &update, &rate, &format, &start, - &stop, &time); - - if (format == GST_FORMAT_TIME) { - /* time segment, this is perfect, copy over the values. */ - gst_segment_set_newsegment (&demux->segment, update, rate, format, - start, stop, time); - - GST_DEBUG_OBJECT (demux, "NEWSEGMENT: %" GST_SEGMENT_FORMAT, - &demux->segment); - - /* and forward */ - ret = gst_flv_demux_push_src_event (demux, event); - } else { - /* non-time format */ - demux->audio_need_segment = TRUE; - demux->video_need_segment = TRUE; - ret = TRUE; - gst_event_unref (event); - } - break; - } - default: - ret = gst_flv_demux_push_src_event (demux, event); - break; - } - - gst_object_unref (demux); - - return ret; -} - -gboolean -gst_flv_demux_src_event (GstPad * pad, GstEvent * event) -{ - GstFLVDemux *demux; - gboolean ret = FALSE; - - demux = GST_FLV_DEMUX (gst_pad_get_parent (pad)); - - GST_DEBUG_OBJECT (demux, "handling event %s", GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK: - if (demux->random_access) { - ret = gst_flv_demux_handle_seek_pull (demux, event); - } else { - ret = gst_flv_demux_handle_seek_push (demux, event); - } - break; - default: - ret = gst_pad_push_event (demux->sinkpad, event); - break; - } - - gst_object_unref (demux); - - return ret; -} - -gboolean -gst_flv_demux_query (GstPad * pad, GstQuery * query) -{ - gboolean res = TRUE; - GstFLVDemux *demux; - - demux = GST_FLV_DEMUX (gst_pad_get_parent (pad)); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_DURATION: - { - GstFormat format; - - gst_query_parse_duration (query, &format, NULL); - - /* duration is time only */ - if (format != GST_FORMAT_TIME) { - GST_DEBUG_OBJECT (demux, "duration query only supported for time " - "format"); - res = FALSE; - goto beach; - } - - GST_DEBUG_OBJECT (pad, "duration query, replying %" GST_TIME_FORMAT, - GST_TIME_ARGS (demux->duration)); - - gst_query_set_duration (query, GST_FORMAT_TIME, demux->duration); - - break; - } - case GST_QUERY_POSITION: - { - GstFormat format; - - gst_query_parse_position (query, &format, NULL); - - /* position is time only */ - if (format != GST_FORMAT_TIME) { - GST_DEBUG_OBJECT (demux, "position query only supported for time " - "format"); - res = FALSE; - goto beach; - } - - GST_DEBUG_OBJECT (pad, "position query, replying %" GST_TIME_FORMAT, - GST_TIME_ARGS (demux->segment.last_stop)); - - gst_query_set_duration (query, GST_FORMAT_TIME, demux->segment.last_stop); - - break; - } - - case GST_QUERY_LATENCY: - default: - { - GstPad *peer; - - if ((peer = gst_pad_get_peer (demux->sinkpad))) { - /* query latency on peer pad */ - res = gst_pad_query (peer, query); - gst_object_unref (peer); - } else { - /* no peer, we don't know */ - res = FALSE; - } - break; - } - } - -beach: - gst_object_unref (demux); - - return res; -} - -static GstStateChangeReturn -gst_flv_demux_change_state (GstElement * element, GstStateChange transition) -{ - GstFLVDemux *demux; - GstStateChangeReturn ret; - - demux = GST_FLV_DEMUX (element); - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - /* If this is our own index destroy it as the - * old entries might be wrong for the new stream */ - if (demux->own_index) { - gst_object_unref (demux->index); - demux->index = NULL; - demux->own_index = FALSE; - } - - /* If no index was created, generate one */ - if (G_UNLIKELY (!demux->index)) { - GST_DEBUG_OBJECT (demux, "no index provided creating our own"); - - demux->index = gst_index_factory_make ("memindex"); - - gst_index_get_writer_id (demux->index, GST_OBJECT (demux), - &demux->index_id); - demux->own_index = TRUE; - } - gst_flv_demux_cleanup (demux); - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - if (ret == GST_STATE_CHANGE_FAILURE) - return ret; - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_flv_demux_cleanup (demux); - break; - default: - break; - } - - return ret; -} - -static void -gst_flv_demux_set_index (GstElement * element, GstIndex * index) -{ - GstFLVDemux *demux = GST_FLV_DEMUX (element); - - GST_OBJECT_LOCK (demux); - if (demux->index) - gst_object_unref (demux->index); - demux->index = gst_object_ref (index); - GST_OBJECT_UNLOCK (demux); - - gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id); - demux->own_index = FALSE; -} - -static GstIndex * -gst_flv_demux_get_index (GstElement * element) -{ - GstIndex *result = NULL; - - GstFLVDemux *demux = GST_FLV_DEMUX (element); - - GST_OBJECT_LOCK (demux); - if (demux->index) - result = gst_object_ref (demux->index); - GST_OBJECT_UNLOCK (demux); - - return result; -} - -static void -gst_flv_demux_dispose (GObject * object) -{ - GstFLVDemux *demux = GST_FLV_DEMUX (object); - - GST_DEBUG_OBJECT (demux, "disposing FLV demuxer"); - - if (demux->adapter) { - gst_adapter_clear (demux->adapter); - g_object_unref (demux->adapter); - demux->adapter = NULL; - } - - if (demux->taglist) { - gst_tag_list_free (demux->taglist); - demux->taglist = NULL; - } - - if (demux->new_seg_event) { - gst_event_unref (demux->new_seg_event); - demux->new_seg_event = NULL; - } - - if (demux->close_seg_event) { - gst_event_unref (demux->close_seg_event); - demux->close_seg_event = NULL; - } - - if (demux->audio_codec_data) { - gst_buffer_unref (demux->audio_codec_data); - demux->audio_codec_data = NULL; - } - - if (demux->video_codec_data) { - gst_buffer_unref (demux->video_codec_data); - demux->video_codec_data = NULL; - } - - if (demux->audio_pad) { - gst_object_unref (demux->audio_pad); - demux->audio_pad = NULL; - } - - if (demux->video_pad) { - gst_object_unref (demux->video_pad); - demux->video_pad = NULL; - } - - if (demux->index) { - gst_object_unref (demux->index); - demux->index = NULL; - } - - if (demux->times) { - g_array_free (demux->times, TRUE); - demux->times = NULL; - } - - if (demux->filepositions) { - g_array_free (demux->filepositions, TRUE); - demux->filepositions = NULL; - } - - GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object)); -} - -static void -gst_flv_demux_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&flv_sink_template)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&audio_src_template)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&video_src_template)); - gst_element_class_set_details_simple (element_class, "FLV Demuxer", - "Codec/Demuxer", - "Demux FLV feeds into digital streams", - "Julien Moutte <julien@moutte.net>"); -} - -static void -gst_flv_demux_class_init (GstFLVDemuxClass * klass) -{ - GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_flv_demux_dispose); - - gstelement_class->change_state = - GST_DEBUG_FUNCPTR (gst_flv_demux_change_state); - gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_flv_demux_set_index); - gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_flv_demux_get_index); -} - -static void -gst_flv_demux_init (GstFLVDemux * demux, GstFLVDemuxClass * g_class) -{ - demux->sinkpad = - gst_pad_new_from_static_template (&flv_sink_template, "sink"); - - gst_pad_set_event_function (demux->sinkpad, - GST_DEBUG_FUNCPTR (gst_flv_demux_sink_event)); - gst_pad_set_chain_function (demux->sinkpad, - GST_DEBUG_FUNCPTR (gst_flv_demux_chain)); - gst_pad_set_activate_function (demux->sinkpad, - GST_DEBUG_FUNCPTR (gst_flv_demux_sink_activate)); - gst_pad_set_activatepull_function (demux->sinkpad, - GST_DEBUG_FUNCPTR (gst_flv_demux_sink_activate_pull)); - gst_pad_set_activatepush_function (demux->sinkpad, - GST_DEBUG_FUNCPTR (gst_flv_demux_sink_activate_push)); - - gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad); - - demux->adapter = gst_adapter_new (); - demux->taglist = gst_tag_list_new (); - gst_segment_init (&demux->segment, GST_FORMAT_TIME); - - demux->own_index = FALSE; - - gst_flv_demux_cleanup (demux); -} - -static gboolean -plugin_init (GstPlugin * plugin) -{ - GST_DEBUG_CATEGORY_INIT (flvdemux_debug, "flvdemux", 0, "FLV demuxer"); - - if (!gst_element_register (plugin, "flvdemux", GST_RANK_PRIMARY, - gst_flv_demux_get_type ()) || - !gst_element_register (plugin, "flvmux", GST_RANK_NONE, - gst_flv_mux_get_type ())) - return FALSE; - - return TRUE; -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, - "flv", "FLV muxing and demuxing plugin", - plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/gst/flv/gstflvdemux.h b/gst/flv/gstflvdemux.h deleted file mode 100644 index 72c0bcd4..00000000 --- a/gst/flv/gstflvdemux.h +++ /dev/null @@ -1,130 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Julien Moutte <julien@moutte.net> - * - * 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 __FLV_DEMUX_H__ -#define __FLV_DEMUX_H__ - -#include <gst/gst.h> -#include <gst/base/gstadapter.h> - -G_BEGIN_DECLS -#define GST_TYPE_FLV_DEMUX \ - (gst_flv_demux_get_type()) -#define GST_FLV_DEMUX(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FLV_DEMUX,GstFLVDemux)) -#define GST_FLV_DEMUX_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FLV_DEMUX,GstFLVDemuxClass)) -#define GST_IS_FLV_DEMUX(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FLV_DEMUX)) -#define GST_IS_FLV_DEMUX_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FLV_DEMUX)) -typedef struct _GstFLVDemux GstFLVDemux; -typedef struct _GstFLVDemuxClass GstFLVDemuxClass; - -typedef enum -{ - FLV_STATE_HEADER, - FLV_STATE_TAG_TYPE, - FLV_STATE_TAG_VIDEO, - FLV_STATE_TAG_AUDIO, - FLV_STATE_TAG_SCRIPT, - FLV_STATE_DONE, - FLV_STATE_NONE -} GstFLVDemuxState; - -struct _GstFLVDemux -{ - GstElement element; - - GstPad *sinkpad; - - GstPad *audio_pad; - GstPad *video_pad; - - /* <private> */ - - GstIndex *index; - gint index_id; - gboolean own_index; - - GArray * times; - GArray * filepositions; - - GstAdapter *adapter; - - GstSegment segment; - - GstEvent *close_seg_event; - GstEvent *new_seg_event; - - GstTagList *taglist; - - GstFLVDemuxState state; - - guint64 offset; - guint64 cur_tag_offset; - GstClockTime duration; - guint64 tag_size; - guint64 tag_data_size; - - /* Audio infos */ - guint16 rate; - guint16 channels; - guint16 width; - guint16 audio_codec_tag; - guint64 audio_offset; - gboolean audio_need_discont; - gboolean audio_need_segment; - gboolean audio_linked; - GstBuffer * audio_codec_data; - - /* Video infos */ - guint32 w; - guint32 h; - guint32 par_x; - guint32 par_y; - guint16 video_codec_tag; - guint64 video_offset; - gboolean video_need_discont; - gboolean video_need_segment; - gboolean video_linked; - gboolean got_par; - GstBuffer * video_codec_data; - - gboolean random_access; - gboolean need_header; - gboolean has_audio; - gboolean has_video; - gboolean push_tags; - gboolean strict; - gboolean flushing; -}; - -struct _GstFLVDemuxClass -{ - GstElementClass parent_class; -}; - -GType gst_flv_demux_get_type (void); - -gboolean gst_flv_demux_query (GstPad * pad, GstQuery * query); -gboolean gst_flv_demux_src_event (GstPad * pad, GstEvent * event); - -G_END_DECLS -#endif /* __FLV_DEMUX_H__ */ diff --git a/gst/flv/gstflvmux.c b/gst/flv/gstflvmux.c deleted file mode 100644 index dd27276a..00000000 --- a/gst/flv/gstflvmux.c +++ /dev/null @@ -1,1040 +0,0 @@ -/* GStreamer - * - * Copyright (c) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk> - * - * 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. - */ - -/** - * SECTION:element-flvmux - * - * flvmux muxes different streams into an FLV file. - * - * <refsect2> - * <title>Example launch line</title> - * |[ - * gst-launch -v filesrc location=/path/to/audio ! decodebin2 ! queue ! flvmux name=m ! filesink location=file.flv filesrc location=/path/to/video ! decodebin2 ! queue ! m. - * ]| This pipeline muxes an audio and video file into a single FLV file. - * </refsect2> - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <math.h> -#include <string.h> - -#include "gstflvmux.h" - -GST_DEBUG_CATEGORY_STATIC (flvmux_debug); -#define GST_CAT_DEFAULT flvmux_debug - -static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("video/x-flv") - ); - -static GstStaticPadTemplate videosink_templ = GST_STATIC_PAD_TEMPLATE ("video", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS ("video/x-flash-video; " - "video/x-flash-screen; " - "video/x-vp6-flash; " "video/x-vp6-alpha; " "video/x-h264;") - ); - -static GstStaticPadTemplate audiosink_templ = GST_STATIC_PAD_TEMPLATE ("audio", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS - ("audio/x-adpcm, layout = (string) swf, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; " - "audio/mpeg, mpegversion = (int) 1, layer = (int) 3, channels = (int) { 1, 2 }, rate = (int) { 5512, 8000, 11025, 22050, 44100 }, parsed = (boolean) TRUE; " - "audio/mpeg, mpegversion = (int) 4, framed = (boolean) TRUE; " - "audio/x-nellymoser, channels = (int) { 1, 2 }, rate = (int) { 5512, 8000, 11025, 16000, 22050, 44100 }; " - "audio/x-raw-int, endianness = (int) LITTLE_ENDIAN, channels = (int) { 1, 2 }, width = (int) 8, depth = (int) 8, rate = (int) { 5512, 11025, 22050, 44100 }, signed = (boolean) FALSE; " - "audio/x-raw-int, endianness = (int) LITTLE_ENDIAN, channels = (int) { 1, 2 }, width = (int) 16, depth = (int) 16, rate = (int) { 5512, 11025, 22050, 44100 }, signed = (boolean) TRUE; " - "audio/x-alaw, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; " - "audio/x-mulaw, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; " - "audio/x-speex, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 };") - ); - -#define _do_init(type) \ - G_STMT_START{ \ - static const GInterfaceInfo tag_setter_info = { \ - NULL, \ - NULL, \ - NULL \ - }; \ - g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, \ - &tag_setter_info); \ - }G_STMT_END - -GST_BOILERPLATE_FULL (GstFlvMux, gst_flv_mux, GstElement, GST_TYPE_ELEMENT, - _do_init); - -static void gst_flv_mux_finalize (GObject * object); -static GstFlowReturn -gst_flv_mux_collected (GstCollectPads * pads, gpointer user_data); - -static gboolean gst_flv_mux_handle_src_event (GstPad * pad, GstEvent * event); -static GstPad *gst_flv_mux_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * name); -static void gst_flv_mux_release_pad (GstElement * element, GstPad * pad); - -static GstStateChangeReturn -gst_flv_mux_change_state (GstElement * element, GstStateChange transition); - -static void gst_flv_mux_reset (GstElement * element); - -static void -gst_flv_mux_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&videosink_templ)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&audiosink_templ)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&src_templ)); - gst_element_class_set_details_simple (element_class, "FLV muxer", - "Codec/Muxer", - "Muxes video/audio streams into a FLV stream", - "Sebastian Dröge <sebastian.droege@collabora.co.uk>"); - - GST_DEBUG_CATEGORY_INIT (flvmux_debug, "flvmux", 0, "FLV muxer"); -} - -static void -gst_flv_mux_class_init (GstFlvMuxClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - - GST_DEBUG_CATEGORY_INIT (flvmux_debug, "flvmux", 0, "FLV muxer"); - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; - - gobject_class->finalize = gst_flv_mux_finalize; - - gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_flv_mux_change_state); - gstelement_class->request_new_pad = - GST_DEBUG_FUNCPTR (gst_flv_mux_request_new_pad); - gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_flv_mux_release_pad); -} - -static void -gst_flv_mux_init (GstFlvMux * mux, GstFlvMuxClass * g_class) -{ - mux->srcpad = gst_pad_new_from_static_template (&src_templ, "src"); - gst_pad_set_event_function (mux->srcpad, gst_flv_mux_handle_src_event); - gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad); - - mux->collect = gst_collect_pads_new (); - gst_collect_pads_set_function (mux->collect, - (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_flv_mux_collected), mux); - - gst_flv_mux_reset (GST_ELEMENT (mux)); -} - -static void -gst_flv_mux_finalize (GObject * object) -{ - GstFlvMux *mux = GST_FLV_MUX (object); - - gst_object_unref (mux->collect); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -gst_flv_mux_reset (GstElement * element) -{ - GstFlvMux *mux = GST_FLV_MUX (element); - GSList *sl; - - while ((sl = mux->collect->data) != NULL) { - GstFlvPad *cpad = (GstFlvPad *) sl->data; - - if (cpad->audio_codec_data) - gst_buffer_unref (cpad->audio_codec_data); - if (cpad->video_codec_data) - gst_buffer_unref (cpad->video_codec_data); - - gst_collect_pads_remove_pad (mux->collect, cpad->collect.pad); - } - - if (mux->tags) - gst_tag_list_free (mux->tags); - mux->tags = NULL; - - mux->state = GST_FLV_MUX_STATE_HEADER; -} - -static gboolean -gst_flv_mux_handle_src_event (GstPad * pad, GstEvent * event) -{ - GstEventType type; - - type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN; - - switch (type) { - case GST_EVENT_SEEK: - /* disable seeking for now */ - return FALSE; - default: - break; - } - - return gst_pad_event_default (pad, event); -} - -static gboolean -gst_flv_mux_handle_sink_event (GstPad * pad, GstEvent * event) -{ - GstFlvMux *mux = GST_FLV_MUX (gst_pad_get_parent (pad)); - gboolean ret = TRUE; - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_TAG:{ - GstTagList *tags; - - if (!mux->tags) - mux->tags = gst_tag_list_new (); - - gst_event_parse_tag (event, &tags); - if (tags) { - gst_tag_list_insert (mux->tags, tags, - gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux))); - } - - break; - } - case GST_EVENT_NEWSEGMENT: - /* We don't support NEWSEGMENT events */ - ret = FALSE; - gst_event_unref (event); - break; - default: - break; - } - - /* now GstCollectPads can take care of the rest, e.g. EOS */ - if (ret) - ret = mux->collect_event (pad, event); - gst_object_unref (mux); - - return ret; -} - -static gboolean -gst_flv_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps) -{ - GstFlvMux *mux = GST_FLV_MUX (gst_pad_get_parent (pad)); - GstFlvPad *cpad = (GstFlvPad *) gst_pad_get_element_private (pad); - gboolean ret = TRUE; - GstStructure *s; - - s = gst_caps_get_structure (caps, 0); - - if (strcmp (gst_structure_get_name (s), "video/x-flash-video") == 0) { - cpad->video_codec = 2; - } else if (strcmp (gst_structure_get_name (s), "video/x-flash-screen") == 0) { - cpad->video_codec = 3; - } else if (strcmp (gst_structure_get_name (s), "video/x-vp6-flash") == 0) { - cpad->video_codec = 4; - } else if (strcmp (gst_structure_get_name (s), "video/x-vp6-alpha") == 0) { - cpad->video_codec = 5; - } else if (strcmp (gst_structure_get_name (s), "video/x-h264") == 0) { - cpad->video_codec = 7; - } else { - ret = FALSE; - } - - if (ret && gst_structure_has_field (s, "codec_data")) { - const GValue *val = gst_structure_get_value (s, "codec_data"); - - if (val) { - cpad->video_codec_data = gst_buffer_ref (gst_value_get_buffer (val)); - cpad->sent_codec_data = FALSE; - } else { - cpad->sent_codec_data = TRUE; - } - } else { - cpad->sent_codec_data = TRUE; - } - - gst_object_unref (mux); - - return ret; -} - -static gboolean -gst_flv_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps) -{ - GstFlvMux *mux = GST_FLV_MUX (gst_pad_get_parent (pad)); - GstFlvPad *cpad = (GstFlvPad *) gst_pad_get_element_private (pad); - gboolean ret = TRUE; - GstStructure *s; - - s = gst_caps_get_structure (caps, 0); - - if (strcmp (gst_structure_get_name (s), "audio/x-adpcm") == 0) { - const gchar *layout = gst_structure_get_string (s, "layout"); - if (layout && strcmp (layout, "swf") == 0) { - cpad->audio_codec = 1; - } else { - ret = FALSE; - } - } else if (strcmp (gst_structure_get_name (s), "audio/mpeg") == 0) { - gint mpegversion; - - if (gst_structure_get_int (s, "mpegversion", &mpegversion)) { - if (mpegversion == 1) { - gint layer; - - if (gst_structure_get_int (s, "layer", &layer) && layer == 3) { - gint rate; - - if (gst_structure_get_int (s, "rate", &rate) && rate == 8000) - cpad->audio_codec = 14; - else - cpad->audio_codec = 2; - } else { - ret = FALSE; - } - } else if (mpegversion == 4) { - cpad->audio_codec = 10; - } else { - ret = FALSE; - } - } else { - ret = FALSE; - } - } else if (strcmp (gst_structure_get_name (s), "audio/x-nellymoser") == 0) { - gint rate, channels; - - if (gst_structure_get_int (s, "rate", &rate) - && gst_structure_get_int (s, "channels", &channels)) { - if (channels == 1 && rate == 16000) - cpad->audio_codec = 4; - else if (channels == 1 && rate == 8000) - cpad->audio_codec = 5; - } else { - cpad->audio_codec = 6; - } - } else if (strcmp (gst_structure_get_name (s), "audio/x-raw-int") == 0) { - gint endianness; - - if (gst_structure_get_int (s, "endianness", &endianness) - && endianness == G_LITTLE_ENDIAN) - cpad->audio_codec = 3; - else - ret = FALSE; - } else if (strcmp (gst_structure_get_name (s), "audio/x-alaw") == 0) { - cpad->audio_codec = 7; - } else if (strcmp (gst_structure_get_name (s), "audio/x-mulaw") == 0) { - cpad->audio_codec = 8; - } else if (strcmp (gst_structure_get_name (s), "audio/x-speex") == 0) { - cpad->audio_codec = 11; - } else { - ret = FALSE; - } - - if (ret) { - gint rate, channels, width; - - if (gst_structure_get_int (s, "rate", &rate)) { - if (cpad->audio_codec == 10) - cpad->rate = 3; - else if (rate == 5512) - cpad->rate = 0; - else if (rate == 11025) - cpad->rate = 1; - else if (rate == 22050) - cpad->rate = 2; - else if (rate == 44100) - cpad->rate = 3; - else if (rate == 8000 && (cpad->audio_codec == 5 - || cpad->audio_codec == 14)) - cpad->rate = 0; - else if (rate == 16000 && cpad->audio_codec == 4) - cpad->rate = 0; - else - ret = FALSE; - } else if (cpad->audio_codec == 10) { - cpad->rate = 3; - } else { - ret = FALSE; - } - - if (gst_structure_get_int (s, "channels", &channels)) { - if (cpad->audio_codec == 4 || cpad->audio_codec == 5 - || cpad->audio_codec == 6) - cpad->channels = 0; - else if (cpad->audio_codec == 10) - cpad->channels = 1; - else if (channels == 1) - cpad->channels = 0; - else if (channels == 2) - cpad->channels = 1; - else - ret = FALSE; - } else if (cpad->audio_codec == 4 || cpad->audio_codec == 5 - || cpad->audio_codec == 6) { - cpad->channels = 0; - } else if (cpad->audio_codec == 10) { - cpad->channels = 1; - } else { - ret = FALSE; - } - - if (gst_structure_get_int (s, "width", &width)) { - if (cpad->audio_codec != 3) - cpad->width = 1; - else if (width == 8) - cpad->width = 0; - else if (width == 16) - cpad->width = 1; - else - ret = FALSE; - } else if (cpad->audio_codec != 3) { - cpad->width = 1; - } else { - ret = FALSE; - } - } - - if (ret && gst_structure_has_field (s, "codec_data")) { - const GValue *val = gst_structure_get_value (s, "codec_data"); - - if (val) { - cpad->audio_codec_data = gst_buffer_ref (gst_value_get_buffer (val)); - cpad->sent_codec_data = FALSE; - } else { - cpad->sent_codec_data = TRUE; - } - } else { - cpad->sent_codec_data = TRUE; - } - - gst_object_unref (mux); - - return ret; -} - -static GstPad * -gst_flv_mux_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * pad_name) -{ - GstElementClass *klass = GST_ELEMENT_GET_CLASS (element); - GstFlvMux *mux = GST_FLV_MUX (element); - GstFlvPad *cpad; - GstPad *pad = NULL; - const gchar *name = NULL; - GstPadSetCapsFunction setcapsfunc = NULL; - gboolean video; - - if (mux->state != GST_FLV_MUX_STATE_HEADER) { - GST_WARNING_OBJECT (mux, "Can't request pads after writing header"); - return NULL; - } - - if (templ == gst_element_class_get_pad_template (klass, "audio")) { - if (mux->have_audio) { - GST_WARNING_OBJECT (mux, "Already have an audio pad"); - return NULL; - } - mux->have_audio = TRUE; - name = "audio"; - video = FALSE; - setcapsfunc = GST_DEBUG_FUNCPTR (gst_flv_mux_audio_pad_setcaps); - } else if (templ == gst_element_class_get_pad_template (klass, "video")) { - if (mux->have_video) { - GST_WARNING_OBJECT (mux, "Already have a video pad"); - return NULL; - } - mux->have_video = TRUE; - name = "video"; - video = TRUE; - setcapsfunc = GST_DEBUG_FUNCPTR (gst_flv_mux_video_pad_setcaps); - } else { - GST_WARNING_OBJECT (mux, "Invalid template"); - return NULL; - } - - pad = gst_pad_new_from_template (templ, name); - cpad = (GstFlvPad *) - gst_collect_pads_add_pad (mux->collect, pad, sizeof (GstFlvPad)); - - cpad->video = video; - - cpad->audio_codec = G_MAXUINT; - cpad->rate = G_MAXUINT; - cpad->width = G_MAXUINT; - cpad->channels = G_MAXUINT; - cpad->audio_codec_data = NULL; - - cpad->video_codec = G_MAXUINT; - cpad->video_codec_data = NULL; - - cpad->sent_codec_data = FALSE; - - cpad->last_timestamp = 0; - - /* FIXME: hacked way to override/extend the event function of - * GstCollectPads; because it sets its own event function giving the - * element no access to events. - */ - mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (pad); - gst_pad_set_event_function (pad, - GST_DEBUG_FUNCPTR (gst_flv_mux_handle_sink_event)); - - gst_pad_set_setcaps_function (pad, setcapsfunc); - gst_pad_set_active (pad, TRUE); - gst_element_add_pad (element, pad); - - return pad; -} - -static void -gst_flv_mux_release_pad (GstElement * element, GstPad * pad) -{ - GstFlvMux *mux = GST_FLV_MUX (GST_PAD_PARENT (pad)); - GstFlvPad *cpad = (GstFlvPad *) gst_pad_get_element_private (pad); - - if (cpad && cpad->audio_codec_data) - gst_buffer_unref (cpad->audio_codec_data); - if (cpad && cpad->video_codec_data) - gst_buffer_unref (cpad->video_codec_data); - - gst_collect_pads_remove_pad (mux->collect, pad); - gst_element_remove_pad (element, pad); -} - -static GstFlowReturn -gst_flv_mux_write_metadata (GstFlvMux * mux) -{ - GstTagList *merged_tags; - const GstTagList *user_tags; - GstFlowReturn ret = GST_FLOW_OK; - GstBuffer *script_tag, *tmp; - guint8 *data; - gint i, n_tags, tags_written = 0; - - user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux)); - GST_DEBUG_OBJECT (mux, "upstream tags = %" GST_PTR_FORMAT, mux->tags); - GST_DEBUG_OBJECT (mux, "user-set tags = %" GST_PTR_FORMAT, user_tags); - - merged_tags = gst_tag_list_merge (user_tags, mux->tags, - gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux))); - - GST_DEBUG_OBJECT (mux, "merged tags = %" GST_PTR_FORMAT, merged_tags); - - script_tag = gst_buffer_new_and_alloc (11); - data = GST_BUFFER_DATA (script_tag); - - data[0] = 18; - - /* Data size, unknown for now */ - data[1] = 0; - data[2] = 0; - data[3] = 0; - - /* Timestamp */ - data[4] = data[5] = data[6] = data[7] = 0; - - /* Stream ID */ - data[8] = data[9] = data[10] = 0; - - tmp = gst_buffer_new_and_alloc (13); - data = GST_BUFFER_DATA (tmp); - data[0] = 2; /* string */ - data[1] = 0; - data[2] = 0x0a; /* length 10 */ - memcpy (&data[3], "onMetaData", sizeof ("onMetaData")); - - script_tag = gst_buffer_join (script_tag, tmp); - - n_tags = - (merged_tags) ? gst_structure_n_fields ((GstStructure *) merged_tags) : 0; - tmp = gst_buffer_new_and_alloc (5); - data = GST_BUFFER_DATA (tmp); - data[0] = 8; /* ECMA array */ - GST_WRITE_UINT32_BE (data + 1, n_tags); - script_tag = gst_buffer_join (script_tag, tmp); - - for (i = 0; merged_tags && i < n_tags; i++) { - const gchar *tag_name = - gst_structure_nth_field_name ((const GstStructure *) merged_tags, i); - if (!strcmp (tag_name, GST_TAG_DURATION)) { - gdouble d; - guint64 dur; - - if (!gst_tag_list_get_uint64 (merged_tags, GST_TAG_DURATION, &dur)) - continue; - - d = gst_guint64_to_gdouble (dur); - d /= (gdouble) GST_SECOND; - - tmp = gst_buffer_new_and_alloc (2 + 8 + 1 + 8); - data = GST_BUFFER_DATA (tmp); - data[0] = 0; /* 8 bytes name */ - data[1] = 8; - memcpy (&data[2], "duration", sizeof ("duration")); - data[10] = 0; /* double */ - GST_WRITE_DOUBLE_BE (data + 11, d); - script_tag = gst_buffer_join (script_tag, tmp); - tags_written++; - } else if (!strcmp (tag_name, GST_TAG_ARTIST) || - !strcmp (tag_name, GST_TAG_TITLE)) { - gchar *s; - const gchar *t; - - if (!strcmp (tag_name, GST_TAG_ARTIST)) - t = "creator"; - else if (!strcmp (tag_name, GST_TAG_TITLE)) - t = "title"; - - if (!gst_tag_list_get_string (merged_tags, tag_name, &s)) - continue; - - tmp = gst_buffer_new_and_alloc (2 + strlen (t) + 1 + 2 + strlen (s)); - data = GST_BUFFER_DATA (tmp); - data[0] = 0; /* tag name length */ - data[1] = strlen (t); - memcpy (&data[2], t, strlen (t)); - data[2 + strlen (t)] = 2; /* string */ - data[3 + strlen (t)] = (strlen (s) >> 8) & 0xff; - data[4 + strlen (t)] = (strlen (s)) & 0xff; - memcpy (&data[5 + strlen (t)], s, strlen (s)); - script_tag = gst_buffer_join (script_tag, tmp); - - g_free (s); - tags_written++; - } - } - - if (mux->have_video) { - GstPad *video_pad = NULL; - GSList *l = mux->collect->data; - - for (; l; l = l->next) { - GstFlvPad *cpad = l->data; - if (cpad && cpad->video) { - video_pad = cpad->collect.pad; - break; - } - } - - if (video_pad && GST_PAD_CAPS (video_pad)) { - GstStructure *s = gst_caps_get_structure (GST_PAD_CAPS (video_pad), 0); - gint par_x, par_y; - - if (gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_x, &par_y)) { - gdouble d; - - d = par_x; - tmp = gst_buffer_new_and_alloc (2 + 12 + 1 + 8); - data = GST_BUFFER_DATA (tmp); - data[0] = 0; /* 12 bytes name */ - data[1] = 12; - memcpy (&data[2], "AspectRatioX", sizeof ("AspectRatioX")); - data[14] = 0; /* double */ - GST_WRITE_DOUBLE_BE (data + 15, d); - script_tag = gst_buffer_join (script_tag, tmp); - tags_written++; - - d = par_y; - tmp = gst_buffer_new_and_alloc (2 + 12 + 1 + 8); - data = GST_BUFFER_DATA (tmp); - data[0] = 0; /* 12 bytes name */ - data[1] = 12; - memcpy (&data[2], "AspectRatioY", sizeof ("AspectRatioY")); - data[14] = 0; /* double */ - GST_WRITE_DOUBLE_BE (data + 15, d); - script_tag = gst_buffer_join (script_tag, tmp); - tags_written++; - } - } - } - - { - const gchar *s = "GStreamer FLV muxer"; - - tmp = gst_buffer_new_and_alloc (2 + 15 + 1 + 2 + strlen (s)); - data = GST_BUFFER_DATA (tmp); - data[0] = 0; /* 15 bytes name */ - data[1] = 15; - memcpy (&data[2], "metadatacreator", sizeof ("metadatacreator")); - data[17] = 2; /* string */ - data[18] = (strlen (s) >> 8) & 0xff; - data[19] = (strlen (s)) & 0xff; - memcpy (&data[20], s, strlen (s)); - script_tag = gst_buffer_join (script_tag, tmp); - - tags_written++; - } - - { - GTimeVal tv = { 0, }; - time_t secs; - struct tm *tm; - gchar *s; - static const gchar *weekdays[] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - }; - static const gchar *months[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", - "Aug", "Sep", "Oct", "Nov", "Dec" - }; - - g_get_current_time (&tv); - secs = tv.tv_sec; - tm = gmtime (&secs); - - s = g_strdup_printf ("%s %s %d %d:%d:%d %d", weekdays[tm->tm_wday], - months[tm->tm_mon], tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, - tm->tm_year + 1900); - - tmp = gst_buffer_new_and_alloc (2 + 12 + 1 + 2 + strlen (s)); - data = GST_BUFFER_DATA (tmp); - data[0] = 0; /* 12 bytes name */ - data[1] = 12; - memcpy (&data[2], "creationdate", sizeof ("creationdate")); - data[14] = 2; /* string */ - data[15] = (strlen (s) >> 8) & 0xff; - data[16] = (strlen (s)) & 0xff; - memcpy (&data[17], s, strlen (s)); - script_tag = gst_buffer_join (script_tag, tmp); - - g_free (s); - tags_written++; - } - - tmp = gst_buffer_new_and_alloc (2 + 0 + 1); - data = GST_BUFFER_DATA (tmp); - data[0] = 0; /* 0 byte size */ - data[1] = 0; - data[2] = 9; /* end marker */ - script_tag = gst_buffer_join (script_tag, tmp); - tags_written++; - - - tmp = gst_buffer_new_and_alloc (4); - data = GST_BUFFER_DATA (tmp); - GST_WRITE_UINT32_BE (data, GST_BUFFER_SIZE (script_tag)); - script_tag = gst_buffer_join (script_tag, tmp); - - data = GST_BUFFER_DATA (script_tag); - data[1] = ((GST_BUFFER_SIZE (script_tag) - 11 - 4) >> 16) & 0xff; - data[2] = ((GST_BUFFER_SIZE (script_tag) - 11 - 4) >> 8) & 0xff; - data[3] = ((GST_BUFFER_SIZE (script_tag) - 11 - 4) >> 0) & 0xff; - - GST_WRITE_UINT32_BE (data + 11 + 13 + 1, tags_written); - - gst_buffer_set_caps (script_tag, GST_PAD_CAPS (mux->srcpad)); - ret = gst_pad_push (mux->srcpad, script_tag); - - if (merged_tags) - gst_tag_list_free (merged_tags); - - return ret; -} - -static GstFlowReturn -gst_flv_mux_write_header (GstFlvMux * mux) -{ - GstBuffer *header = gst_buffer_new_and_alloc (9 + 4); - guint8 *data = GST_BUFFER_DATA (header); - GstFlowReturn ret; - - if (GST_PAD_CAPS (mux->srcpad) == NULL) { - GstCaps *caps = gst_caps_new_simple ("video/x-flv", NULL); - - gst_pad_set_caps (mux->srcpad, caps); - gst_caps_unref (caps); - } - gst_buffer_set_caps (header, GST_PAD_CAPS (mux->srcpad)); - - data[0] = 'F'; - data[1] = 'L'; - data[2] = 'V'; - data[3] = 0x01; /* Version */ - - data[4] = (mux->have_audio << 2) | mux->have_video; /* flags */ - GST_WRITE_UINT32_BE (data + 5, 9); /* data offset */ - - GST_WRITE_UINT32_BE (data + 9, 0); /* previous tag size */ - - ret = gst_pad_push (mux->srcpad, header); - if (ret != GST_FLOW_OK) - return ret; - - return gst_flv_mux_write_metadata (mux); -} - -static GstFlowReturn -gst_flv_mux_write_buffer (GstFlvMux * mux, GstFlvPad * cpad) -{ - GstBuffer *tag; - guint8 *data; - guint size; - GstBuffer *buffer = - gst_collect_pads_pop (mux->collect, (GstCollectData *) cpad); - guint32 timestamp = - (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) ? GST_BUFFER_TIMESTAMP (buffer) / - GST_MSECOND : cpad->last_timestamp / GST_MSECOND; - gboolean second_run = FALSE; - GstFlowReturn ret; - -next: - size = 11; - if (cpad->video) { - size += 1; - if (cpad->video_codec == 7 && !cpad->sent_codec_data) - size += 4 + GST_BUFFER_SIZE (cpad->video_codec_data); - else if (cpad->video_codec == 7) - size += 4 + GST_BUFFER_SIZE (buffer); - else - size += GST_BUFFER_SIZE (buffer); - } else { - size += 1; - if (cpad->audio_codec == 10 && !cpad->sent_codec_data) - size += 1 + GST_BUFFER_SIZE (cpad->audio_codec_data); - else if (cpad->audio_codec == 10) - size += 1 + GST_BUFFER_SIZE (buffer); - else - size += GST_BUFFER_SIZE (buffer); - } - size += 4; - - tag = gst_buffer_new_and_alloc (size); - data = GST_BUFFER_DATA (tag); - memset (data, 0, size); - - data[0] = (cpad->video) ? 9 : 8; - - data[1] = ((size - 11 - 4) >> 16) & 0xff; - data[2] = ((size - 11 - 4) >> 8) & 0xff; - data[3] = ((size - 11 - 4) >> 0) & 0xff; - - data[4] = (timestamp >> 16) & 0xff; - data[5] = (timestamp >> 8) & 0xff; - data[6] = (timestamp >> 0) & 0xff; - data[7] = (timestamp >> 24) & 0xff; - - data[8] = data[9] = data[10] = 0; - - if (cpad->video) { - if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) - data[11] |= 2 << 4; - else - data[11] |= 1 << 4; - - data[11] |= cpad->video_codec & 0x0f; - - if (cpad->video_codec == 7 && !cpad->sent_codec_data) { - data[12] = 0; - data[13] = data[14] = data[15] = 0; - - memcpy (data + 11 + 1 + 4, GST_BUFFER_DATA (cpad->video_codec_data), - GST_BUFFER_SIZE (cpad->video_codec_data)); - second_run = TRUE; - } else if (cpad->video_codec == 7) { - data[12] = 1; - - /* FIXME: what to do about composition time */ - data[13] = data[14] = data[15] = 0; - - memcpy (data + 11 + 1 + 4, GST_BUFFER_DATA (buffer), - GST_BUFFER_SIZE (buffer)); - } else { - memcpy (data + 11 + 1, GST_BUFFER_DATA (buffer), - GST_BUFFER_SIZE (buffer)); - } - } else { - data[11] |= (cpad->audio_codec << 4) & 0xf0; - data[11] |= (cpad->rate << 2) & 0x0c; - data[11] |= (cpad->width << 1) & 0x02; - data[11] |= (cpad->channels << 0) & 0x01; - - if (cpad->audio_codec == 10 && !cpad->sent_codec_data) { - data[12] = 0; - - memcpy (data + 11 + 1 + 1, GST_BUFFER_DATA (cpad->audio_codec_data), - GST_BUFFER_SIZE (cpad->audio_codec_data)); - second_run = TRUE; - } else if (cpad->audio_codec == 10) { - data[12] = 1; - - memcpy (data + 11 + 1 + 1, GST_BUFFER_DATA (buffer), - GST_BUFFER_SIZE (buffer)); - } else { - memcpy (data + 11 + 1, GST_BUFFER_DATA (buffer), - GST_BUFFER_SIZE (buffer)); - } - } - - GST_WRITE_UINT32_BE (data + size - 4, size - 4); - - gst_buffer_set_caps (tag, GST_PAD_CAPS (mux->srcpad)); - - if (second_run) { - second_run = FALSE; - cpad->sent_codec_data = TRUE; - - ret = gst_pad_push (mux->srcpad, tag); - if (ret != GST_FLOW_OK) { - gst_buffer_unref (buffer); - return ret; - } - - cpad->last_timestamp = timestamp; - - tag = NULL; - goto next; - } - - gst_buffer_copy_metadata (tag, buffer, GST_BUFFER_COPY_TIMESTAMPS); - GST_BUFFER_OFFSET (tag) = GST_BUFFER_OFFSET_END (tag) = - GST_BUFFER_OFFSET_NONE; - - gst_buffer_unref (buffer); - - ret = gst_pad_push (mux->srcpad, tag); - - if (ret == GST_FLOW_OK) - cpad->last_timestamp = timestamp; - - return ret; -} - -static GstFlowReturn -gst_flv_mux_collected (GstCollectPads * pads, gpointer user_data) -{ - GstFlvMux *mux = GST_FLV_MUX (user_data); - GstFlvPad *best; - GstClockTime best_time; - GstFlowReturn ret; - GSList *sl; - gboolean eos = TRUE; - - if (mux->state == GST_FLV_MUX_STATE_HEADER) { - if (mux->collect->data == NULL) { - GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL), - ("No input streams configured")); - return GST_FLOW_ERROR; - } - - if (gst_pad_push_event (mux->srcpad, - gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0))) - ret = gst_flv_mux_write_header (mux); - else - ret = GST_FLOW_ERROR; - - if (ret != GST_FLOW_OK) - return ret; - mux->state = GST_FLV_MUX_STATE_DATA; - } - - best = NULL; - best_time = GST_CLOCK_TIME_NONE; - for (sl = mux->collect->data; sl; sl = sl->next) { - GstFlvPad *cpad = sl->data; - GstBuffer *buffer = gst_collect_pads_peek (pads, (GstCollectData *) cpad); - GstClockTime time; - - if (!buffer) - continue; - - eos = FALSE; - - time = GST_BUFFER_TIMESTAMP (buffer); - gst_buffer_unref (buffer); - - /* Use buffers without valid timestamp first */ - if (!GST_CLOCK_TIME_IS_VALID (time)) { - GST_WARNING_OBJECT (pads, "Buffer without valid timestamp"); - - best_time = cpad->last_timestamp; - best = cpad; - break; - } - - - if (best == NULL || (GST_CLOCK_TIME_IS_VALID (best_time) - && time < best_time)) { - best = cpad; - best_time = time; - } - } - - if (GST_CLOCK_TIME_IS_VALID (best_time) - && best_time / GST_MSECOND > G_MAXUINT32) { - GST_WARNING_OBJECT (mux, "Timestamp larger than FLV supports - EOS"); - eos = TRUE; - } - - if (!eos && best) { - return gst_flv_mux_write_buffer (mux, best); - } else if (eos) { - gst_pad_push_event (mux->srcpad, gst_event_new_eos ()); - return GST_FLOW_UNEXPECTED; - } else { - return GST_FLOW_OK; - } -} - -static GstStateChangeReturn -gst_flv_mux_change_state (GstElement * element, GstStateChange transition) -{ - GstStateChangeReturn ret; - GstFlvMux *mux = GST_FLV_MUX (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - gst_collect_pads_start (mux->collect); - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_collect_pads_stop (mux->collect); - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_flv_mux_reset (GST_ELEMENT (mux)); - break; - case GST_STATE_CHANGE_READY_TO_NULL: - break; - default: - break; - } - - return ret; -} diff --git a/gst/flv/gstflvmux.h b/gst/flv/gstflvmux.h deleted file mode 100644 index 02df089d..00000000 --- a/gst/flv/gstflvmux.h +++ /dev/null @@ -1,89 +0,0 @@ -/* GStreamer - * - * Copyright (c) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk> - * - * 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_FLV_MUX_H__ -#define __GST_FLV_MUX_H__ - -#include <gst/gst.h> -#include <gst/base/gstcollectpads.h> - -G_BEGIN_DECLS - -#define GST_TYPE_FLV_MUX \ - (gst_flv_mux_get_type ()) -#define GST_FLV_MUX(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_FLV_MUX, GstFlvMux)) -#define GST_FLV_MUX_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_FLV_MUX, GstFlvMuxClass)) -#define GST_IS_FLV_MUX(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_FLV_MUX)) -#define GST_IS_FLV_MUX_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_FLV_MUX)) - -typedef struct -{ - GstCollectData collect; - - gboolean video; - - guint audio_codec; - guint rate; - guint width; - guint channels; - GstBuffer *audio_codec_data; - - guint video_codec; - GstBuffer *video_codec_data; - - gboolean sent_codec_data; - GstClockTime last_timestamp; -} GstFlvPad; - -typedef enum -{ - GST_FLV_MUX_STATE_HEADER, - GST_FLV_MUX_STATE_DATA -} GstFlvMuxState; - -typedef struct _GstFlvMux { - GstElement element; - - GstPad *srcpad; - GstCollectPads *collect; - - /* <private> */ - GstPadEventFunction collect_event; - - GstFlvMuxState state; - gboolean have_audio; - gboolean have_video; - - GstTagList *tags; -} GstFlvMux; - -typedef struct _GstFlvMuxClass { - GstElementClass parent; -} GstFlvMuxClass; - -GType gst_flv_mux_get_type (void); - -G_END_DECLS - -#endif /* __GST_FLV_MUX_H__ */ diff --git a/gst/flv/gstflvparse.c b/gst/flv/gstflvparse.c deleted file mode 100644 index 59446512..00000000 --- a/gst/flv/gstflvparse.c +++ /dev/null @@ -1,1283 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Julien Moutte <julien@moutte.net> - * - * 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 "gstflvparse.h" - -#include <gst/base/gstbytereader.h> - -#include <string.h> - -GST_DEBUG_CATEGORY_EXTERN (flvdemux_debug); -#define GST_CAT_DEFAULT flvdemux_debug - -static gchar * -FLV_GET_STRING (GstByteReader * reader) -{ - guint16 string_size = 0; - gchar *string = NULL; - const guint8 *str; - - g_return_val_if_fail (reader != NULL, NULL); - - if (G_UNLIKELY (!gst_byte_reader_get_uint16_be (reader, &string_size))) - return NULL; - - if (G_UNLIKELY (string_size > gst_byte_reader_get_remaining (reader))) - return NULL; - - string = g_try_malloc0 (string_size + 1); - if (G_UNLIKELY (!string)) { - return NULL; - } - - if (G_UNLIKELY (!gst_byte_reader_get_data (reader, string_size, &str))) { - g_free (string); - return NULL; - } - - memcpy (string, str, string_size); - if (!g_utf8_validate (string, string_size, NULL)) { - g_free (string); - return NULL; - } - - return string; -} - -static const GstQueryType * -gst_flv_demux_query_types (GstPad * pad) -{ - static const GstQueryType query_types[] = { - GST_QUERY_DURATION, - 0 - }; - - return query_types; -} - -static void -parse_flv_date_string (GDate * date, const gchar * s) -{ - g_date_set_parse (date, s); - if (g_date_valid (date)) - return; - - /* "Fri Oct 15 15:13:16 2004" needs to be parsed */ - { - static const gchar *months[] = { - "Jan", "Feb", "Mar", "Apr", - "May", "Jun", "Jul", "Aug", - "Sep", "Oct", "Nov", "Dec" - }; - gchar **tokens = g_strsplit (s, " ", -1); - guint64 d; - gchar *endptr; - gint i; - - if (g_strv_length (tokens) != 5) - goto out; - - if (strlen (tokens[1]) != 3) - goto out; - for (i = 0; i < 12; i++) { - if (!strcmp (tokens[1], months[i])) { - break; - } - } - if (i == 12) - goto out; - g_date_set_month (date, i + 1); - - d = g_ascii_strtoull (tokens[2], &endptr, 10); - if (d == 0 && *endptr != '\0') - goto out; - - g_date_set_day (date, d); - - d = g_ascii_strtoull (tokens[4], &endptr, 10); - if (d == 0 && *endptr != '\0') - goto out; - - g_date_set_year (date, d); - - out: - if (tokens) - g_strfreev (tokens); - } -} - -static gboolean -gst_flv_parse_metadata_item (GstFLVDemux * demux, GstByteReader * reader, - gboolean * end_marker) -{ - gchar *tag_name = NULL; - guint8 tag_type = 0; - - /* Initialize the end_marker flag to FALSE */ - *end_marker = FALSE; - - /* Name of the tag */ - tag_name = FLV_GET_STRING (reader); - if (G_UNLIKELY (!tag_name)) { - GST_WARNING_OBJECT (demux, "failed reading tag name"); - return FALSE; - } - - /* What kind of object is that */ - if (!gst_byte_reader_get_uint8 (reader, &tag_type)) - goto error; - - GST_DEBUG_OBJECT (demux, "tag name %s, tag type %d", tag_name, tag_type); - - switch (tag_type) { - case 0: // Double - { /* Use a union to read the uint64 and then as a double */ - gdouble d; - - if (!gst_byte_reader_get_float64_be (reader, &d)) - goto error; - - GST_DEBUG_OBJECT (demux, "%s => (double) %f", tag_name, d); - - if (!strcmp (tag_name, "duration")) { - demux->duration = d * GST_SECOND; - - gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE, - GST_TAG_DURATION, demux->duration, NULL); - } else if (!strcmp (tag_name, "AspectRatioX")) { - demux->par_x = d; - demux->got_par = TRUE; - } else if (!strcmp (tag_name, "AspectRatioY")) { - demux->par_y = d; - demux->got_par = TRUE; - } else { - GST_INFO_OBJECT (demux, "Tag \'%s\' not handled", tag_name); - } - - break; - } - case 1: // Boolean - { - guint8 b; - - if (!gst_byte_reader_get_uint8 (reader, &b)) - goto error; - - GST_DEBUG_OBJECT (demux, "%s => (boolean) %d", tag_name, b); - - GST_INFO_OBJECT (demux, "Tag \'%s\' not handled", tag_name); - - break; - } - case 2: // String - { - gchar *s = NULL; - - s = FLV_GET_STRING (reader); - if (s == NULL) - goto error; - - GST_DEBUG_OBJECT (demux, "%s => (string) %s", tag_name, s); - - if (!strcmp (tag_name, "creationdate")) { - GDate *date = g_date_new (); - - parse_flv_date_string (date, s); - if (!g_date_valid (date)) { - GST_DEBUG_OBJECT (demux, "Failed to parse string as date"); - } else { - gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE, - GST_TAG_DATE, date, NULL); - } - g_date_free (date); - } else if (!strcmp (tag_name, "creator")) { - gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE, - GST_TAG_ARTIST, s, NULL); - } else if (!strcmp (tag_name, "title")) { - gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE, - GST_TAG_TITLE, s, NULL); - } else if (!strcmp (tag_name, "metadatacreator")) { - gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE, - GST_TAG_ENCODER, s, NULL); - } else { - GST_INFO_OBJECT (demux, "Tag \'%s\' not handled", tag_name); - } - - g_free (s); - - break; - } - case 3: // Object - { - gboolean end_of_object_marker = FALSE; - - while (!end_of_object_marker) { - gboolean ok = - gst_flv_parse_metadata_item (demux, reader, &end_of_object_marker); - - if (G_UNLIKELY (!ok)) { - GST_WARNING_OBJECT (demux, "failed reading a tag, skipping"); - goto error; - } - } - - break; - } - case 8: // ECMA array - { - guint32 nb_elems; - gboolean end_of_object_marker = FALSE; - - if (!gst_byte_reader_get_uint32_be (reader, &nb_elems)) - goto error; - - GST_DEBUG_OBJECT (demux, "there are approx. %d elements in the array", - nb_elems); - - while (!end_of_object_marker) { - gboolean ok = - gst_flv_parse_metadata_item (demux, reader, &end_of_object_marker); - - if (G_UNLIKELY (!ok)) { - GST_WARNING_OBJECT (demux, "failed reading a tag, skipping"); - goto error; - } - } - - break; - } - case 9: // End marker - { - GST_DEBUG_OBJECT (demux, "end marker ?"); - if (tag_name[0] == '\0') { - - GST_DEBUG_OBJECT (demux, "end marker detected"); - - *end_marker = TRUE; - } - - break; - } - case 10: // Array - { - guint32 nb_elems; - - if (!gst_byte_reader_get_uint32_be (reader, &nb_elems)) - goto error; - - GST_DEBUG_OBJECT (demux, "array has %d elements", nb_elems); - - if (!strcmp (tag_name, "times")) { - if (demux->times) { - g_array_free (demux->times, TRUE); - } - demux->times = g_array_new (FALSE, TRUE, sizeof (gdouble)); - } else if (!strcmp (tag_name, "filepositions")) { - if (demux->filepositions) { - g_array_free (demux->filepositions, TRUE); - } - demux->filepositions = g_array_new (FALSE, TRUE, sizeof (gdouble)); - } - - while (nb_elems--) { - guint8 elem_type; - - if (!gst_byte_reader_get_uint8 (reader, &elem_type)) - goto error; - - switch (elem_type) { - case 0: - { - gdouble d; - - if (!gst_byte_reader_get_float64_be (reader, &d)) - goto error; - - GST_DEBUG_OBJECT (demux, "element is a double %f", d); - - if (!strcmp (tag_name, "times") && demux->times) { - g_array_append_val (demux->times, d); - } else if (!strcmp (tag_name, "filepositions") && - demux->filepositions) { - g_array_append_val (demux->filepositions, d); - } - break; - } - default: - GST_WARNING_OBJECT (demux, "unsupported array element type %d", - elem_type); - } - } - - break; - } - case 11: // Date - { - gdouble d; - gint16 i; - - if (!gst_byte_reader_get_float64_be (reader, &d)) - goto error; - - if (!gst_byte_reader_get_int16_be (reader, &i)) - goto error; - - GST_DEBUG_OBJECT (demux, - "%s => (date as a double) %f, timezone offset %d", tag_name, d, i); - - GST_INFO_OBJECT (demux, "Tag \'%s\' not handled", tag_name); - - break; - } - default: - GST_WARNING_OBJECT (demux, "unsupported tag type %d", tag_type); - } - - g_free (tag_name); - - return TRUE; - -error: - g_free (tag_name); - - return FALSE; -} - -GstFlowReturn -gst_flv_parse_tag_script (GstFLVDemux * demux, GstBuffer * buffer) -{ - GstFlowReturn ret = GST_FLOW_OK; - GstByteReader reader = GST_BYTE_READER_INIT_FROM_BUFFER (buffer); - guint8 type; - - g_return_val_if_fail (GST_BUFFER_SIZE (buffer) >= 7, GST_FLOW_ERROR); - - gst_byte_reader_skip (&reader, 7); - - GST_LOG_OBJECT (demux, "parsing a script tag"); - - if (!gst_byte_reader_get_uint8 (&reader, &type)) - return GST_FLOW_OK; - - /* Must be string */ - if (type == 2) { - gchar *function_name; - guint i; - - function_name = FLV_GET_STRING (&reader); - - GST_LOG_OBJECT (demux, "function name is %s", GST_STR_NULL (function_name)); - - if (function_name != NULL && strcmp (function_name, "onMetaData") == 0) { - guint32 nb_elems = 0; - gboolean end_marker = FALSE; - - GST_DEBUG_OBJECT (demux, "we have a metadata script object"); - - /* Next type must be a ECMA array */ - if (!gst_byte_reader_get_uint8 (&reader, &type) || type != 8) { - g_free (function_name); - return GST_FLOW_OK; - } - - if (!gst_byte_reader_get_uint32_be (&reader, &nb_elems)) { - g_free (function_name); - return GST_FLOW_OK; - } - - GST_DEBUG_OBJECT (demux, "there are approx. %d elements in the array", - nb_elems); - - while (nb_elems-- && !end_marker) { - gboolean ok = gst_flv_parse_metadata_item (demux, &reader, &end_marker); - - if (G_UNLIKELY (!ok)) { - GST_WARNING_OBJECT (demux, "failed reading a tag, skipping"); - break; - } - } - - demux->push_tags = TRUE; - } - - g_free (function_name); - - if (demux->index && demux->times && demux->filepositions - && !demux->random_access) { - /* If an index was found and we're in push mode, insert associations */ - for (i = 0; i < MIN (demux->times->len, demux->filepositions->len); i++) { - guint64 time, fileposition; - - time = g_array_index (demux->times, gdouble, i) * GST_SECOND; - fileposition = g_array_index (demux->filepositions, gdouble, i); - GST_LOG_OBJECT (demux, "adding association %" GST_TIME_FORMAT "-> %" - G_GUINT64_FORMAT, GST_TIME_ARGS (time), fileposition); - gst_index_add_association (demux->index, demux->index_id, - GST_ASSOCIATION_FLAG_KEY_UNIT, GST_FORMAT_TIME, time, - GST_FORMAT_BYTES, fileposition, NULL); - } - } - } - - return ret; -} - -static gboolean -gst_flv_parse_audio_negotiate (GstFLVDemux * demux, guint32 codec_tag, - guint32 rate, guint32 channels, guint32 width) -{ - GstCaps *caps = NULL; - gchar *codec_name = NULL; - gboolean ret = FALSE; - - switch (codec_tag) { - case 1: - caps = gst_caps_new_simple ("audio/x-adpcm", "layout", G_TYPE_STRING, - "swf", NULL); - codec_name = "Shockwave ADPCM"; - break; - case 2: - case 14: - caps = gst_caps_new_simple ("audio/mpeg", - "mpegversion", G_TYPE_INT, 1, "layer", G_TYPE_INT, 3, - "parsed", G_TYPE_BOOLEAN, TRUE, NULL); - codec_name = "MPEG 1 Audio, Layer 3 (MP3)"; - break; - case 0: - case 3: - /* Assuming little endian for 0 (aka endianness of the - * system on which the file was created) as most people - * are probably using little endian machines */ - caps = gst_caps_new_simple ("audio/x-raw-int", - "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, - "signed", G_TYPE_BOOLEAN, (width == 8) ? FALSE : TRUE, - "width", G_TYPE_INT, width, "depth", G_TYPE_INT, width, NULL); - codec_name = "Raw Audio"; - break; - case 4: - case 5: - case 6: - caps = gst_caps_new_simple ("audio/x-nellymoser", NULL); - codec_name = "Nellymoser ASAO"; - break; - case 10: - caps = gst_caps_new_simple ("audio/mpeg", - "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE, NULL); - codec_name = "AAC"; - break; - case 7: - caps = gst_caps_new_simple ("audio/x-alaw", NULL); - codec_name = "A-Law"; - break; - case 8: - caps = gst_caps_new_simple ("audio/x-mulaw", NULL); - codec_name = "Mu-Law"; - break; - case 11: - caps = gst_caps_new_simple ("audio/x-speex", NULL); - codec_name = "Speex"; - break; - default: - GST_WARNING_OBJECT (demux, "unsupported audio codec tag %u", codec_tag); - } - - if (G_UNLIKELY (!caps)) { - GST_WARNING_OBJECT (demux, "failed creating caps for audio pad"); - goto beach; - } - - gst_caps_set_simple (caps, - "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, channels, NULL); - - if (demux->audio_codec_data) { - gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, - demux->audio_codec_data, NULL); - } - - ret = gst_pad_set_caps (demux->audio_pad, caps); - - if (G_LIKELY (ret)) { - /* Store the caps we have set */ - demux->audio_codec_tag = codec_tag; - demux->rate = rate; - demux->channels = channels; - demux->width = width; - - if (codec_name) { - if (demux->taglist == NULL) - demux->taglist = gst_tag_list_new (); - gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE, - GST_TAG_AUDIO_CODEC, codec_name, NULL); - } - - GST_DEBUG_OBJECT (demux->audio_pad, "successfully negotiated caps %" - GST_PTR_FORMAT, caps); - } else { - GST_WARNING_OBJECT (demux->audio_pad, "failed negotiating caps %" - GST_PTR_FORMAT, caps); - } - - gst_caps_unref (caps); - -beach: - return ret; -} - -GstFlowReturn -gst_flv_parse_tag_audio (GstFLVDemux * demux, GstBuffer * buffer) -{ - GstFlowReturn ret = GST_FLOW_OK; - guint32 pts = 0, codec_tag = 0, rate = 5512, width = 8, channels = 1; - guint32 codec_data = 0, pts_ext = 0; - guint8 flags = 0; - guint8 *data = GST_BUFFER_DATA (buffer); - GstBuffer *outbuf; - - GST_LOG_OBJECT (demux, "parsing an audio tag"); - - g_return_val_if_fail (GST_BUFFER_SIZE (buffer) == demux->tag_size, - GST_FLOW_ERROR); - - GST_LOG_OBJECT (demux, "pts bytes %02X %02X %02X %02X", data[0], data[1], - data[2], data[3]); - - /* Grab information about audio tag */ - pts = GST_READ_UINT24_BE (data); - /* read the pts extension to 32 bits integer */ - pts_ext = GST_READ_UINT8 (data + 3); - /* Combine them */ - pts |= pts_ext << 24; - - if (GST_BUFFER_SIZE (buffer) < 12) { - GST_ERROR_OBJECT (demux, "Too small tag size"); - return GST_FLOW_ERROR; - } - - /* Skip the stream id and go directly to the flags */ - flags = GST_READ_UINT8 (data + 7); - - /* Channels */ - if (flags & 0x01) { - channels = 2; - } - /* Width */ - if (flags & 0x02) { - width = 16; - } - /* Sampling rate */ - if ((flags & 0x0C) == 0x0C) { - rate = 44100; - } else if ((flags & 0x0C) == 0x08) { - rate = 22050; - } else if ((flags & 0x0C) == 0x04) { - rate = 11025; - } - /* Codec tag */ - codec_tag = flags >> 4; - if (codec_tag == 10) { /* AAC has an extra byte for packet type */ - codec_data = 2; - } else { - codec_data = 1; - } - - /* codec tags with special rates */ - if (codec_tag == 5 || codec_tag == 14) - rate = 8000; - else if (codec_tag == 4) - rate = 16000; - - GST_LOG_OBJECT (demux, "audio tag with %d channels, %dHz sampling rate, " - "%d bits width, codec tag %u (flags %02X)", channels, rate, width, - codec_tag, flags); - - /* If we don't have our audio pad created, then create it. */ - if (G_UNLIKELY (!demux->audio_pad)) { - - demux->audio_pad = - gst_pad_new_from_template (gst_element_class_get_pad_template - (GST_ELEMENT_GET_CLASS (demux), "audio"), "audio"); - if (G_UNLIKELY (!demux->audio_pad)) { - GST_WARNING_OBJECT (demux, "failed creating audio pad"); - ret = GST_FLOW_ERROR; - goto beach; - } - - /* Negotiate caps */ - if (!gst_flv_parse_audio_negotiate (demux, codec_tag, rate, channels, - width)) { - gst_object_unref (demux->audio_pad); - demux->audio_pad = NULL; - ret = GST_FLOW_ERROR; - goto beach; - } - - GST_DEBUG_OBJECT (demux, "created audio pad with caps %" GST_PTR_FORMAT, - GST_PAD_CAPS (demux->audio_pad)); - - /* Set functions on the pad */ - gst_pad_set_query_type_function (demux->audio_pad, - GST_DEBUG_FUNCPTR (gst_flv_demux_query_types)); - gst_pad_set_query_function (demux->audio_pad, - GST_DEBUG_FUNCPTR (gst_flv_demux_query)); - gst_pad_set_event_function (demux->audio_pad, - GST_DEBUG_FUNCPTR (gst_flv_demux_src_event)); - - gst_pad_use_fixed_caps (demux->audio_pad); - - /* Make it active */ - gst_pad_set_active (demux->audio_pad, TRUE); - - /* We need to set caps before adding */ - gst_element_add_pad (GST_ELEMENT (demux), - gst_object_ref (demux->audio_pad)); - - /* We only emit no more pads when we have audio and video. Indeed we can - * not trust the FLV header to tell us if there will be only audio or - * only video and we would just break discovery of some files */ - if (demux->audio_pad && demux->video_pad) { - GST_DEBUG_OBJECT (demux, "emitting no more pads"); - gst_element_no_more_pads (GST_ELEMENT (demux)); - } - } - - /* Check if caps have changed */ - if (G_UNLIKELY (rate != demux->rate || channels != demux->channels || - codec_tag != demux->audio_codec_tag || width != demux->width)) { - GST_DEBUG_OBJECT (demux, "audio settings have changed, changing caps"); - - /* Negotiate caps */ - if (!gst_flv_parse_audio_negotiate (demux, codec_tag, rate, channels, - width)) { - ret = GST_FLOW_ERROR; - goto beach; - } - } - - /* Push taglist if present */ - if ((demux->has_audio && !demux->audio_pad) && - (demux->has_video && !demux->video_pad)) { - GST_DEBUG_OBJECT (demux, "we are still waiting for a stream to come up " - "before we can push tags"); - } else { - if (demux->taglist && demux->push_tags) { - GST_DEBUG_OBJECT (demux, "pushing tags out"); - gst_element_found_tags (GST_ELEMENT (demux), demux->taglist); - demux->taglist = gst_tag_list_new (); - demux->push_tags = FALSE; - } - } - - /* Check if we have anything to push */ - if (demux->tag_data_size <= codec_data) { - GST_LOG_OBJECT (demux, "Nothing left in this tag, returning"); - goto beach; - } - - /* Create buffer from pad */ - outbuf = - gst_buffer_create_sub (buffer, 7 + codec_data, - demux->tag_data_size - codec_data); - - if (demux->audio_codec_tag == 10) { - guint8 aac_packet_type = GST_READ_UINT8 (data + 8); - - switch (aac_packet_type) { - case 0: - { - /* AudioSpecificConfic data */ - GST_LOG_OBJECT (demux, "got an AAC codec data packet"); - if (demux->audio_codec_data) { - gst_buffer_unref (demux->audio_codec_data); - } - demux->audio_codec_data = outbuf; - /* Use that buffer data in the caps */ - gst_flv_parse_audio_negotiate (demux, codec_tag, rate, channels, width); - goto beach; - break; - } - case 1: - /* AAC raw packet */ - GST_LOG_OBJECT (demux, "got a raw AAC audio packet"); - break; - default: - GST_WARNING_OBJECT (demux, "invalid AAC packet type %u", - aac_packet_type); - } - } - - /* Fill buffer with data */ - GST_BUFFER_TIMESTAMP (outbuf) = pts * GST_MSECOND; - GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE; - GST_BUFFER_OFFSET (outbuf) = demux->audio_offset++; - GST_BUFFER_OFFSET_END (outbuf) = demux->audio_offset; - gst_buffer_set_caps (outbuf, GST_PAD_CAPS (demux->audio_pad)); - - if (demux->duration == GST_CLOCK_TIME_NONE || - demux->duration < GST_BUFFER_TIMESTAMP (outbuf)) - demux->duration = GST_BUFFER_TIMESTAMP (outbuf); - - /* Only add audio frames to the index if we have no video - * and if we don't have random access */ - if (!demux->has_video && demux->index && !demux->random_access) { - GST_LOG_OBJECT (demux, "adding association %" GST_TIME_FORMAT "-> %" - G_GUINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), - demux->cur_tag_offset); - gst_index_add_association (demux->index, demux->index_id, - GST_ASSOCIATION_FLAG_KEY_UNIT, - GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (outbuf), - GST_FORMAT_BYTES, demux->cur_tag_offset, NULL); - } - - if (G_UNLIKELY (demux->audio_need_discont)) { - GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); - demux->audio_need_discont = FALSE; - } - - gst_segment_set_last_stop (&demux->segment, GST_FORMAT_TIME, - GST_BUFFER_TIMESTAMP (outbuf)); - - /* Do we need a newsegment event ? */ - if (G_UNLIKELY (demux->audio_need_segment)) { - if (demux->close_seg_event) - gst_pad_push_event (demux->audio_pad, - gst_event_ref (demux->close_seg_event)); - - if (!demux->new_seg_event) { - GST_DEBUG_OBJECT (demux, "pushing newsegment from %" - GST_TIME_FORMAT " to %" GST_TIME_FORMAT, - GST_TIME_ARGS (demux->segment.last_stop), - GST_TIME_ARGS (demux->segment.stop)); - demux->new_seg_event = - gst_event_new_new_segment (FALSE, demux->segment.rate, - demux->segment.format, demux->segment.last_stop, - demux->segment.stop, demux->segment.last_stop); - } else { - GST_DEBUG_OBJECT (demux, "pushing pre-generated newsegment event"); - } - - gst_pad_push_event (demux->audio_pad, gst_event_ref (demux->new_seg_event)); - - demux->audio_need_segment = FALSE; - } - - GST_LOG_OBJECT (demux, "pushing %d bytes buffer at pts %" GST_TIME_FORMAT - " with duration %" GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT, - GST_BUFFER_SIZE (outbuf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), - GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf)); - - /* Push downstream */ - ret = gst_pad_push (demux->audio_pad, outbuf); - if (G_UNLIKELY (ret != GST_FLOW_OK)) { - GST_WARNING_OBJECT (demux, "failed pushing a %" G_GUINT64_FORMAT - " bytes audio buffer: %s", demux->tag_data_size, - gst_flow_get_name (ret)); - if (ret == GST_FLOW_NOT_LINKED) { - demux->audio_linked = FALSE; - } - goto beach; - } - - demux->audio_linked = TRUE; - -beach: - return ret; -} - -static gboolean -gst_flv_parse_video_negotiate (GstFLVDemux * demux, guint32 codec_tag) -{ - gboolean ret = FALSE; - GstCaps *caps = NULL; - gchar *codec_name = NULL; - - /* Generate caps for that pad */ - switch (codec_tag) { - case 2: - caps = gst_caps_new_simple ("video/x-flash-video", NULL); - codec_name = "Sorenson Video"; - break; - case 3: - caps = gst_caps_new_simple ("video/x-flash-screen", NULL); - codec_name = "Flash Screen Video"; - case 4: - caps = gst_caps_new_simple ("video/x-vp6-flash", NULL); - codec_name = "On2 VP6 Video"; - break; - case 5: - caps = gst_caps_new_simple ("video/x-vp6-alpha", NULL); - codec_name = "On2 VP6 Video with alpha channel"; - break; - case 7: - caps = gst_caps_new_simple ("video/x-h264", NULL); - codec_name = "H.264/AVC Video"; - break; - default: - GST_WARNING_OBJECT (demux, "unsupported video codec tag %u", codec_tag); - } - - if (G_UNLIKELY (!caps)) { - GST_WARNING_OBJECT (demux, "failed creating caps for video pad"); - goto beach; - } - - gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION, - demux->par_x, demux->par_y, NULL); - - if (demux->video_codec_data) { - gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, - demux->video_codec_data, NULL); - } - - ret = gst_pad_set_caps (demux->video_pad, caps); - - if (G_LIKELY (ret)) { - /* Store the caps we have set */ - demux->video_codec_tag = codec_tag; - - if (codec_name) { - if (demux->taglist == NULL) - demux->taglist = gst_tag_list_new (); - gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE, - GST_TAG_VIDEO_CODEC, codec_name, NULL); - } - - GST_DEBUG_OBJECT (demux->video_pad, "successfully negotiated caps %" - GST_PTR_FORMAT, caps); - } else { - GST_WARNING_OBJECT (demux->video_pad, "failed negotiating caps %" - GST_PTR_FORMAT, caps); - } - - gst_caps_unref (caps); - -beach: - return ret; -} - -GstFlowReturn -gst_flv_parse_tag_video (GstFLVDemux * demux, GstBuffer * buffer) -{ - GstFlowReturn ret = GST_FLOW_OK; - guint32 pts = 0, codec_data = 1, pts_ext = 0; - gboolean keyframe = FALSE; - guint8 flags = 0, codec_tag = 0; - guint8 *data = GST_BUFFER_DATA (buffer); - GstBuffer *outbuf; - - g_return_val_if_fail (GST_BUFFER_SIZE (buffer) == demux->tag_size, - GST_FLOW_ERROR); - - GST_LOG_OBJECT (demux, "parsing a video tag"); - - GST_LOG_OBJECT (demux, "pts bytes %02X %02X %02X %02X", data[0], data[1], - data[2], data[3]); - - /* Grab information about video tag */ - pts = GST_READ_UINT24_BE (data); - /* read the pts extension to 32 bits integer */ - pts_ext = GST_READ_UINT8 (data + 3); - /* Combine them */ - pts |= pts_ext << 24; - - if (GST_BUFFER_SIZE (buffer) < 12) { - GST_ERROR_OBJECT (demux, "Too small tag size"); - return GST_FLOW_ERROR; - } - - /* Skip the stream id and go directly to the flags */ - flags = GST_READ_UINT8 (data + 7); - - /* Keyframe */ - if ((flags >> 4) == 1) { - keyframe = TRUE; - } - /* Codec tag */ - codec_tag = flags & 0x0F; - if (codec_tag == 4 || codec_tag == 5) { - codec_data = 2; - } else if (codec_tag == 7) { - codec_data = 5; - } - - GST_LOG_OBJECT (demux, "video tag with codec tag %u, keyframe (%d) " - "(flags %02X)", codec_tag, keyframe, flags); - - /* If we don't have our video pad created, then create it. */ - if (G_UNLIKELY (!demux->video_pad)) { - demux->video_pad = - gst_pad_new_from_template (gst_element_class_get_pad_template - (GST_ELEMENT_GET_CLASS (demux), "video"), "video"); - if (G_UNLIKELY (!demux->video_pad)) { - GST_WARNING_OBJECT (demux, "failed creating video pad"); - ret = GST_FLOW_ERROR; - goto beach; - } - - if (!gst_flv_parse_video_negotiate (demux, codec_tag)) { - gst_object_unref (demux->video_pad); - demux->video_pad = NULL; - ret = GST_FLOW_ERROR; - goto beach; - } - - /* When we ve set pixel-aspect-ratio we use that boolean to detect a - * metadata tag that would come later and trigger a caps change */ - demux->got_par = FALSE; - - GST_DEBUG_OBJECT (demux, "created video pad with caps %" GST_PTR_FORMAT, - GST_PAD_CAPS (demux->video_pad)); - - /* Set functions on the pad */ - gst_pad_set_query_type_function (demux->video_pad, - GST_DEBUG_FUNCPTR (gst_flv_demux_query_types)); - gst_pad_set_query_function (demux->video_pad, - GST_DEBUG_FUNCPTR (gst_flv_demux_query)); - gst_pad_set_event_function (demux->video_pad, - GST_DEBUG_FUNCPTR (gst_flv_demux_src_event)); - - gst_pad_use_fixed_caps (demux->video_pad); - - /* Make it active */ - gst_pad_set_active (demux->video_pad, TRUE); - - /* We need to set caps before adding */ - gst_element_add_pad (GST_ELEMENT (demux), - gst_object_ref (demux->video_pad)); - - /* We only emit no more pads when we have audio and video. Indeed we can - * not trust the FLV header to tell us if there will be only audio or - * only video and we would just break discovery of some files */ - if (demux->audio_pad && demux->video_pad) { - GST_DEBUG_OBJECT (demux, "emitting no more pads"); - gst_element_no_more_pads (GST_ELEMENT (demux)); - } - } - - /* Check if caps have changed */ - if (G_UNLIKELY (codec_tag != demux->video_codec_tag || demux->got_par)) { - - GST_DEBUG_OBJECT (demux, "video settings have changed, changing caps"); - - if (!gst_flv_parse_video_negotiate (demux, codec_tag)) { - ret = GST_FLOW_ERROR; - goto beach; - } - - /* When we ve set pixel-aspect-ratio we use that boolean to detect a - * metadata tag that would come later and trigger a caps change */ - demux->got_par = FALSE; - } - - /* Push taglist if present */ - if ((demux->has_audio && !demux->audio_pad) && - (demux->has_video && !demux->video_pad)) { - GST_DEBUG_OBJECT (demux, "we are still waiting for a stream to come up " - "before we can push tags"); - } else { - if (demux->taglist && demux->push_tags) { - GST_DEBUG_OBJECT (demux, "pushing tags out"); - gst_element_found_tags (GST_ELEMENT (demux), demux->taglist); - demux->taglist = gst_tag_list_new (); - demux->push_tags = FALSE; - } - } - - /* Check if we have anything to push */ - if (demux->tag_data_size <= codec_data) { - GST_LOG_OBJECT (demux, "Nothing left in this tag, returning"); - goto beach; - } - - /* Create buffer from pad */ - outbuf = - gst_buffer_create_sub (buffer, 7 + codec_data, - demux->tag_data_size - codec_data); - - if (demux->video_codec_tag == 7) { - guint8 avc_packet_type = GST_READ_UINT8 (data + 8); - - switch (avc_packet_type) { - case 0: - { - /* AVCDecoderConfigurationRecord data */ - GST_LOG_OBJECT (demux, "got an H.264 codec data packet"); - if (demux->video_codec_data) { - gst_buffer_unref (demux->video_codec_data); - } - demux->video_codec_data = outbuf; - /* Use that buffer data in the caps */ - gst_flv_parse_video_negotiate (demux, codec_tag); - goto beach; - break; - } - case 1: - /* H.264 NALU packet */ - GST_LOG_OBJECT (demux, "got a H.264 NALU audio packet"); - break; - default: - GST_WARNING_OBJECT (demux, "invalid AAC packet type %u", - avc_packet_type); - } - } - - /* Fill buffer with data */ - GST_BUFFER_TIMESTAMP (outbuf) = pts * GST_MSECOND; - GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE; - GST_BUFFER_OFFSET (outbuf) = demux->video_offset++; - GST_BUFFER_OFFSET_END (outbuf) = demux->video_offset; - gst_buffer_set_caps (outbuf, GST_PAD_CAPS (demux->video_pad)); - - if (demux->duration == GST_CLOCK_TIME_NONE || - demux->duration < GST_BUFFER_TIMESTAMP (outbuf)) - demux->duration = GST_BUFFER_TIMESTAMP (outbuf); - - if (!keyframe) { - GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); - if (demux->index && !demux->random_access) { - GST_LOG_OBJECT (demux, "adding association %" GST_TIME_FORMAT "-> %" - G_GUINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), - demux->cur_tag_offset); - gst_index_add_association (demux->index, demux->index_id, - GST_ASSOCIATION_FLAG_NONE, - GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (outbuf), - GST_FORMAT_BYTES, demux->cur_tag_offset, NULL); - } - } else { - if (demux->index && !demux->random_access) { - GST_LOG_OBJECT (demux, "adding association %" GST_TIME_FORMAT "-> %" - G_GUINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), - demux->cur_tag_offset); - gst_index_add_association (demux->index, demux->index_id, - GST_ASSOCIATION_FLAG_KEY_UNIT, - GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (outbuf), - GST_FORMAT_BYTES, demux->cur_tag_offset, NULL); - } - } - - if (G_UNLIKELY (demux->video_need_discont)) { - GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); - demux->video_need_discont = FALSE; - } - - gst_segment_set_last_stop (&demux->segment, GST_FORMAT_TIME, - GST_BUFFER_TIMESTAMP (outbuf)); - - /* Do we need a newsegment event ? */ - if (G_UNLIKELY (demux->video_need_segment)) { - if (demux->close_seg_event) - gst_pad_push_event (demux->video_pad, - gst_event_ref (demux->close_seg_event)); - - if (!demux->new_seg_event) { - GST_DEBUG_OBJECT (demux, "pushing newsegment from %" - GST_TIME_FORMAT " to %" GST_TIME_FORMAT, - GST_TIME_ARGS (demux->segment.last_stop), - GST_TIME_ARGS (demux->segment.stop)); - demux->new_seg_event = - gst_event_new_new_segment (FALSE, demux->segment.rate, - demux->segment.format, demux->segment.last_stop, - demux->segment.stop, demux->segment.last_stop); - } else { - GST_DEBUG_OBJECT (demux, "pushing pre-generated newsegment event"); - } - - gst_pad_push_event (demux->video_pad, gst_event_ref (demux->new_seg_event)); - - demux->video_need_segment = FALSE; - } - - GST_LOG_OBJECT (demux, "pushing %d bytes buffer at pts %" GST_TIME_FORMAT - " with duration %" GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT - ", keyframe (%d)", GST_BUFFER_SIZE (outbuf), - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), - GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf), - keyframe); - - /* Push downstream */ - ret = gst_pad_push (demux->video_pad, outbuf); - - if (G_UNLIKELY (ret != GST_FLOW_OK)) { - GST_WARNING_OBJECT (demux, "failed pushing a %" G_GUINT64_FORMAT - " bytes video buffer: %s", demux->tag_data_size, - gst_flow_get_name (ret)); - if (ret == GST_FLOW_NOT_LINKED) { - demux->video_linked = FALSE; - } - goto beach; - } - - demux->video_linked = TRUE; - -beach: - return ret; -} - -GstClockTime -gst_flv_parse_tag_timestamp (GstFLVDemux * demux, GstBuffer * buffer, - size_t * tag_size) -{ - guint32 pts = 0, pts_ext = 0; - guint32 tag_data_size; - guint8 type; - gboolean keyframe = TRUE; - GstClockTime ret; - guint8 *data = GST_BUFFER_DATA (buffer); - - g_return_val_if_fail (GST_BUFFER_SIZE (buffer) >= 12, GST_CLOCK_TIME_NONE); - - type = data[0]; - - if (type != 9 && type != 8 && type != 18) { - GST_WARNING_OBJECT (demux, "Unsupported tag type %u", data[0]); - return GST_CLOCK_TIME_NONE; - } - - if (type == 9) - demux->has_video = TRUE; - else if (type == 8) - demux->has_audio = TRUE; - - tag_data_size = GST_READ_UINT24_BE (data + 1); - - if (GST_BUFFER_SIZE (buffer) >= tag_data_size + 11 + 4) { - if (GST_READ_UINT32_BE (data + tag_data_size + 11) != tag_data_size + 11) { - GST_WARNING_OBJECT (demux, "Invalid tag size"); - return GST_CLOCK_TIME_NONE; - } - } - - if (tag_size) - *tag_size = tag_data_size + 11 + 4; - - data += 4; - - GST_LOG_OBJECT (demux, "pts bytes %02X %02X %02X %02X", data[0], data[1], - data[2], data[3]); - - /* Grab timestamp of tag tag */ - pts = GST_READ_UINT24_BE (data); - /* read the pts extension to 32 bits integer */ - pts_ext = GST_READ_UINT8 (data + 3); - /* Combine them */ - pts |= pts_ext << 24; - - if (type == 9) { - data += 7; - - keyframe = ((data[0] >> 4) == 1); - } - - ret = pts * GST_MSECOND; - - if (demux->index && (type == 9 || (type == 8 && !demux->has_video))) { - GST_LOG_OBJECT (demux, "adding association %" GST_TIME_FORMAT "-> %" - G_GUINT64_FORMAT, GST_TIME_ARGS (ret), demux->offset); - gst_index_add_association (demux->index, demux->index_id, - (keyframe) ? GST_ASSOCIATION_FLAG_KEY_UNIT : GST_ASSOCIATION_FLAG_NONE, - GST_FORMAT_TIME, ret, GST_FORMAT_BYTES, demux->offset, NULL); - } - - if (demux->duration == GST_CLOCK_TIME_NONE || demux->duration < ret) - demux->duration = ret; - - return ret; -} - -GstFlowReturn -gst_flv_parse_tag_type (GstFLVDemux * demux, GstBuffer * buffer) -{ - GstFlowReturn ret = GST_FLOW_OK; - guint8 tag_type = 0; - guint8 *data = GST_BUFFER_DATA (buffer); - - g_return_val_if_fail (GST_BUFFER_SIZE (buffer) >= 4, GST_FLOW_ERROR); - - tag_type = data[0]; - - switch (tag_type) { - case 9: - demux->state = FLV_STATE_TAG_VIDEO; - demux->has_video = TRUE; - break; - case 8: - demux->state = FLV_STATE_TAG_AUDIO; - demux->has_audio = TRUE; - break; - case 18: - demux->state = FLV_STATE_TAG_SCRIPT; - break; - default: - GST_WARNING_OBJECT (demux, "unsupported tag type %u", tag_type); - } - - /* Tag size is 1 byte of type + 3 bytes of size + 7 bytes + tag data size + - * 4 bytes of previous tag size */ - demux->tag_data_size = GST_READ_UINT24_BE (data + 1); - demux->tag_size = demux->tag_data_size + 11; - - GST_LOG_OBJECT (demux, "tag data size is %" G_GUINT64_FORMAT, - demux->tag_data_size); - - return ret; -} - -GstFlowReturn -gst_flv_parse_header (GstFLVDemux * demux, GstBuffer * buffer) -{ - GstFlowReturn ret = GST_FLOW_OK; - guint8 *data = GST_BUFFER_DATA (buffer); - - g_return_val_if_fail (GST_BUFFER_SIZE (buffer) >= 9, GST_FLOW_ERROR); - - /* Check for the FLV tag */ - if (data[0] == 'F' && data[1] == 'L' && data[2] == 'V') { - GST_DEBUG_OBJECT (demux, "FLV header detected"); - } else { - if (G_UNLIKELY (demux->strict)) { - GST_WARNING_OBJECT (demux, "invalid header tag detected"); - ret = GST_FLOW_UNEXPECTED; - goto beach; - } - } - - /* Jump over the 4 first bytes */ - data += 4; - - /* Now look at audio/video flags */ - { - guint8 flags = data[0]; - - demux->has_video = demux->has_audio = FALSE; - - if (flags & 1) { - GST_DEBUG_OBJECT (demux, "there is a video stream"); - demux->has_video = TRUE; - } - if (flags & 4) { - GST_DEBUG_OBJECT (demux, "there is an audio stream"); - demux->has_audio = TRUE; - } - } - - /* We don't care about the rest */ - demux->need_header = FALSE; - -beach: - return ret; -} diff --git a/gst/id3tag/Makefile.am b/gst/id3tag/Makefile.am new file mode 100644 index 00000000..9595be0f --- /dev/null +++ b/gst/id3tag/Makefile.am @@ -0,0 +1,19 @@ +plugin_LTLIBRARIES = libgstid3tag.la + +libgstid3tag_la_SOURCES = \ + gsttagmux.c \ + id3tag.c \ + gstid3tag.c + +libgstid3tag_la_CFLAGS = \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_CFLAGS) + +libgstid3tag_la_LIBADD = \ + $(GST_PLUGINS_BASE_LIBS) -lgsttag-$(GST_MAJORMINOR) \ + $(GST_LIBS) + +libgstid3tag_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstid3tag_la_LIBTOOLFLAGS = --tag=disable-static + +noinst_HEADERS = gstid3tag.h id3tag.h gsttagmux.h diff --git a/gst/id3tag/gstid3tag.c b/gst/id3tag/gstid3tag.c new file mode 100644 index 00000000..9c8072c0 --- /dev/null +++ b/gst/id3tag/gstid3tag.c @@ -0,0 +1,229 @@ +/* GStreamer ID3 v1 and v2 muxer + * + * Copyright (C) 2006 Christophe Fergeau <teuf@gnome.org> + * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net> + * Copyright (C) 2009 Pioneers of the Inevitable <songbird@songbirdnest.com> + * + * 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. + */ + +/** + * SECTION:element-id3tag + * @see_also: #GstID3Demux, #GstTagSetter + * + * This element adds ID3v2 tags to the beginning of a stream, and ID3v1 tags + * to the end. + * + * It defaults to writing ID3 version 2.3.0 tags (since those are the most + * widely supported), but can optionally write version 2.4.0 tags. + * + * Applications can set the tags to write using the #GstTagSetter interface. + * Tags sent by upstream elements will be picked up automatically (and merged + * according to the merge mode set via the tag setter interface). + * + * <refsect2> + * <title>Example pipelines</title> + * |[ + * gst-launch -v filesrc location=foo.ogg ! decodebin ! audioconvert ! lame ! id3tag ! filesink location=foo.mp3 + * ]| A pipeline that transcodes a file from Ogg/Vorbis to mp3 format with + * ID3 tags that contain the same metadata as the the Ogg/Vorbis file. + * Make sure the Ogg/Vorbis file actually has comments to preserve. + * |[ + * gst-launch -m filesrc location=foo.mp3 ! id3demux ! fakesink silent=TRUE 2> /dev/null | grep taglist + * ]| Verify that tags have been written. + * </refsect2> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "gstid3tag.h" +#include <gst/tag/tag.h> + +#include <string.h> + +GST_DEBUG_CATEGORY (gst_id3_tag_debug); +#define GST_CAT_DEFAULT gst_id3_tag_debug + +enum +{ + ARG_0, + ARG_WRITE_V1, + ARG_WRITE_V2, + ARG_V2_MAJOR_VERSION +}; + +#define DEFAULT_WRITE_V1 TRUE +#define DEFAULT_WRITE_V2 TRUE +#define DEFAULT_V2_MAJOR_VERSION 3 + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-id3")); + +GST_BOILERPLATE (GstId3Tag, gst_id3_tag, GstTagMux, GST_TYPE_TAG_MUX); + +static GstBuffer *gst_id3_tag_render_v2_tag (GstTagMux * mux, + GstTagList * taglist); +static GstBuffer *gst_id3_tag_render_v1_tag (GstTagMux * mux, + GstTagList * taglist); + +static void gst_id3_tag_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_id3_tag_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static void +gst_id3_tag_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_template)); + + gst_element_class_set_details_simple (element_class, + "ID3 v1 and v2 Muxer", "Formatter/Metadata", + "Adds an ID3v2 header and ID3v1 footer to a file", + "Michael Smith <msmith@songbirdnest.com>, " + "Tim-Philipp Müller <tim centricular net>"); + + GST_DEBUG_CATEGORY_INIT (gst_id3_tag_debug, "id3tag", 0, + "ID3 v1 and v2 tag muxer"); +} + +static void +gst_id3_tag_class_init (GstId3TagClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + gobject_class->set_property = gst_id3_tag_set_property; + gobject_class->get_property = gst_id3_tag_get_property; + + g_object_class_install_property (gobject_class, ARG_WRITE_V1, + g_param_spec_boolean ("write-v1", "Write id3v1 tag", + "Write an id3v1 tag at the end of the file", DEFAULT_WRITE_V1, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_object_class_install_property (gobject_class, ARG_WRITE_V2, + g_param_spec_boolean ("write-v2", "Write id3v2 tag", + "Write an id3v2 tag at the start of the file", DEFAULT_WRITE_V2, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_object_class_install_property (gobject_class, ARG_V2_MAJOR_VERSION, + g_param_spec_int ("v2-version", "Version (3 or 4) of id3v2 tag", + "Set version (3 for id3v2.3, 4 for id3v2.4) of id3v2 tags", + 3, 4, DEFAULT_V2_MAJOR_VERSION, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + GST_TAG_MUX_CLASS (klass)->render_start_tag = + GST_DEBUG_FUNCPTR (gst_id3_tag_render_v2_tag); + + GST_TAG_MUX_CLASS (klass)->render_end_tag = gst_id3_tag_render_v1_tag; +} + +static void +gst_id3_tag_init (GstId3Tag * id3mux, GstId3TagClass * id3mux_class) +{ + id3mux->write_v1 = DEFAULT_WRITE_V1; + id3mux->write_v2 = DEFAULT_WRITE_V2; + + id3mux->v2_major_version = DEFAULT_V2_MAJOR_VERSION; +} + +static void +gst_id3_tag_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstId3Tag *mux = GST_ID3TAG (object); + + switch (prop_id) { + case ARG_WRITE_V1: + mux->write_v1 = g_value_get_boolean (value); + break; + case ARG_WRITE_V2: + mux->write_v2 = g_value_get_boolean (value); + break; + case ARG_V2_MAJOR_VERSION: + mux->v2_major_version = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_id3_tag_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstId3Tag *mux = GST_ID3TAG (object); + + switch (prop_id) { + case ARG_WRITE_V1: + g_value_set_boolean (value, mux->write_v1); + break; + case ARG_WRITE_V2: + g_value_set_boolean (value, mux->write_v2); + break; + case ARG_V2_MAJOR_VERSION: + g_value_set_int (value, mux->v2_major_version); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstBuffer * +gst_id3_tag_render_v2_tag (GstTagMux * mux, GstTagList * taglist) +{ + GstId3Tag *id3mux = GST_ID3TAG (mux); + + if (id3mux->write_v2) + return gst_id3mux_render_v2_tag (mux, taglist, id3mux->v2_major_version); + else + return NULL; +} + +static GstBuffer * +gst_id3_tag_render_v1_tag (GstTagMux * mux, GstTagList * taglist) +{ + GstId3Tag *id3mux = GST_ID3TAG (mux); + + if (id3mux->write_v1) + return gst_id3mux_render_v1_tag (mux, taglist); + else + return NULL; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + if (!gst_element_register (plugin, "id3tag", GST_RANK_NONE, GST_TYPE_ID3TAG)) + return FALSE; + + gst_tag_register_musicbrainz_tags (); + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "id3tag", + "ID3 v1 and v2 muxing plugin", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); diff --git a/gst/id3tag/gstid3tag.h b/gst/id3tag/gstid3tag.h new file mode 100644 index 00000000..a9a1ad1c --- /dev/null +++ b/gst/id3tag/gstid3tag.h @@ -0,0 +1,63 @@ +/* GStreamer ID3 v1 and v2 muxer + * + * Copyright (C) 2006 Christophe Fergeau <teuf@gnome.org> + * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net> + * Copyright (C) 2009 Pioneers of the Inevitable <songbird@songbirdnest.com> + * + * 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_ID3TAG_H +#define GST_ID3TAG_H + +#include "gsttagmux.h" +#include "id3tag.h" + +G_BEGIN_DECLS + +typedef struct _GstId3Tag GstId3Tag; +typedef struct _GstId3TagClass GstId3TagClass; + +struct _GstId3Tag { + GstTagMux tagmux; + + gboolean write_v1; + gboolean write_v2; + + gint v2_major_version; +}; + +struct _GstId3TagClass { + GstTagMuxClass tagmux_class; +}; + +#define GST_TYPE_ID3TAG \ + (gst_id3_tag_get_type()) +#define GST_ID3TAG(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ID3TAG,GstId3Tag)) +#define GST_ID3TAG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ID3TAG,GstId3TagClass)) +#define GST_IS_ID3TAG(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ID3TAG)) +#define GST_IS_ID3TAG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ID3TAG)) + +GType gst_id3_tag_get_type (void); + +G_END_DECLS + +#endif /* GST_ID3TAG_H */ + diff --git a/gst/id3tag/gsttagmux.c b/gst/id3tag/gsttagmux.c new file mode 100644 index 00000000..3b7ff119 --- /dev/null +++ b/gst/id3tag/gsttagmux.c @@ -0,0 +1,495 @@ +/* GStreamer tag muxer base class + * + * Copyright (C) 2006 Christophe Fergeau <teuf@gnome.org> + * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net> + * Copyright (C) 2006 Sebastian Dröge <slomo@circular-chaos.org> + * Copyright (C) 2009 Pioneers of the Inevitable <songbird@songbirdnest.com> + * + * 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 <string.h> +#include <gst/gsttagsetter.h> +#include <gst/tag/tag.h> + +#include "gsttagmux.h" + +GST_DEBUG_CATEGORY_STATIC (gst_tag_mux_debug); +#define GST_CAT_DEFAULT gst_tag_mux_debug + +/* Subclass provides a src template and pad. We accept anything as input here, + however. */ + +static GstStaticPadTemplate gst_tag_mux_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("ANY")); + +static void +gst_tag_mux_iface_init (GType tag_type) +{ + static const GInterfaceInfo tag_setter_info = { + NULL, + NULL, + NULL + }; + + g_type_add_interface_static (tag_type, GST_TYPE_TAG_SETTER, &tag_setter_info); +} + +/* make sure to register a less generic type so we can easily move this + * GstTagMux base class into -base without causing GType name conflicts */ +typedef GstTagMux GstId3TagMux; +typedef GstTagMuxClass GstId3TagMuxClass; + +GST_BOILERPLATE_FULL (GstId3TagMux, gst_tag_mux, + GstElement, GST_TYPE_ELEMENT, gst_tag_mux_iface_init); + + +static GstStateChangeReturn +gst_tag_mux_change_state (GstElement * element, GstStateChange transition); +static GstFlowReturn gst_tag_mux_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_tag_mux_sink_event (GstPad * pad, GstEvent * event); + +static void +gst_tag_mux_finalize (GObject * obj) +{ + GstTagMux *mux = GST_TAG_MUX (obj); + + if (mux->newsegment_ev) { + gst_event_unref (mux->newsegment_ev); + mux->newsegment_ev = NULL; + } + + if (mux->event_tags) { + gst_tag_list_free (mux->event_tags); + mux->event_tags = NULL; + } + + if (mux->final_tags) { + gst_tag_list_free (mux->final_tags); + mux->final_tags = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + +static void +gst_tag_mux_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_tag_mux_sink_template)); + + GST_DEBUG_CATEGORY_INIT (gst_tag_mux_debug, "tagmux", 0, + "tag muxer base class"); +} + +static void +gst_tag_mux_class_init (GstTagMuxClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_tag_mux_finalize); + gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_tag_mux_change_state); +} + +static void +gst_tag_mux_init (GstTagMux * mux, GstTagMuxClass * mux_class) +{ + GstElementClass *element_klass = GST_ELEMENT_CLASS (mux_class); + GstPadTemplate *tmpl; + + /* pad through which data comes in to the element */ + mux->sinkpad = + gst_pad_new_from_static_template (&gst_tag_mux_sink_template, "sink"); + gst_pad_set_chain_function (mux->sinkpad, + GST_DEBUG_FUNCPTR (gst_tag_mux_chain)); + gst_pad_set_event_function (mux->sinkpad, + GST_DEBUG_FUNCPTR (gst_tag_mux_sink_event)); + gst_element_add_pad (GST_ELEMENT (mux), mux->sinkpad); + + /* pad through which data goes out of the element */ + tmpl = gst_element_class_get_pad_template (element_klass, "src"); + if (tmpl) { + mux->srcpad = gst_pad_new_from_template (tmpl, "src"); + gst_pad_use_fixed_caps (mux->srcpad); + gst_pad_set_caps (mux->srcpad, gst_pad_template_get_caps (tmpl)); + gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad); + } + + mux->render_start_tag = TRUE; + mux->render_end_tag = TRUE; +} + +static GstTagList * +gst_tag_mux_get_tags (GstTagMux * mux) +{ + GstTagSetter *tagsetter = GST_TAG_SETTER (mux); + const GstTagList *tagsetter_tags; + GstTagMergeMode merge_mode; + + if (mux->final_tags) + return mux->final_tags; + + tagsetter_tags = gst_tag_setter_get_tag_list (tagsetter); + merge_mode = gst_tag_setter_get_tag_merge_mode (tagsetter); + + GST_LOG_OBJECT (mux, "merging tags, merge mode = %d", merge_mode); + GST_LOG_OBJECT (mux, "event tags: %" GST_PTR_FORMAT, mux->event_tags); + GST_LOG_OBJECT (mux, "set tags: %" GST_PTR_FORMAT, tagsetter_tags); + + mux->final_tags = + gst_tag_list_merge (tagsetter_tags, mux->event_tags, merge_mode); + + GST_LOG_OBJECT (mux, "final tags: %" GST_PTR_FORMAT, mux->final_tags); + + return mux->final_tags; +} + +static GstFlowReturn +gst_tag_mux_render_start_tag (GstTagMux * mux) +{ + GstTagMuxClass *klass; + GstBuffer *buffer; + GstTagList *taglist; + GstEvent *event; + GstFlowReturn ret; + + taglist = gst_tag_mux_get_tags (mux); + + klass = GST_TAG_MUX_CLASS (G_OBJECT_GET_CLASS (mux)); + + if (klass->render_start_tag == NULL) + goto no_vfunc; + + buffer = klass->render_start_tag (mux, taglist); + + /* Null buffer is ok, just means we're not outputting anything */ + if (buffer == NULL) { + GST_INFO_OBJECT (mux, "No start tag generated"); + mux->start_tag_size = 0; + return GST_FLOW_OK; + } + + mux->start_tag_size = GST_BUFFER_SIZE (buffer); + GST_LOG_OBJECT (mux, "tag size = %" G_GSIZE_FORMAT " bytes", + mux->start_tag_size); + + /* Send newsegment event from byte position 0, so the tag really gets + * written to the start of the file, independent of the upstream segment */ + gst_pad_push_event (mux->srcpad, + gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0)); + + /* Send an event about the new tags to downstream elements */ + /* gst_event_new_tag takes ownership of the list, so use a copy */ + event = gst_event_new_tag (gst_tag_list_copy (taglist)); + gst_pad_push_event (mux->srcpad, event); + + GST_BUFFER_OFFSET (buffer) = 0; + ret = gst_pad_push (mux->srcpad, buffer); + + mux->current_offset = mux->start_tag_size; + mux->max_offset = MAX (mux->max_offset, mux->current_offset); + + return ret; + +no_vfunc: + { + GST_ERROR_OBJECT (mux, "Subclass does not implement " + "render_start_tag vfunc!"); + return GST_FLOW_ERROR; + } +} + +static GstFlowReturn +gst_tag_mux_render_end_tag (GstTagMux * mux) +{ + GstTagMuxClass *klass; + GstBuffer *buffer; + GstTagList *taglist; + GstFlowReturn ret; + + taglist = gst_tag_mux_get_tags (mux); + + klass = GST_TAG_MUX_CLASS (G_OBJECT_GET_CLASS (mux)); + + if (klass->render_end_tag == NULL) + goto no_vfunc; + + buffer = klass->render_end_tag (mux, taglist); + + if (buffer == NULL) { + GST_INFO_OBJECT (mux, "No end tag generated"); + mux->end_tag_size = 0; + return GST_FLOW_OK; + } + + mux->end_tag_size = GST_BUFFER_SIZE (buffer); + GST_LOG_OBJECT (mux, "tag size = %" G_GSIZE_FORMAT " bytes", + mux->end_tag_size); + + /* Send newsegment event from the end of the file, so it gets written there, + independent of whatever new segment events upstream has sent us */ + gst_pad_push_event (mux->srcpad, + gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, mux->max_offset, + -1, 0)); + + GST_BUFFER_OFFSET (buffer) = mux->max_offset; + ret = gst_pad_push (mux->srcpad, buffer); + + return ret; + +no_vfunc: + { + GST_ERROR_OBJECT (mux, "Subclass does not implement " + "render_end_tag vfunc!"); + return GST_FLOW_ERROR; + } +} + +static GstEvent * +gst_tag_mux_adjust_event_offsets (GstTagMux * mux, + const GstEvent * newsegment_event) +{ + GstFormat format; + gint64 start, stop, cur; + + gst_event_parse_new_segment ((GstEvent *) newsegment_event, NULL, NULL, + &format, &start, &stop, &cur); + + g_assert (format == GST_FORMAT_BYTES); + + if (start != -1) + start += mux->start_tag_size; + if (stop != -1) + stop += mux->start_tag_size; + if (cur != -1) + cur += mux->start_tag_size; + + GST_DEBUG_OBJECT (mux, "adjusting newsegment event offsets to start=%" + G_GINT64_FORMAT ", stop=%" G_GINT64_FORMAT ", cur=%" G_GINT64_FORMAT + " (delta = +%" G_GSIZE_FORMAT ")", start, stop, cur, mux->start_tag_size); + + return gst_event_new_new_segment (TRUE, 1.0, format, start, stop, cur); +} + +static GstFlowReturn +gst_tag_mux_chain (GstPad * pad, GstBuffer * buffer) +{ + GstTagMux *mux = GST_TAG_MUX (GST_OBJECT_PARENT (pad)); + GstFlowReturn ret; + int length; + + if (mux->render_start_tag) { + + GST_INFO_OBJECT (mux, "Adding tags to stream"); + ret = gst_tag_mux_render_start_tag (mux); + if (ret != GST_FLOW_OK) { + GST_DEBUG_OBJECT (mux, "flow: %s", gst_flow_get_name (ret)); + gst_buffer_unref (buffer); + return ret; + } + + /* Now send the cached newsegment event that we got from upstream */ + if (mux->newsegment_ev) { + gint64 start; + GstEvent *newseg; + + GST_DEBUG_OBJECT (mux, "sending cached newsegment event"); + newseg = gst_tag_mux_adjust_event_offsets (mux, mux->newsegment_ev); + gst_event_unref (mux->newsegment_ev); + mux->newsegment_ev = NULL; + + gst_event_parse_new_segment (newseg, NULL, NULL, NULL, &start, NULL, + NULL); + + gst_pad_push_event (mux->srcpad, newseg); + mux->current_offset = start; + mux->max_offset = MAX (mux->max_offset, mux->current_offset); + } else { + /* upstream sent no newsegment event or only one in a non-BYTE format */ + } + + mux->render_start_tag = FALSE; + } + + buffer = gst_buffer_make_metadata_writable (buffer); + + if (GST_BUFFER_OFFSET (buffer) != GST_BUFFER_OFFSET_NONE) { + GST_LOG_OBJECT (mux, "Adjusting buffer offset from %" G_GINT64_FORMAT + " to %" G_GINT64_FORMAT, GST_BUFFER_OFFSET (buffer), + GST_BUFFER_OFFSET (buffer) + mux->start_tag_size); + GST_BUFFER_OFFSET (buffer) += mux->start_tag_size; + } + + length = GST_BUFFER_SIZE (buffer); + + gst_buffer_set_caps (buffer, GST_PAD_CAPS (mux->srcpad)); + ret = gst_pad_push (mux->srcpad, buffer); + + mux->current_offset += length; + mux->max_offset = MAX (mux->max_offset, mux->current_offset); + + return ret; +} + +static gboolean +gst_tag_mux_sink_event (GstPad * pad, GstEvent * event) +{ + GstTagMux *mux; + gboolean result; + + mux = GST_TAG_MUX (gst_pad_get_parent (pad)); + result = FALSE; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_TAG:{ + GstTagList *tags; + + gst_event_parse_tag (event, &tags); + + GST_INFO_OBJECT (mux, "Got tag event: %" GST_PTR_FORMAT, tags); + + if (mux->event_tags != NULL) { + gst_tag_list_insert (mux->event_tags, tags, GST_TAG_MERGE_REPLACE); + } else { + mux->event_tags = gst_tag_list_copy (tags); + } + + GST_INFO_OBJECT (mux, "Event tags are now: %" GST_PTR_FORMAT, + mux->event_tags); + + /* just drop the event, we'll push a new tag event in render_start_tag */ + gst_event_unref (event); + result = TRUE; + break; + } + case GST_EVENT_NEWSEGMENT:{ + GstFormat fmt; + gint64 start; + + gst_event_parse_new_segment (event, NULL, NULL, &fmt, &start, NULL, NULL); + + if (fmt != GST_FORMAT_BYTES) { + GST_WARNING_OBJECT (mux, "dropping newsegment event in %s format", + gst_format_get_name (fmt)); + gst_event_unref (event); + break; + } + + if (mux->render_start_tag) { + /* we have not rendered the tag yet, which means that we don't know + * how large it is going to be yet, so we can't adjust the offsets + * here at this point and need to cache the newsegment event for now + * (also, there could be tag events coming after this newsegment event + * and before the first buffer). */ + if (mux->newsegment_ev) { + GST_WARNING_OBJECT (mux, "discarding old cached newsegment event"); + gst_event_unref (mux->newsegment_ev); + } + + GST_LOG_OBJECT (mux, "caching newsegment event for later"); + mux->newsegment_ev = event; + } else { + GST_DEBUG_OBJECT (mux, "got newsegment event, adjusting offsets"); + gst_pad_push_event (mux->srcpad, + gst_tag_mux_adjust_event_offsets (mux, event)); + gst_event_unref (event); + + mux->current_offset = start; + mux->max_offset = MAX (mux->max_offset, mux->current_offset); + } + event = NULL; + result = TRUE; + break; + } + case GST_EVENT_EOS:{ + if (mux->render_end_tag) { + GstFlowReturn ret; + + GST_INFO_OBJECT (mux, "Adding tags to stream"); + ret = gst_tag_mux_render_end_tag (mux); + if (ret != GST_FLOW_OK) { + GST_DEBUG_OBJECT (mux, "flow: %s", gst_flow_get_name (ret)); + return ret; + } + + mux->render_end_tag = FALSE; + } + + /* Now forward EOS */ + result = gst_pad_event_default (pad, event); + break; + } + default: + result = gst_pad_event_default (pad, event); + break; + } + + gst_object_unref (mux); + + return result; +} + + +static GstStateChangeReturn +gst_tag_mux_change_state (GstElement * element, GstStateChange transition) +{ + GstTagMux *mux; + GstStateChangeReturn result; + + mux = GST_TAG_MUX (element); + + result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (result != GST_STATE_CHANGE_SUCCESS) { + return result; + } + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY:{ + if (mux->newsegment_ev) { + gst_event_unref (mux->newsegment_ev); + mux->newsegment_ev = NULL; + } + if (mux->event_tags) { + gst_tag_list_free (mux->event_tags); + mux->event_tags = NULL; + } + mux->start_tag_size = 0; + mux->end_tag_size = 0; + mux->render_start_tag = TRUE; + mux->render_end_tag = TRUE; + mux->current_offset = 0; + mux->max_offset = 0; + break; + } + default: + break; + } + + return result; +} diff --git a/gst/id3tag/gsttagmux.h b/gst/id3tag/gsttagmux.h new file mode 100644 index 00000000..c13a7326 --- /dev/null +++ b/gst/id3tag/gsttagmux.h @@ -0,0 +1,79 @@ +/* GStreamer tag muxer base class + * + * Copyright (C) 2006 Christophe Fergeau <teuf@gnome.org> + * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net> + * Copyright (C) 2009 Pioneers of the Inevitable <songbird@songbirdnest.com> + * + * 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_TAG_MUX_H +#define GST_TAG_MUX_H + +#include <gst/gst.h> + +G_BEGIN_DECLS + +typedef struct _GstTagMux GstTagMux; +typedef struct _GstTagMuxClass GstTagMuxClass; + +/* Definition of structure storing data for this element. */ +struct _GstTagMux { + GstElement element; + + GstPad *srcpad; + GstPad *sinkpad; + GstTagList *event_tags; /* tags received from upstream elements */ + GstTagList *final_tags; /* Final set of tags used for muxing */ + gsize start_tag_size; + gsize end_tag_size; + gboolean render_start_tag; + gboolean render_end_tag; + + gint64 current_offset; + gint64 max_offset; + + GstEvent *newsegment_ev; /* cached newsegment event from upstream */ +}; + +/* Standard definition defining a class for this element. */ +struct _GstTagMuxClass { + GstElementClass parent_class; + + /* vfuncs */ + GstBuffer * (*render_start_tag) (GstTagMux * mux, GstTagList * tag_list); + GstBuffer * (*render_end_tag) (GstTagMux * mux, GstTagList * tag_list); +}; + +/* Standard macros for defining types for this element. */ +#define GST_TYPE_TAG_MUX \ + (gst_tag_mux_get_type()) +#define GST_TAG_MUX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_TAG_MUX,GstTagMux)) +#define GST_TAG_MUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_TAG_MUX,GstTagMuxClass)) +#define GST_IS_TAG_MUX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_TAG_MUX)) +#define GST_IS_TAG_MUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_TAG_MUX)) + +/* Standard function returning type information. */ +GType gst_tag_mux_get_type (void); + +G_END_DECLS + +#endif + diff --git a/gst/id3tag/id3tag.c b/gst/id3tag/id3tag.c new file mode 100644 index 00000000..a39e2a8e --- /dev/null +++ b/gst/id3tag/id3tag.c @@ -0,0 +1,1194 @@ +/* GStreamer ID3v2 tag writer + * + * Copyright (C) 2006 Christophe Fergeau <teuf@gnome.org> + * Copyright (C) 2006-2009 Tim-Philipp Müller <tim centricular net> + * Copyright (C) 2009 Pioneers of the Inevitable <songbird@songbirdnest.com> + * + * 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 "id3tag.h" +#include <string.h> + +#include <gst/tag/tag.h> + +GST_DEBUG_CATEGORY_EXTERN (gst_id3_tag_debug); +#define GST_CAT_DEFAULT gst_id3_tag_debug + +#define ID3V2_APIC_PICTURE_OTHER 0 +#define ID3V2_APIC_PICTURE_FILE_ICON 1 + +/* ======================================================================== */ + +typedef GString GstByteWriter; + +static inline GstByteWriter * +gst_byte_writer_new (guint size) +{ + return (GstByteWriter *) g_string_sized_new (size); +} + +static inline guint +gst_byte_writer_get_length (GstByteWriter * w) +{ + return ((GString *) w)->len; +} + +static inline void +gst_byte_writer_write_bytes (GstByteWriter * w, const guint8 * data, guint len) +{ + g_string_append_len ((GString *) w, (const gchar *) data, len); +} + +static inline void +gst_byte_writer_write_uint8 (GstByteWriter * w, guint8 val) +{ + guint8 data[1]; + + GST_WRITE_UINT8 (data, val); + gst_byte_writer_write_bytes (w, data, 1); +} + +static inline void +gst_byte_writer_write_uint16 (GstByteWriter * w, guint16 val) +{ + guint8 data[2]; + + GST_WRITE_UINT16_BE (data, val); + gst_byte_writer_write_bytes (w, data, 2); +} + +static inline void +gst_byte_writer_write_uint32 (GstByteWriter * w, guint32 val) +{ + guint8 data[4]; + + GST_WRITE_UINT32_BE (data, val); + gst_byte_writer_write_bytes (w, data, 4); +} + +static inline void +gst_byte_writer_write_uint32_syncsafe (GstByteWriter * w, guint32 val) +{ + guint8 data[4]; + + data[0] = (guint8) ((val >> 21) & 0x7f); + data[1] = (guint8) ((val >> 14) & 0x7f); + data[2] = (guint8) ((val >> 7) & 0x7f); + data[3] = (guint8) ((val >> 0) & 0x7f); + gst_byte_writer_write_bytes (w, data, 4); +} + +static void +gst_byte_writer_copy_bytes (GstByteWriter * w, guint8 * dest, guint offset, + gint size) +{ + guint length; + + length = gst_byte_writer_get_length (w); + + if (size == -1) + size = length - offset; + +#if GLIB_CHECK_VERSION(2,16,0) + g_warn_if_fail (length >= (offset + size)); +#endif + + memcpy (dest, w->str + offset, MIN (size, length - offset)); +} + +static inline void +gst_byte_writer_free (GstByteWriter * w) +{ + g_string_free (w, TRUE); +} + +/* ======================================================================== */ + +/* +typedef enum { + GST_ID3V2_FRAME_FLAG_NONE = 0, + GST_ID3V2_FRAME_FLAG_ +} GstID3v2FrameMsgFlags; +*/ + +typedef struct +{ + gchar id[5]; + guint32 len; /* Length encoded in the header; this is the + total length - header size */ + guint16 flags; + GstByteWriter *writer; + gboolean dirty; /* TRUE if frame header needs updating */ +} GstId3v2Frame; + +typedef struct +{ + GArray *frames; + guint major_version; /* The 3 in v2.3.0 */ +} GstId3v2Tag; + +typedef void (*GstId3v2AddTagFunc) (GstId3v2Tag * tag, const GstTagList * list, + const gchar * gst_tag, guint num_tags, const gchar * data); + +#define ID3V2_ENCODING_UTF8 0x03 + +static gboolean id3v2_tag_init (GstId3v2Tag * tag, guint major_version); +static void id3v2_tag_unset (GstId3v2Tag * tag); + +static void id3v2_frame_init (GstId3v2Frame * frame, + const gchar * frame_id, guint16 flags); +static void id3v2_frame_unset (GstId3v2Frame * frame); +static void id3v2_frame_finish (GstId3v2Tag * tag, GstId3v2Frame * frame); +static guint id3v2_frame_get_size (GstId3v2Tag * tag, GstId3v2Frame * frame); + +static void id3v2_tag_add_text_frame (GstId3v2Tag * tag, + const gchar * frame_id, gchar ** strings, int num_strings); + +static gboolean +id3v2_tag_init (GstId3v2Tag * tag, guint major_version) +{ + if (major_version != 3 && major_version != 4) + return FALSE; + + tag->major_version = major_version; + tag->frames = g_array_new (TRUE, TRUE, sizeof (GstId3v2Frame)); + + return TRUE; +} + +static void +id3v2_tag_unset (GstId3v2Tag * tag) +{ + guint i; + + for (i = 0; i < tag->frames->len; ++i) + id3v2_frame_unset (&g_array_index (tag->frames, GstId3v2Frame, i)); + + g_array_free (tag->frames, TRUE); + memset (tag, 0, sizeof (GstId3v2Tag)); +} + +#ifndef GST_ROUND_UP_1024 +#define GST_ROUND_UP_1024(num) (((num)+1023)&~1023) +#endif + +static GstBuffer * +id3v2_tag_to_buffer (GstId3v2Tag * tag) +{ + GstByteWriter *w; + GstBuffer *buf; + guint8 *dest; + guint i, size, offset, size_frames = 0; + + GST_DEBUG ("Creating buffer for ID3v2 tag containing %d frames", + tag->frames->len); + + for (i = 0; i < tag->frames->len; ++i) { + GstId3v2Frame *frame = &g_array_index (tag->frames, GstId3v2Frame, i); + + id3v2_frame_finish (tag, frame); + size_frames += id3v2_frame_get_size (tag, frame); + } + + size = GST_ROUND_UP_1024 (10 + size_frames); + + w = gst_byte_writer_new (10); + gst_byte_writer_write_uint8 (w, 'I'); + gst_byte_writer_write_uint8 (w, 'D'); + gst_byte_writer_write_uint8 (w, '3'); + gst_byte_writer_write_uint8 (w, tag->major_version); + gst_byte_writer_write_uint8 (w, 0); /* micro version */ + gst_byte_writer_write_uint8 (w, 0); /* flags */ + gst_byte_writer_write_uint32_syncsafe (w, size - 10); + + buf = gst_buffer_new_and_alloc (size); + dest = GST_BUFFER_DATA (buf); + gst_byte_writer_copy_bytes (w, dest, 0, 10); + offset = 10; + + for (i = 0; i < tag->frames->len; ++i) { + GstId3v2Frame *frame = &g_array_index (tag->frames, GstId3v2Frame, i); + + gst_byte_writer_copy_bytes (frame->writer, dest + offset, 0, -1); + offset += id3v2_frame_get_size (tag, frame); + } + + /* Zero out any additional space in our buffer as padding. */ + memset (dest + offset, 0, size - offset); + + gst_byte_writer_free (w); + + return buf; +} + +static inline void +id3v2_frame_write_bytes (GstId3v2Frame * frame, const guint8 * data, guint len) +{ + gst_byte_writer_write_bytes (frame->writer, data, len); + frame->dirty = TRUE; +} + +static inline void +id3v2_frame_write_uint8 (GstId3v2Frame * frame, guint8 val) +{ + gst_byte_writer_write_uint8 (frame->writer, val); + frame->dirty = TRUE; +} + +static inline void +id3v2_frame_write_uint16 (GstId3v2Frame * frame, guint16 val) +{ + gst_byte_writer_write_uint16 (frame->writer, val); + frame->dirty = TRUE; +} + +static inline void +id3v2_frame_write_uint32 (GstId3v2Frame * frame, guint32 val) +{ + gst_byte_writer_write_uint32 (frame->writer, val); + frame->dirty = TRUE; +} + +static inline void +id3v2_frame_write_uint32_syncsafe (GstId3v2Frame * frame, guint32 val) +{ + guint8 data[4]; + + data[0] = (guint8) ((val >> 21) & 0x7f); + data[1] = (guint8) ((val >> 14) & 0x7f); + data[2] = (guint8) ((val >> 7) & 0x7f); + data[3] = (guint8) ((val >> 0) & 0x7f); + gst_byte_writer_write_bytes (frame->writer, data, 4); + frame->dirty = TRUE; +} + +static void +id3v2_frame_init (GstId3v2Frame * frame, const gchar * frame_id, guint16 flags) +{ + g_assert (strlen (frame_id) == 4); /* we only handle 2.3.0/2.4.0 */ + memcpy (frame->id, frame_id, 4 + 1); + frame->flags = flags; + frame->len = 0; + frame->writer = gst_byte_writer_new (64); + id3v2_frame_write_bytes (frame, (const guint8 *) frame->id, 4); + id3v2_frame_write_uint32 (frame, 0); /* size, set later */ + id3v2_frame_write_uint16 (frame, frame->flags); +} + +static void +id3v2_frame_finish (GstId3v2Tag * tag, GstId3v2Frame * frame) +{ + if (frame->dirty) { + frame->len = frame->writer->len - 10; + GST_LOG ("[%s] %u bytes", frame->id, frame->len); + if (tag->major_version == 3) { + GST_WRITE_UINT32_BE (frame->writer->str + 4, frame->len); + } else { + /* Version 4 uses a syncsafe int here */ + GST_WRITE_UINT8 (frame->writer->str + 4, (frame->len >> 21) & 0x7f); + GST_WRITE_UINT8 (frame->writer->str + 5, (frame->len >> 14) & 0x7f); + GST_WRITE_UINT8 (frame->writer->str + 6, (frame->len >> 7) & 0x7f); + GST_WRITE_UINT8 (frame->writer->str + 7, (frame->len >> 0) & 0x7f); + } + frame->dirty = FALSE; + } +} + +static guint +id3v2_frame_get_size (GstId3v2Tag * tag, GstId3v2Frame * frame) +{ + id3v2_frame_finish (tag, frame); + return gst_byte_writer_get_length (frame->writer); +} + +static void +id3v2_frame_unset (GstId3v2Frame * frame) +{ + gst_byte_writer_free (frame->writer); + memset (frame, 0, sizeof (GstId3v2Frame)); +} + +static void +id3v2_tag_add_text_frame (GstId3v2Tag * tag, const gchar * frame_id, + gchar ** strings_utf8, int num_strings) +{ + GstId3v2Frame frame; + guint len, i; + + if (num_strings < 1 || strings_utf8 == NULL || strings_utf8[0] == NULL) { + GST_LOG ("Not adding text frame, no strings"); + return; + } + + id3v2_frame_init (&frame, frame_id, 0); + id3v2_frame_write_uint8 (&frame, ID3V2_ENCODING_UTF8); + + GST_LOG ("Adding text frame %s with %d strings", frame_id, num_strings); + + for (i = 0; i < num_strings; ++i) { + len = strlen (strings_utf8[i]); + g_return_if_fail (g_utf8_validate (strings_utf8[i], len, NULL)); + + /* write NUL terminator as well */ + id3v2_frame_write_bytes (&frame, (const guint8 *) strings_utf8[i], len + 1); + + /* only v2.4.0 supports multiple strings per frame (according to the + * earlier specs tag readers should just ignore everything after the first + * string, but we probably shouldn't write anything there, just in case + * tag readers that only support the old version are not expecting + * more data after the first string) */ + if (tag->major_version < 4) + break; + } + + if (i < num_strings - 1) { + GST_WARNING ("Only wrote one of multiple string values for text frame %s " + "- ID3v2 supports multiple string values only since v2.4.0, but writing" + "v2.%u.0 tag", frame_id, tag->major_version); + } + + g_array_append_val (tag->frames, frame); +} + +/* ====================================================================== */ + +static void +add_text_tag (GstId3v2Tag * id3v2tag, const GstTagList * list, + const gchar * tag, guint num_tags, const gchar * frame_id) +{ + gchar **strings; + guint n, i; + + GST_LOG ("Adding '%s' frame", frame_id); + + strings = g_new0 (gchar *, num_tags + 1); + for (n = 0, i = 0; n < num_tags; ++n) { + if (gst_tag_list_get_string_index (list, tag, n, &strings[i]) && + strings[i] != NULL) { + GST_LOG ("%s: %s[%u] = '%s'", frame_id, tag, i, strings[i]); + ++i; + } + } + + if (strings[0] != NULL) { + id3v2_tag_add_text_frame (id3v2tag, frame_id, strings, i); + } else { + GST_WARNING ("Empty list for tag %s, skipping", tag); + } + + g_strfreev (strings); +} + +static void +add_id3v2frame_tag (GstId3v2Tag * id3v2tag, const GstTagList * list, + const gchar * tag, guint num_tags, const gchar * unused) +{ + guint i; + + for (i = 0; i < num_tags; ++i) { + const GValue *val; + GstBuffer *buf; + + val = gst_tag_list_get_value_index (list, tag, i); + buf = (GstBuffer *) gst_value_get_mini_object (val); + + if (buf && GST_BUFFER_CAPS (buf)) { + GstStructure *s; + gint version = 0; + + s = gst_caps_get_structure (GST_BUFFER_CAPS (buf), 0); + /* We can only add it if this private buffer is for the same ID3 version, + because we don't understand the contents at all. */ + if (s && gst_structure_get_int (s, "version", &version) && + version == id3v2tag->major_version) { + GstId3v2Frame frame; + gchar frame_id[5]; + guint16 flags; + guint8 *data = GST_BUFFER_DATA (buf); + gint size = GST_BUFFER_SIZE (buf); + + if (size < 10) /* header size */ + return; + + /* We only reach here if the frame version matches the muxer. Since the + muxer only does v2.3 or v2.4, the frame must be one of those - and + so the frame header is the same format */ + memcpy (frame_id, data, 4); + frame_id[4] = 0; + flags = GST_READ_UINT16_BE (data + 8); + + id3v2_frame_init (&frame, frame_id, flags); + id3v2_frame_write_bytes (&frame, data + 10, size - 10); + + g_array_append_val (id3v2tag->frames, frame); + GST_DEBUG ("Added unparsed tag with %d bytes", size); + } else { + GST_WARNING ("Discarding unrecognised ID3 tag for different ID3 " + "version"); + } + } + } +} + +static void +add_text_tag_v4 (GstId3v2Tag * id3v2tag, const GstTagList * list, + const gchar * tag, guint num_tags, const gchar * frame_id) +{ + if (id3v2tag->major_version == 4) + add_text_tag (id3v2tag, list, tag, num_tags, frame_id); + else { + GST_WARNING ("Cannot serialise tag '%s' in ID3v2.%d", frame_id, + id3v2tag->major_version); + } +} + +static void +add_count_or_num_tag (GstId3v2Tag * id3v2tag, const GstTagList * list, + const gchar * tag, guint num_tags, const gchar * frame_id) +{ + static const struct + { + const gchar *gst_tag; + const gchar *corr_count; /* corresponding COUNT tag (if number) */ + const gchar *corr_num; /* corresponding NUMBER tag (if count) */ + } corr[] = { + { + GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT, NULL}, { + GST_TAG_TRACK_COUNT, NULL, GST_TAG_TRACK_NUMBER}, { + GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT, NULL}, { + GST_TAG_ALBUM_VOLUME_COUNT, NULL, GST_TAG_ALBUM_VOLUME_NUMBER} + }; + guint idx; + + for (idx = 0; idx < G_N_ELEMENTS (corr); ++idx) { + if (strcmp (corr[idx].gst_tag, tag) == 0) + break; + } + + g_assert (idx < G_N_ELEMENTS (corr)); + g_assert (frame_id && strlen (frame_id) == 4); + + if (corr[idx].corr_num == NULL) { + guint number; + + /* number tag */ + if (gst_tag_list_get_uint_index (list, tag, 0, &number)) { + gchar *tag_str; + guint count; + + if (gst_tag_list_get_uint_index (list, corr[idx].corr_count, 0, &count)) + tag_str = g_strdup_printf ("%u/%u", number, count); + else + tag_str = g_strdup_printf ("%u", number); + + GST_DEBUG ("Setting %s to %s (frame_id = %s)", tag, tag_str, frame_id); + + id3v2_tag_add_text_frame (id3v2tag, frame_id, &tag_str, 1); + g_free (tag_str); + } + } else if (corr[idx].corr_count == NULL) { + guint count; + + /* count tag */ + if (gst_tag_list_get_uint_index (list, corr[idx].corr_num, 0, &count)) { + GST_DEBUG ("%s handled with %s, skipping", tag, corr[idx].corr_num); + } else if (gst_tag_list_get_uint_index (list, tag, 0, &count)) { + gchar *tag_str = g_strdup_printf ("0/%u", count); + GST_DEBUG ("Setting %s to %s (frame_id = %s)", tag, tag_str, frame_id); + + id3v2_tag_add_text_frame (id3v2tag, frame_id, &tag_str, 1); + g_free (tag_str); + } + } + + if (num_tags > 1) { + GST_WARNING ("more than one %s, can only handle one", tag); + } +} + +static void +add_comment_tag (GstId3v2Tag * id3v2tag, const GstTagList * list, + const gchar * tag, guint num_tags, const gchar * unused) +{ + guint n; + + GST_LOG ("Adding comment frames"); + for (n = 0; n < num_tags; ++n) { + gchar *s = NULL; + + if (gst_tag_list_get_string_index (list, tag, n, &s) && s != NULL) { + gchar *desc = NULL, *val = NULL, *lang = NULL; + int desclen, vallen; + GstId3v2Frame frame; + + id3v2_frame_init (&frame, "COMM", 0); + id3v2_frame_write_uint8 (&frame, ID3V2_ENCODING_UTF8); + + if (strcmp (tag, GST_TAG_COMMENT) == 0 || + !gst_tag_parse_extended_comment (s, &desc, &lang, &val, TRUE)) { + /* create dummy description fields */ + desc = g_strdup ("Comment"); + val = g_strdup (s); + } + + /* If we don't have a valid language, match what taglib does for + unknown languages */ + if (!lang || strlen (lang) < 3) + lang = g_strdup ("XXX"); + + desclen = strlen (desc); + g_return_if_fail (g_utf8_validate (desc, desclen, NULL)); + vallen = strlen (val); + g_return_if_fail (g_utf8_validate (val, vallen, NULL)); + + GST_LOG ("%s[%u] = '%s' (%s|%s|%s)", tag, n, s, GST_STR_NULL (desc), + GST_STR_NULL (lang), GST_STR_NULL (val)); + + id3v2_frame_write_bytes (&frame, (const guint8 *) lang, 3); + /* write description and value, each including NULL terminator */ + id3v2_frame_write_bytes (&frame, (const guint8 *) desc, desclen + 1); + id3v2_frame_write_bytes (&frame, (const guint8 *) val, vallen + 1); + + g_free (lang); + g_free (desc); + g_free (val); + + g_array_append_val (id3v2tag->frames, frame); + } + g_free (s); + } +} + +static void +add_image_tag (GstId3v2Tag * id3v2tag, const GstTagList * list, + const gchar * tag, guint num_tags, const gchar * unused) +{ + guint n; + + for (n = 0; n < num_tags; ++n) { + const GValue *val; + GstBuffer *image; + + GST_DEBUG ("image %u/%u", n + 1, num_tags); + + val = gst_tag_list_get_value_index (list, tag, n); + image = (GstBuffer *) gst_value_get_mini_object (val); + + if (GST_IS_BUFFER (image) && GST_BUFFER_SIZE (image) > 0 && + GST_BUFFER_CAPS (image) != NULL && + !gst_caps_is_empty (GST_BUFFER_CAPS (image))) { + const gchar *mime_type; + GstStructure *s; + + s = gst_caps_get_structure (GST_BUFFER_CAPS (image), 0); + mime_type = gst_structure_get_name (s); + if (mime_type != NULL) { + const gchar *desc; + GstId3v2Frame frame; + + /* APIC frame specifies "-->" if we're providing a URL to the image + rather than directly embedding it */ + if (strcmp (mime_type, "text/uri-list") == 0) + mime_type = "-->"; + + GST_DEBUG ("Attaching picture of %u bytes and mime type %s", + GST_BUFFER_SIZE (image), mime_type); + + id3v2_frame_init (&frame, "APIC", 0); + id3v2_frame_write_uint8 (&frame, ID3V2_ENCODING_UTF8); + id3v2_frame_write_bytes (&frame, (const guint8 *) mime_type, + strlen (mime_type) + 1); + + /* FIXME set image type properly from caps */ + if (strcmp (tag, GST_TAG_PREVIEW_IMAGE) == 0) + id3v2_frame_write_uint8 (&frame, ID3V2_APIC_PICTURE_FILE_ICON); + else + id3v2_frame_write_uint8 (&frame, ID3V2_APIC_PICTURE_OTHER); + + desc = gst_structure_get_string (s, "image-description"); + if (!desc) + desc = ""; + id3v2_frame_write_bytes (&frame, (const guint8 *) desc, + strlen (desc) + 1); + + g_array_append_val (id3v2tag->frames, frame); + } + } else { + GST_WARNING ("NULL image or no caps on image buffer (%p, caps=%" + GST_PTR_FORMAT ")", image, (image) ? GST_BUFFER_CAPS (image) : NULL); + } + } +} + +static void +add_musicbrainz_tag (GstId3v2Tag * id3v2tag, const GstTagList * list, + const gchar * tag, guint num_tags, const gchar * data) +{ + static const struct + { + const gchar gst_tag[28]; + const gchar spec_id[28]; + const gchar realworld_id[28]; + } mb_ids[] = { + { + GST_TAG_MUSICBRAINZ_ARTISTID, "MusicBrainz Artist Id", + "musicbrainz_artistid"}, { + GST_TAG_MUSICBRAINZ_ALBUMID, "MusicBrainz Album Id", "musicbrainz_albumid"}, { + GST_TAG_MUSICBRAINZ_ALBUMARTISTID, "MusicBrainz Album Artist Id", + "musicbrainz_albumartistid"}, { + GST_TAG_MUSICBRAINZ_TRMID, "MusicBrainz TRM Id", "musicbrainz_trmid"}, { + GST_TAG_CDDA_MUSICBRAINZ_DISCID, "MusicBrainz DiscID", + "musicbrainz_discid"}, { + /* the following one is more or less made up, there seems to be little + * evidence that any popular application is actually putting this info + * into TXXX frames; the first one comes from a musicbrainz wiki 'proposed + * tags' page, the second one is analogue to the vorbis/ape/flac tag. */ + GST_TAG_CDDA_CDDB_DISCID, "CDDB DiscID", "discid"} + }; + guint i, idx; + + idx = (guint8) data[0]; + g_assert (idx < G_N_ELEMENTS (mb_ids)); + + for (i = 0; i < num_tags; ++i) { + gchar *id_str; + + if (gst_tag_list_get_string_index (list, tag, 0, &id_str) && id_str) { + /* add two frames, one with the ID the musicbrainz.org spec mentions + * and one with the ID that applications use in the real world */ + GstId3v2Frame frame1, frame2; + + GST_DEBUG ("Setting '%s' to '%s'", mb_ids[idx].spec_id, id_str); + + id3v2_frame_init (&frame1, "TXXX", 0); + id3v2_frame_write_uint8 (&frame1, ID3V2_ENCODING_UTF8); + id3v2_frame_write_bytes (&frame1, (const guint8 *) mb_ids[idx].spec_id, + strlen (mb_ids[idx].spec_id) + 1); + id3v2_frame_write_bytes (&frame1, (const guint8 *) id_str, + strlen (id_str) + 1); + g_array_append_val (id3v2tag->frames, frame1); + + id3v2_frame_init (&frame2, "TXXX", 0); + id3v2_frame_write_uint8 (&frame2, ID3V2_ENCODING_UTF8); + id3v2_frame_write_bytes (&frame2, + (const guint8 *) mb_ids[idx].realworld_id, + strlen (mb_ids[idx].realworld_id) + 1); + id3v2_frame_write_bytes (&frame2, (const guint8 *) id_str, + strlen (id_str) + 1); + g_array_append_val (id3v2tag->frames, frame2); + + g_free (id_str); + } + } +} + +static void +add_unique_file_id_tag (GstId3v2Tag * id3v2tag, const GstTagList * list, + const gchar * tag, guint num_tags, const gchar * unused) +{ + const gchar *origin = "http://musicbrainz.org"; + gchar *id_str = NULL; + + if (gst_tag_list_get_string_index (list, tag, 0, &id_str) && id_str) { + GstId3v2Frame frame; + + GST_LOG ("Adding %s (%s): %s", tag, origin, id_str); + + id3v2_frame_init (&frame, "UFID", 0); + id3v2_frame_write_bytes (&frame, (const guint8 *) origin, + strlen (origin) + 1); + id3v2_frame_write_bytes (&frame, (const guint8 *) id_str, + strlen (id_str) + 1); + g_array_append_val (id3v2tag->frames, frame); + + g_free (id_str); + } +} + +static void +add_date_tag (GstId3v2Tag * id3v2tag, const GstTagList * list, + const gchar * tag, guint num_tags, const gchar * unused) +{ + guint n; + guint i = 0; + const gchar *frame_id; + gchar **strings; + + if (id3v2tag->major_version == 3) + frame_id = "TYER"; + else + frame_id = "TDRC"; + + GST_LOG ("Adding date frame"); + + strings = g_new0 (gchar *, num_tags + 1); + for (n = 0; n < num_tags; ++n) { + GDate *date = NULL; + + if (gst_tag_list_get_date_index (list, tag, n, &date) && date != NULL) { + GDateYear year; + gchar *s; + + year = g_date_get_year (date); + if (year > 500 && year < 2100) { + s = g_strdup_printf ("%u", year); + GST_LOG ("%s[%u] = '%s'", tag, n, s); + strings[i] = s; + i++; + } else { + GST_WARNING ("invalid year %u, skipping", year); + } + + g_date_free (date); + } + } + + if (strings[0] != NULL) { + id3v2_tag_add_text_frame (id3v2tag, frame_id, strings, i); + } else { + GST_WARNING ("Empty list for tag %s, skipping", tag); + } + + g_strfreev (strings); +} + +static void +add_encoder_tag (GstId3v2Tag * id3v2tag, const GstTagList * list, + const gchar * tag, guint num_tags, const gchar * unused) +{ + guint n; + gchar **strings; + int i = 0; + + /* ENCODER_VERSION is either handled with the ENCODER tag or not at all */ + if (strcmp (tag, GST_TAG_ENCODER_VERSION) == 0) + return; + + strings = g_new0 (gchar *, num_tags + 1); + for (n = 0; n < num_tags; ++n) { + gchar *encoder = NULL; + + if (gst_tag_list_get_string_index (list, tag, n, &encoder) && encoder) { + guint encoder_version; + gchar *s; + + if (gst_tag_list_get_uint_index (list, GST_TAG_ENCODER_VERSION, n, + &encoder_version) && encoder_version > 0) { + s = g_strdup_printf ("%s %u", encoder, encoder_version); + } else { + s = g_strdup (encoder); + } + + GST_LOG ("encoder[%u] = '%s'", n, s); + strings[i] = s; + i++; + g_free (encoder); + } + } + + if (strings[0] != NULL) { + id3v2_tag_add_text_frame (id3v2tag, "TSSE", strings, i); + } else { + GST_WARNING ("Empty list for tag %s, skipping", tag); + } + + g_strfreev (strings); +} + +static void +add_uri_tag (GstId3v2Tag * id3v2tag, const GstTagList * list, + const gchar * tag, guint num_tags, const gchar * frame_id) +{ + gchar *url = NULL; + + g_assert (frame_id != NULL); + + /* URI tags are limited to one of each per taglist */ + if (gst_tag_list_get_string_index (list, tag, 0, &url) && url != NULL) { + guint url_len; + + url_len = strlen (url); + if (url_len > 0 && gst_uri_is_valid (url)) { + GstId3v2Frame frame; + + id3v2_frame_init (&frame, frame_id, 0); + id3v2_frame_write_bytes (&frame, (const guint8 *) url, strlen (url) + 1); + g_array_append_val (id3v2tag->frames, frame); + } else { + GST_WARNING ("Tag %s does not contain a valid URI (%s)", tag, url); + } + + g_free (url); + } +} + +static void +add_relative_volume_tag (GstId3v2Tag * id3v2tag, const GstTagList * list, + const gchar * tag, guint num_tags, const gchar * unused) +{ + const char *gain_tag_name; + const char *peak_tag_name; + gdouble peak_val; + gdouble gain_val; + const char *identification; + guint16 peak_int; + gint16 gain_int; + guint8 peak_bits; + GstId3v2Frame frame; + gchar *frame_id; + + /* figure out tag names and the identification string to use */ + if (strcmp (tag, GST_TAG_TRACK_PEAK) == 0 || + strcmp (tag, GST_TAG_TRACK_GAIN) == 0) { + gain_tag_name = GST_TAG_TRACK_GAIN; + peak_tag_name = GST_TAG_TRACK_PEAK; + identification = "track"; + GST_DEBUG ("adding track relative-volume frame"); + } else { + gain_tag_name = GST_TAG_ALBUM_GAIN; + peak_tag_name = GST_TAG_ALBUM_PEAK; + identification = "album"; + + if (id3v2tag->major_version == 3) { + GST_WARNING ("Cannot store replaygain album gain data in ID3v2.3"); + return; + } + GST_DEBUG ("adding album relative-volume frame"); + } + + /* find the value for the paired tag (gain, if this is peak, and + * vice versa). if both tags exist, only write the frame when + * we're processing the peak tag. + */ + if (strcmp (tag, GST_TAG_TRACK_PEAK) == 0 || + strcmp (tag, GST_TAG_ALBUM_PEAK) == 0) { + + gst_tag_list_get_double (list, tag, &peak_val); + + if (gst_tag_list_get_tag_size (list, gain_tag_name) > 0) { + gst_tag_list_get_double (list, gain_tag_name, &gain_val); + GST_DEBUG ("setting volume adjustment %g", gain_val); + gain_int = (gint16) (gain_val * 512.0); + } else + gain_int = 0; + + /* copying mutagen: always write as 16 bits for sanity. */ + peak_int = (short) (peak_val * G_MAXSHORT); + peak_bits = 16; + } else { + gst_tag_list_get_double (list, tag, &gain_val); + GST_DEBUG ("setting volume adjustment %g", gain_val); + + gain_int = (gint16) (gain_val * 512.0); + peak_bits = 0; + peak_int = 0; + + if (gst_tag_list_get_tag_size (list, peak_tag_name) != 0) { + GST_DEBUG + ("both gain and peak tags exist, not adding frame this time around"); + return; + } + } + + if (id3v2tag->major_version == 4) { + /* 2.4: Use RVA2 tag */ + frame_id = "RVA2"; + } else { + /* 2.3: Use XRVA tag - this is experimental, but useful in the real world. + This version only officially supports the 'RVAD' tag, but that appears + to not be widely implemented in reality. */ + frame_id = "XRVA"; + } + + id3v2_frame_init (&frame, frame_id, 0); + id3v2_frame_write_bytes (&frame, (const guint8 *) identification, + strlen (identification) + 1); + id3v2_frame_write_uint8 (&frame, 0x01); /* Master volume */ + id3v2_frame_write_uint16 (&frame, gain_int); + id3v2_frame_write_uint8 (&frame, peak_bits); + if (peak_bits) + id3v2_frame_write_uint16 (&frame, peak_int); + + g_array_append_val (id3v2tag->frames, frame); +} + +/* id3demux produces these for frames it cannot parse */ +#define GST_ID3_DEMUX_TAG_ID3V2_FRAME "private-id3v2-frame" + +static const struct +{ + const gchar *gst_tag; + const GstId3v2AddTagFunc func; + const gchar *data; +} add_funcs[] = { + { + /* Simple text tags */ + GST_TAG_ARTIST, add_text_tag, "TPE1"}, { + GST_TAG_TITLE, add_text_tag, "TIT2"}, { + GST_TAG_ALBUM, add_text_tag, "TALB"}, { + GST_TAG_COPYRIGHT, add_text_tag, "TCOP"}, { + GST_TAG_COMPOSER, add_text_tag, "TCOM"}, { + GST_TAG_GENRE, add_text_tag, "TCON"}, { + + /* Private frames */ + GST_ID3_DEMUX_TAG_ID3V2_FRAME, add_id3v2frame_tag, NULL}, { + + /* Track and album numbers */ + GST_TAG_TRACK_NUMBER, add_count_or_num_tag, "TRCK"}, { + GST_TAG_TRACK_COUNT, add_count_or_num_tag, "TRCK"}, { + GST_TAG_ALBUM_VOLUME_NUMBER, add_count_or_num_tag, "TPOS"}, { + GST_TAG_ALBUM_VOLUME_COUNT, add_count_or_num_tag, "TPOS"}, { + + /* Comment tags */ + GST_TAG_COMMENT, add_comment_tag, NULL}, { + GST_TAG_EXTENDED_COMMENT, add_comment_tag, NULL}, { + + /* Images */ + GST_TAG_IMAGE, add_image_tag, NULL}, { + GST_TAG_PREVIEW_IMAGE, add_image_tag, NULL}, { + + /* Misc user-defined text tags for IDs (and UFID frame) */ + GST_TAG_MUSICBRAINZ_ARTISTID, add_musicbrainz_tag, "\000"}, { + GST_TAG_MUSICBRAINZ_ALBUMID, add_musicbrainz_tag, "\001"}, { + GST_TAG_MUSICBRAINZ_ALBUMARTISTID, add_musicbrainz_tag, "\002"}, { + GST_TAG_MUSICBRAINZ_TRMID, add_musicbrainz_tag, "\003"}, { + GST_TAG_CDDA_MUSICBRAINZ_DISCID, add_musicbrainz_tag, "\004"}, { + GST_TAG_CDDA_CDDB_DISCID, add_musicbrainz_tag, "\005"}, { + GST_TAG_MUSICBRAINZ_TRACKID, add_unique_file_id_tag, NULL}, { + + /* Info about encoder */ + GST_TAG_ENCODER, add_encoder_tag, NULL}, { + GST_TAG_ENCODER_VERSION, add_encoder_tag, NULL}, { + + /* URIs */ + GST_TAG_COPYRIGHT_URI, add_uri_tag, "WCOP"}, { + GST_TAG_LICENSE_URI, add_uri_tag, "WCOP"}, { + + /* Up to here, all the frame ids and contents have been the same between + versions 2.3 and 2.4. The rest of them differ... */ + /* Date (in ID3v2.3, this is a TYER tag. In v2.4, it's a TDRC tag */ + GST_TAG_DATE, add_date_tag, NULL}, { + + /* Replaygain data (not really supported in 2.3, we use an experimental + tag there) */ + GST_TAG_TRACK_PEAK, add_relative_volume_tag, NULL}, { + GST_TAG_TRACK_GAIN, add_relative_volume_tag, NULL}, { + GST_TAG_ALBUM_PEAK, add_relative_volume_tag, NULL}, { + GST_TAG_ALBUM_GAIN, add_relative_volume_tag, NULL}, { + + /* Sortable version of various tags. These are all v2.4 ONLY */ + GST_TAG_ARTIST_SORTNAME, add_text_tag_v4, "TSOP"}, { + GST_TAG_ALBUM_SORTNAME, add_text_tag_v4, "TSOA"}, { + GST_TAG_TITLE_SORTNAME, add_text_tag_v4, "TSOT"} +}; + +static void +foreach_add_tag (const GstTagList * list, const gchar * tag, gpointer userdata) +{ + GstId3v2Tag *id3v2tag = (GstId3v2Tag *) userdata; + guint num_tags, i; + + num_tags = gst_tag_list_get_tag_size (list, tag); + + GST_LOG ("Processing tag %s (num=%u)", tag, num_tags); + + if (num_tags > 1 && gst_tag_is_fixed (tag)) { + GST_WARNING ("Multiple occurences of fixed tag '%s', ignoring some", tag); + num_tags = 1; + } + + for (i = 0; i < G_N_ELEMENTS (add_funcs); ++i) { + if (strcmp (add_funcs[i].gst_tag, tag) == 0) { + add_funcs[i].func (id3v2tag, list, tag, num_tags, add_funcs[i].data); + break; + } + } + + if (i == G_N_ELEMENTS (add_funcs)) { + GST_WARNING ("Unsupported tag '%s' - not written", tag); + } +} + +GstBuffer * +gst_id3mux_render_v2_tag (GstTagMux * mux, GstTagList * taglist, int version) +{ + GstId3v2Tag tag; + GstBuffer *buf; + + if (!id3v2_tag_init (&tag, version)) { + GST_WARNING_OBJECT (mux, "Unsupported version %d", version); + return NULL; + } + + /* Render the tag */ + gst_tag_list_foreach (taglist, foreach_add_tag, &tag); + +#if 0 + /* Do we want to add our own signature to the tag somewhere? */ + { + gchar *tag_producer_str; + + tag_producer_str = g_strdup_printf ("(GStreamer id3v2mux %s, using " + "taglib %u.%u)", VERSION, TAGLIB_MAJOR_VERSION, TAGLIB_MINOR_VERSION); + add_one_txxx_tag (id3v2tag, "tag_encoder", tag_producer_str); + g_free (tag_producer_str); + } +#endif + + /* Create buffer with tag */ + buf = id3v2_tag_to_buffer (&tag); + gst_buffer_set_caps (buf, GST_PAD_CAPS (mux->srcpad)); + GST_LOG_OBJECT (mux, "tag size = %d bytes", GST_BUFFER_SIZE (buf)); + + id3v2_tag_unset (&tag); + + return buf; +} + +#define ID3_V1_TAG_SIZE 128 + +typedef void (*GstId3v1WriteFunc) (const GstTagList * list, + const gchar * gst_tag, guint8 * dst, int len); + +static void +latin1_convert (const GstTagList * list, const gchar * tag, + guint8 * dst, int maxlen) +{ + gchar *str; + gsize len; + gchar *latin1; + + if (!gst_tag_list_get_string (list, tag, &str)) + return; + + /* Convert to Latin-1 (ISO-8859-1), replacing unrepresentable characters + with '?' */ + latin1 = g_convert_with_fallback (str, -1, "ISO-8859-1", "UTF-8", "?", + NULL, &len, NULL); + + if (latin1) { + len = MIN (len, maxlen); + memcpy (dst, latin1, len); + g_free (latin1); + } + + g_free (str); +} + +static void +date_v1_convert (const GstTagList * list, const gchar * tag, + guint8 * dst, int maxlen) +{ + GDate *date; + + /* Only one date supported */ + if (gst_tag_list_get_date_index (list, tag, 0, &date) && date != NULL) { + GDateYear year = g_date_get_year (date); + /* Check for plausible year */ + if (year > 500 && year < 2100) { + gchar str[5]; + g_snprintf (str, 5, "%.4u", year); + memcpy (dst, str, 4); + } else { + GST_WARNING ("invalid year %u, skipping", year); + } + + g_date_free (date); + } +} + +static void +genre_v1_convert (const GstTagList * list, const gchar * tag, + guint8 * dst, int maxlen) +{ + gchar *str; + int genreidx = -1; + guint i, max; + + /* We only support one genre */ + if (!gst_tag_list_get_string_index (list, tag, 0, &str)) + return; + + max = gst_tag_id3_genre_count (); + + for (i = 0; i < max; i++) { + const gchar *genre = gst_tag_id3_genre_get (i); + if (g_str_equal (str, genre)) { + genreidx = i; + break; + } + } + + if (genreidx >= 0 && genreidx <= 127) + *dst = (guint8) genreidx; + + g_free (str); +} + +static void +track_number_convert (const GstTagList * list, const gchar * tag, + guint8 * dst, int maxlen) +{ + guint tracknum; + + /* We only support one track number */ + if (!gst_tag_list_get_uint_index (list, tag, 0, &tracknum)) + return; + + if (tracknum <= 127) + *dst = (guint8) tracknum; +} + +static const struct +{ + const gchar *gst_tag; + const gint offset; + const gint length; + const GstId3v1WriteFunc func; +} v1_funcs[] = { + { + GST_TAG_TITLE, 3, 30, latin1_convert}, { + GST_TAG_ARTIST, 33, 30, latin1_convert}, { + GST_TAG_ALBUM, 63, 30, latin1_convert}, { + GST_TAG_DATE, 93, 4, date_v1_convert}, { + GST_TAG_COMMENT, 97, 28, latin1_convert}, { + /* Note: one-byte gap here */ + GST_TAG_TRACK_NUMBER, 126, 1, track_number_convert}, { + GST_TAG_GENRE, 127, 1, genre_v1_convert} +}; + +GstBuffer * +gst_id3mux_render_v1_tag (GstTagMux * mux, GstTagList * taglist) +{ + GstBuffer *buf = gst_buffer_new_and_alloc (ID3_V1_TAG_SIZE); + guint8 *data = GST_BUFFER_DATA (buf); + int i; + + memset (data, 0, ID3_V1_TAG_SIZE); + + data[0] = 'T'; + data[1] = 'A'; + data[2] = 'G'; + + for (i = 0; i < G_N_ELEMENTS (v1_funcs); i++) { + v1_funcs[i].func (taglist, v1_funcs[i].gst_tag, data + v1_funcs[i].offset, + v1_funcs[i].length); + } + + gst_buffer_set_caps (buf, GST_PAD_CAPS (mux->srcpad)); + return buf; +} diff --git a/gst/flv/gstflvparse.h b/gst/id3tag/id3tag.h index 203d30de..1fb59376 100644 --- a/gst/flv/gstflvparse.h +++ b/gst/id3tag/id3tag.h @@ -1,5 +1,5 @@ -/* GStreamer - * Copyright (C) <2007> Julien Moutte <julien@moutte.net> +/* GStreamer ID3v2 tag writer + * Copyright (C) 2009 Tim-Philipp Müller <tim centricular net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -17,26 +17,16 @@ * Boston, MA 02111-1307, USA. */ -#ifndef __FLV_PARSE_H__ -#define __FLV_PARSE_H__ - -#include "gstflvdemux.h" +#include "gsttagmux.h" G_BEGIN_DECLS +#define ID3_VERSION_2_3 3 +#define ID3_VERSION_2_4 4 -GstFlowReturn gst_flv_parse_tag_script (GstFLVDemux * demux, - GstBuffer *buffer); - -GstFlowReturn gst_flv_parse_tag_audio (GstFLVDemux * demux, GstBuffer *buffer); - -GstFlowReturn gst_flv_parse_tag_video (GstFLVDemux * demux, GstBuffer *buffer); - -GstFlowReturn gst_flv_parse_tag_type (GstFLVDemux * demux, GstBuffer *buffer); - -GstFlowReturn gst_flv_parse_header (GstFLVDemux * demux, GstBuffer *buffer); - -GstClockTime gst_flv_parse_tag_timestamp (GstFLVDemux *demux, GstBuffer *buffer, size_t *tag_data_size); +GstBuffer * gst_id3mux_render_v2_tag (GstTagMux * mux, GstTagList * taglist, + int version); +GstBuffer * gst_id3mux_render_v1_tag (GstTagMux * mux, GstTagList * taglist); G_END_DECLS -#endif /* __FLV_PARSE_H__ */ + diff --git a/gst/mpeg4videoparse/mpeg4videoparse.c b/gst/mpeg4videoparse/mpeg4videoparse.c index 1efa28be..e0e28d0c 100644 --- a/gst/mpeg4videoparse/mpeg4videoparse.c +++ b/gst/mpeg4videoparse/mpeg4videoparse.c @@ -726,6 +726,10 @@ gst_mpeg4vparse_cleanup (GstMpeg4VParse * parse) if (parse->adapter) { gst_adapter_clear (parse->adapter); } + if (parse->config != NULL) { + gst_buffer_unref (parse->config); + parse->config = NULL; + } parse->state = PARSE_NEED_START; parse->have_config = FALSE; @@ -760,6 +764,10 @@ gst_mpeg4vparse_dispose (GObject * object) g_object_unref (parse->adapter); parse->adapter = NULL; } + if (parse->config != NULL) { + gst_buffer_unref (parse->config); + parse->config = NULL; + } GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object)); } diff --git a/gst/mpegdemux/gstmpegdefs.h b/gst/mpegdemux/gstmpegdefs.h index 7f38f4de..7ad1e25c 100644 --- a/gst/mpegdemux/gstmpegdefs.h +++ b/gst/mpegdemux/gstmpegdefs.h @@ -170,7 +170,11 @@ #define ST_PS_AUDIO_AC3 0x81 #define ST_PS_AUDIO_DTS 0x8a #define ST_PS_AUDIO_LPCM 0x8b +#define ST_HDV_PRIVATE_A0 0xa0 +#define ST_HDV_PRIVATE_A1 0xa1 #define ST_PS_DVD_SUBPICTURE 0xff +/* Blu-ray PGS subpictures */ +#define ST_BD_PGS_SUBPICTURE 0x90 /* Un-official time-code stream */ #define ST_PS_TIMECODE 0xd2 diff --git a/gst/mpegdemux/gstmpegdemux.c b/gst/mpegdemux/gstmpegdemux.c index 439bb563..ce1d0978 100644 --- a/gst/mpegdemux/gstmpegdemux.c +++ b/gst/mpegdemux/gstmpegdemux.c @@ -39,6 +39,7 @@ * Fluendo, S.L. All Rights Reserved. * * Contributor(s): Wim Taymans <wim@fluendo.com> + * Jan Schmidt <thaytan@noraisin.net> */ #ifdef HAVE_CONFIG_H @@ -56,6 +57,9 @@ #define SCAN_SCR_SZ 12 #define SCAN_PTS_SZ 80 +#define SEGMENT_THRESHOLD (300*GST_MSECOND) +#define VIDEO_SEGMENT_THRESHOLD (500*GST_MSECOND) + typedef enum { SCAN_SCR, @@ -177,6 +181,13 @@ static GstStaticPadTemplate audio_template = "audio/x-private1-ac3;" "audio/x-private1-dts;" "audio/ac3") ); +static GstStaticPadTemplate subpicture_template = +GST_STATIC_PAD_TEMPLATE ("subpicture_%02x", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + GST_STATIC_CAPS ("video/x-dvd-subpicture") + ); + static GstStaticPadTemplate private_template = GST_STATIC_PAD_TEMPLATE ("private_%d", GST_PAD_SRC, @@ -209,6 +220,10 @@ static inline gboolean gst_flups_demux_scan_forward_ts (GstFluPSDemux * demux, static inline gboolean gst_flups_demux_scan_backward_ts (GstFluPSDemux * demux, guint64 * pos, SCAN_MODE mode, guint64 * rts); +static void gst_flups_demux_send_segment_updates (GstFluPSDemux * demux, + GstClockTime new_time); +static void gst_flups_demux_clear_times (GstFluPSDemux * demux); + static GstElementClass *parent_class = NULL; /*static guint gst_flups_demux_signals[LAST_SIGNAL] = { 0 };*/ @@ -250,10 +265,14 @@ gst_flups_demux_base_init (GstFluPSDemuxClass * klass) klass->sink_template = gst_static_pad_template_get (&sink_template); klass->video_template = gst_static_pad_template_get (&video_template); klass->audio_template = gst_static_pad_template_get (&audio_template); + klass->subpicture_template = + gst_static_pad_template_get (&subpicture_template); klass->private_template = gst_static_pad_template_get (&private_template); gst_element_class_add_pad_template (element_class, klass->video_template); gst_element_class_add_pad_template (element_class, klass->audio_template); + gst_element_class_add_pad_template (element_class, + klass->subpicture_template); gst_element_class_add_pad_template (element_class, klass->private_template); gst_element_class_add_pad_template (element_class, klass->sink_template); @@ -340,6 +359,7 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type) gchar *name; GstFluPSDemuxClass *klass = GST_FLUPS_DEMUX_GET_CLASS (demux); GstCaps *caps; + GstClockTime threshold = SEGMENT_THRESHOLD; name = NULL; template = NULL; @@ -368,6 +388,7 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type) caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, mpeg_version, "systemstream", G_TYPE_BOOLEAN, FALSE, NULL); + threshold = VIDEO_SEGMENT_THRESHOLD; break; } case ST_AUDIO_MPEG1: @@ -387,6 +408,7 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type) template = klass->video_template; name = g_strdup_printf ("video_%02x", id); caps = gst_caps_new_simple ("video/x-h264", NULL); + threshold = VIDEO_SEGMENT_THRESHOLD; break; case ST_PS_AUDIO_AC3: template = klass->audio_template; @@ -404,6 +426,9 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type) caps = gst_caps_new_simple ("audio/x-private1-lpcm", NULL); break; case ST_PS_DVD_SUBPICTURE: + template = klass->subpicture_template; + name = g_strdup_printf ("subpicture_%02x", id); + caps = gst_caps_new_simple ("video/x-dvd-subpicture", NULL); break; case ST_GST_AUDIO_RAWA52: template = klass->audio_template; @@ -424,6 +449,7 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type) stream->notlinked = FALSE; stream->type = stream_type; stream->pad = gst_pad_new_from_template (template, name); + stream->segment_thresh = threshold; gst_pad_set_event_function (stream->pad, GST_DEBUG_FUNCPTR (gst_flups_demux_src_event)); gst_pad_set_query_function (stream->pad, @@ -559,6 +585,20 @@ gst_flups_demux_send_data (GstFluPSDemux * demux, GstFluPSStream * stream, GST_TIME_ARGS (demux->src_segment.last_stop), GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (demux->current_scr))); + if (demux->src_segment.last_stop != GST_CLOCK_TIME_NONE) { + GstClockTime new_time = demux->base_time + demux->src_segment.last_stop; + + if (stream->last_ts == GST_CLOCK_TIME_NONE || stream->last_ts < new_time) { +#if 0 + g_print ("last_ts update on pad %s to time %" GST_TIME_FORMAT "\n", + GST_PAD_NAME (stream->pad), GST_TIME_ARGS (cur_scr_time)); +#endif + stream->last_ts = new_time; + } + + gst_flups_demux_send_segment_updates (demux, new_time); + } + /* Set the buffer discont flag, and clear discont state on the stream */ if (stream->discont) { GST_DEBUG_OBJECT (demux, "marking discont buffer"); @@ -731,12 +771,76 @@ gst_flups_demux_flush (GstFluPSDemux * demux) gst_adapter_clear (demux->adapter); gst_adapter_clear (demux->rev_adapter); gst_pes_filter_drain (&demux->filter); + gst_flups_demux_clear_times (demux); demux->adapter_offset = G_MAXUINT64; demux->current_scr = G_MAXUINT64; demux->bytes_since_scr = 0; } static void +gst_flups_demux_clear_times (GstFluPSDemux * demux) +{ + gint id; + + /* Clear the last ts for all streams */ + for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) { + GstFluPSStream *stream = demux->streams[id]; + + if (stream) { + stream->last_seg_start = stream->last_ts = GST_CLOCK_TIME_NONE; + } + } +} + +static void +gst_flups_demux_send_segment_updates (GstFluPSDemux * demux, + GstClockTime new_time) +{ + /* Advance all lagging streams by sending a segment update */ + gint id; + GstEvent *event = NULL; + + /* FIXME: Handle reverse playback */ + + if (new_time > demux->src_segment.stop) + return; + + for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) { + GstFluPSStream *stream = demux->streams[id]; + + if (stream) { + if (stream->last_ts == GST_CLOCK_TIME_NONE || + stream->last_ts < demux->src_segment.start) + stream->last_ts = demux->src_segment.start; + if (stream->last_ts + stream->segment_thresh < new_time) { +#if 0 + g_print ("Segment update to pad %s time %" GST_TIME_FORMAT " stop now %" + GST_TIME_FORMAT "\n", GST_PAD_NAME (stream->pad), + GST_TIME_ARGS (new_time), GST_TIME_ARGS (demux->src_segment.stop)); +#endif + GST_DEBUG_OBJECT (demux, + "Segment update to pad %s time %" GST_TIME_FORMAT, + GST_PAD_NAME (stream->pad), GST_TIME_ARGS (new_time)); + if (event == NULL) { + event = gst_event_new_new_segment_full (TRUE, + demux->src_segment.rate, demux->src_segment.applied_rate, + GST_FORMAT_TIME, new_time, + demux->src_segment.stop, + demux->src_segment.time + (new_time - demux->src_segment.start)); + } + gst_event_ref (event); + gst_pad_push_event (stream->pad, event); + stream->last_seg_start = stream->last_ts = new_time; + stream->need_segment = FALSE; + } + } + } + + if (event) + gst_event_unref (event); +} + +static void gst_flups_demux_close_segment (GstFluPSDemux * demux) { gint id; @@ -747,6 +851,10 @@ gst_flups_demux_close_segment (GstFluPSDemux * demux) &demux->src_segment); #endif + /* FIXME: Need to send a different segment-close to each pad where the + * last_seg_start != clock_time_none, as that indicates a sparse-stream + * event was sent there */ + /* Close the current segment for a linear playback */ if (demux->src_segment.rate >= 0) { /* for forward playback, we played from start to last_stop */ @@ -1306,7 +1414,7 @@ gst_flups_demux_reset_psm (GstFluPSDemux * demux) FILL_TYPE (0x40, 0x7f, -1); FILL_TYPE (0x80, 0x87, ST_PS_AUDIO_AC3); FILL_TYPE (0x88, 0x9f, ST_PS_AUDIO_DTS); - FILL_TYPE (0xa0, 0xbf, ST_PS_AUDIO_LPCM); + FILL_TYPE (0xa0, 0xaf, ST_PS_AUDIO_LPCM); FILL_TYPE (0xbd, 0xbd, -1); FILL_TYPE (0xc0, 0xdf, ST_AUDIO_MPEG1); FILL_TYPE (0xe0, 0xef, ST_GST_VIDEO_MPEG1_OR_2); @@ -1839,18 +1947,26 @@ gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first, } if (G_LIKELY (stream_type == -1)) { - /* new id */ + /* new id is in the first byte */ id = data[offset++]; - /* Number of audio frames in this packet */ - nframes = data[offset++]; - - GST_DEBUG_OBJECT (demux, "private type 0x%02x, %d frames", id, - nframes); - - datalen -= 2; + datalen--; /* and remap */ stream_type = demux->psm[id]; + + /* Now, if it's a subpicture stream - no more, otherwise + * take the first byte too, since it's the frame count in audio + * streams and our backwards compat convention is to strip it off */ + if (stream_type != ST_PS_DVD_SUBPICTURE) { + /* Number of audio frames in this packet */ + nframes = data[offset++]; + datalen--; + GST_DEBUG_OBJECT (demux, "private type 0x%02x, %d frames", id, + nframes); + } else { + GST_DEBUG_OBJECT (demux, "private type 0x%02x, stream type %d", id, + stream_type); + } } } if (stream_type == -1) diff --git a/gst/mpegdemux/gstmpegdemux.h b/gst/mpegdemux/gstmpegdemux.h index ef175f76..29b3d5e5 100644 --- a/gst/mpegdemux/gstmpegdemux.h +++ b/gst/mpegdemux/gstmpegdemux.h @@ -86,6 +86,10 @@ struct _GstFluPSStream gint type; gint size_bound; + GstClockTime segment_thresh; + GstClockTime last_seg_start; + GstClockTime last_ts; + gboolean discont; gboolean notlinked; gboolean need_segment; @@ -149,6 +153,7 @@ struct _GstFluPSDemuxClass GstPadTemplate *sink_template; GstPadTemplate *video_template; GstPadTemplate *audio_template; + GstPadTemplate *subpicture_template; GstPadTemplate *private_template; }; diff --git a/gst/mpegdemux/gstmpegtsdemux.c b/gst/mpegdemux/gstmpegtsdemux.c index 4464cdf8..ef0de2c8 100644 --- a/gst/mpegdemux/gstmpegtsdemux.c +++ b/gst/mpegdemux/gstmpegtsdemux.c @@ -34,9 +34,9 @@ * * The Original Code is Fluendo MPEG Demuxer plugin. * - * The Initial Developer of the Original Code is Fluendo, S.L. - * Portions created by Fluendo, S.L. are Copyright (C) 2005 - * Fluendo, S.L. All Rights Reserved. + * The Initial Developer of the Original Code is Fluendo, S.A. + * Portions created by Fluendo, S.L. are Copyright (C) 2005,2006,2007,2008,2009 + * Fluendo, S.A. All Rights Reserved. * * Contributor(s): Wim Taymans <wim@fluendo.com> */ @@ -126,6 +126,13 @@ enum PROP_M2TS }; +#define GSTTIME_TO_BYTES(time) \ + ((time != -1) ? gst_util_uint64_scale (MAX(0,(gint64) ((time))), \ + demux->bitrate, GST_SECOND) : -1) +#define BYTES_TO_GSTTIME(bytes) \ + ((bytes != -1) ? (gst_util_uint64_scale (bytes, GST_SECOND, \ + demux->bitrate)) : -1) + #define VIDEO_CAPS \ GST_STATIC_CAPS (\ "video/mpeg, " \ @@ -150,6 +157,10 @@ enum "audio/x-dts" \ ) +/* Can also use the subpicture pads for text subtitles? */ +#define SUBPICTURE_CAPS \ + GST_STATIC_CAPS ("subpicture/x-pgs; video/x-dvd-subpicture") + static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, @@ -168,6 +179,12 @@ GST_STATIC_PAD_TEMPLATE ("audio_%04x", GST_PAD_SOMETIMES, AUDIO_CAPS); +static GstStaticPadTemplate subpicture_template = +GST_STATIC_PAD_TEMPLATE ("subpicture_%04x", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + SUBPICTURE_CAPS); + static GstStaticPadTemplate private_template = GST_STATIC_PAD_TEMPLATE ("private_%04x", GST_PAD_SRC, @@ -189,6 +206,7 @@ static void gst_mpegts_demux_get_property (GObject * object, guint prop_id, static gboolean gst_mpegts_demux_is_PMT (GstMpegTSDemux * demux, guint16 PID); static gboolean gst_mpegts_demux_sink_event (GstPad * pad, GstEvent * event); +static gboolean gst_mpegts_demux_src_event (GstPad * pad, GstEvent * event); static GstFlowReturn gst_mpegts_demux_chain (GstPad * pad, GstBuffer * buffer); static gboolean gst_mpegts_demux_sink_setcaps (GstPad * pad, GstCaps * caps); @@ -242,10 +260,14 @@ gst_mpegts_demux_base_init (GstMpegTSDemuxClass * klass) klass->sink_template = gst_static_pad_template_get (&sink_template); klass->video_template = gst_static_pad_template_get (&video_template); klass->audio_template = gst_static_pad_template_get (&audio_template); + klass->subpicture_template = + gst_static_pad_template_get (&subpicture_template); klass->private_template = gst_static_pad_template_get (&private_template); gst_element_class_add_pad_template (element_class, klass->video_template); gst_element_class_add_pad_template (element_class, klass->audio_template); + gst_element_class_add_pad_template (element_class, + klass->subpicture_template); gst_element_class_add_pad_template (element_class, klass->private_template); gst_element_class_add_pad_template (element_class, klass->sink_template); @@ -327,7 +349,12 @@ gst_mpegts_demux_init (GstMpegTSDemux * demux) demux->m2ts_mode = FALSE; demux->sync_lut = NULL; demux->sync_lut_len = 0; - + demux->bitrate = -1; + demux->num_packets = 0; + demux->pcr[0] = -1; + demux->pcr[1] = -1; + demux->cache_duration = GST_CLOCK_TIME_NONE; + demux->base_pts = GST_CLOCK_TIME_NONE; #ifdef USE_LIBOIL oil_init (); #endif @@ -369,8 +396,8 @@ gst_mpegts_demux_reset (GstMpegTSDemux * demux) case PID_TYPE_ELEMENTARY: gst_pes_filter_uninit (&stream->filter); break; - case PID_PROGRAM_ASSOCIATION_TABLE: - case PID_CONDITIONAL_ACCESS_TABLE: + case PID_TYPE_PROGRAM_ASSOCIATION: + case PID_TYPE_CONDITIONAL_ACCESS: case PID_TYPE_PROGRAM_MAP: gst_section_filter_uninit (&stream->section_filter); break; @@ -607,6 +634,16 @@ gst_mpegts_demux_fill_stream (GstMpegTSStream * stream, guint8 id, caps = gst_caps_new_simple ("private/teletext", NULL); } break; + case ST_HDV_PRIVATE_A0: + template = klass->private_template; + name = g_strdup_printf ("private_%04x", stream->PID); + caps = gst_caps_new_simple ("private/hdv-a0", NULL); + break; + case ST_HDV_PRIVATE_A1: + template = klass->private_template; + name = g_strdup_printf ("private_%04x", stream->PID); + caps = gst_caps_new_simple ("private/hdv-a1", NULL); + break; case ST_PRIVATE_SECTIONS: case ST_MHEG: case ST_DSMCC: @@ -652,6 +689,14 @@ gst_mpegts_demux_fill_stream (GstMpegTSStream * stream, guint8 id, caps = gst_caps_new_simple ("audio/x-lpcm", NULL); break; case ST_PS_DVD_SUBPICTURE: + template = klass->subpicture_template; + name = g_strdup_printf ("subpicture_%04x", stream->PID); + caps = gst_caps_new_simple ("video/x-dvd-subpicture", NULL); + break; + case ST_BD_PGS_SUBPICTURE: + template = klass->subpicture_template; + name = g_strdup_printf ("subpicture_%04x", stream->PID); + caps = gst_caps_new_simple ("subpicture/x-pgs", NULL); break; default: break; @@ -668,6 +713,8 @@ gst_mpegts_demux_fill_stream (GstMpegTSStream * stream, guint8 id, gst_caps_unref (caps); gst_pad_set_query_function (stream->pad, GST_DEBUG_FUNCPTR (gst_mpegts_demux_src_pad_query)); + gst_pad_set_event_function (stream->pad, + GST_DEBUG_FUNCPTR (gst_mpegts_demux_src_event)); g_free (name); return TRUE; @@ -730,7 +777,7 @@ gst_mpegts_demux_send_new_segment (GstMpegTSDemux * demux, } base_PCR = PCR_stream->base_PCR; - time = MPEGTIME_TO_GSTTIME (base_PCR); + demux->base_pts = time = MPEGTIME_TO_GSTTIME (base_PCR); GST_DEBUG_OBJECT (demux, "segment PTS to (%" G_GUINT64_FORMAT ") time: %" G_GUINT64_FORMAT, base_PCR, time); @@ -828,7 +875,7 @@ gst_mpegts_demux_data_cb (GstPESFilter * filter, gboolean first, demux = stream->demux; srcpad = stream->pad; - GST_LOG_OBJECT (demux, "got data on PID 0x%04x", stream->PID); + GST_DEBUG_OBJECT (demux, "got data on PID 0x%04x", stream->PID); if (first && filter->pts != -1) { pts = filter->pts; @@ -932,7 +979,7 @@ gst_mpegts_demux_data_cb (GstPESFilter * filter, gboolean first, pts = -1; } - GST_LOG_OBJECT (demux, "setting PTS to (%" G_GUINT64_FORMAT ") time: %" + GST_DEBUG_OBJECT (demux, "setting PTS to (%" G_GUINT64_FORMAT ") time: %" GST_TIME_FORMAT " on buffer %p first buffer: %d base_time: %" GST_TIME_FORMAT, pts, GST_TIME_ARGS (time), buffer, first, GST_TIME_ARGS (stream->base_time)); @@ -967,8 +1014,8 @@ gst_mpegts_demux_data_cb (GstPESFilter * filter, gboolean first, goto unknown_type; GST_DEBUG_OBJECT (demux, - "New stream 0x%04x of type %d with caps %" GST_PTR_FORMAT, stream->PID, - stream->stream_type, GST_PAD_CAPS (stream->pad)); + "New stream 0x%04x of type 0x%02x with caps %" GST_PTR_FORMAT, + stream->PID, stream->stream_type, GST_PAD_CAPS (stream->pad)); srcpad = stream->pad; @@ -981,7 +1028,7 @@ gst_mpegts_demux_data_cb (GstPESFilter * filter, gboolean first, gst_mpegts_demux_send_new_segment (demux, stream, pts); } - GST_DEBUG_OBJECT (demux, "pushing buffer"); + GST_DEBUG_OBJECT (srcpad, "pushing buffer"); gst_buffer_set_caps (buffer, GST_PAD_CAPS (srcpad)); ret = gst_pad_push (srcpad, buffer); ret = gst_mpegts_demux_combine_flows (demux, stream, ret); @@ -992,7 +1039,7 @@ gst_mpegts_demux_data_cb (GstPESFilter * filter, gboolean first, unknown_type: { GST_DEBUG_OBJECT (demux, "got unknown stream id 0x%02x, type 0x%02x", - filter->id, filter->type); + filter->id, stream->stream_type); gst_buffer_unref (buffer); return gst_mpegts_demux_combine_flows (demux, stream, GST_FLOW_NOT_LINKED); } @@ -1184,16 +1231,16 @@ gst_mpegts_stream_parse_pmt (GstMpegTSStream * stream, if (gst_mpegts_demux_calc_crc32 (data - 3, datalen) != 0) goto wrong_crc; - GST_DEBUG_OBJECT (demux, "PMT section_length: %d", datalen - 3); + GST_LOG_OBJECT (demux, "PMT section_length: %d", datalen - 3); PMT = &stream->PMT; /* check if version number changed */ version_number = (data[2] & 0x3e) >> 1; - GST_DEBUG_OBJECT (demux, "PMT version_number: %d", version_number); + GST_LOG_OBJECT (demux, "PMT version_number: %d", version_number); current_next_indicator = (data[2] & 0x01); - GST_DEBUG_OBJECT (demux, "PMT current_next_indicator %d", + GST_LOG_OBJECT (demux, "PMT current_next_indicator %d", current_next_indicator); if (current_next_indicator == 0) goto not_yet_applicable; @@ -1559,7 +1606,7 @@ gst_mpegts_demux_parse_adaptation_field (GstMpegTSStream * stream, if (length > 0) { guint8 flags = *data++; - GST_DEBUG_OBJECT (demux, "flags 0x%02x", flags); + GST_LOG_OBJECT (demux, "flags 0x%02x", flags); /* discontinuity flag */ if (flags & 0x80) { GST_DEBUG_OBJECT (demux, "discontinuity flag set"); @@ -1578,9 +1625,12 @@ gst_mpegts_demux_parse_adaptation_field (GstMpegTSStream * stream, pcr_ext = (pcr2 & 0x01ff); if (pcr_ext) pcr = (pcr * 300 + pcr_ext % 300) / 300; - GST_DEBUG_OBJECT (demux, "have PCR %" G_GUINT64_FORMAT " on PID 0x%04x " - "and last pcr is %" G_GUINT64_FORMAT, - pcr, stream->PID, stream->last_PCR); + GST_DEBUG_OBJECT (demux, + "have PCR %" G_GUINT64_FORMAT "(%" GST_TIME_FORMAT ") on PID 0x%04x " + "and last pcr is %" G_GUINT64_FORMAT " (%" GST_TIME_FORMAT ")", pcr, + GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (pcr)), stream->PID, + stream->last_PCR, + GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (stream->last_PCR))); /* pcr has been converted into units of 90Khz ticks * so assume discont if last pcr was > 900000 (10 second) lower */ if (stream->last_PCR != -1 && @@ -1676,11 +1726,24 @@ gst_mpegts_demux_parse_adaptation_field (GstMpegTSStream * stream, } stream->last_PCR_difference = pcr - stream->last_PCR; } + GST_DEBUG_OBJECT (demux, "valid pcr: %d last PCR difference: %" G_GUINT64_FORMAT, valid_pcr, stream->last_PCR_difference); if (valid_pcr) { - + GstMpegTSStream *PMT_stream = demux->streams[demux->current_PMT]; + + if (PMT_stream && PMT_stream->PMT.PCR_PID == stream->PID) { + if (demux->pcr[0] == -1) { + GST_DEBUG ("RECORDING pcr[0]:%" G_GUINT64_FORMAT, pcr); + demux->pcr[0] = pcr; + demux->num_packets = 0; + } /* Considering a difference of 1 sec ie 90000 ticks */ + else if (demux->pcr[1] == -1 && ((pcr - demux->pcr[0]) >= 90000)) { + GST_DEBUG ("RECORDING pcr[1]:%" G_GUINT64_FORMAT, pcr); + demux->pcr[1] = pcr; + } + } stream->last_PCR = pcr; if (demux->clock && demux->clock_base != GST_CLOCK_TIME_NONE) { @@ -1955,15 +2018,20 @@ gst_mpegts_demux_is_PMT (GstMpegTSDemux * demux, guint16 PID) } static FORCE_INLINE GstFlowReturn -gst_mpegts_stream_pes_buffer_flush (GstMpegTSStream * stream) +gst_mpegts_stream_pes_buffer_flush (GstMpegTSStream * stream, gboolean discard) { GstFlowReturn ret = GST_FLOW_OK; if (stream->pes_buffer) { - GST_BUFFER_SIZE (stream->pes_buffer) = stream->pes_buffer_used; - ret = gst_pes_filter_push (&stream->filter, stream->pes_buffer); - if (ret == GST_FLOW_LOST_SYNC) + if (discard) { + gst_buffer_unref (stream->pes_buffer); stream->pes_buffer_in_sync = FALSE; + } else { + GST_BUFFER_SIZE (stream->pes_buffer) = stream->pes_buffer_used; + ret = gst_pes_filter_push (&stream->filter, stream->pes_buffer); + if (ret == GST_FLOW_LOST_SYNC) + stream->pes_buffer_in_sync = FALSE; + } stream->pes_buffer = NULL; } return ret; @@ -1985,7 +2053,7 @@ gst_mpegts_stream_pes_buffer_push (GstMpegTSStream * stream, if (stream->pes_buffer_size < (MPEGTS_MAX_PES_BUFFER_SIZE >> 1)) stream->pes_buffer_size <<= 1; - ret = gst_mpegts_stream_pes_buffer_flush (stream); + ret = gst_mpegts_stream_pes_buffer_flush (stream, FALSE); if (ret == GST_FLOW_LOST_SYNC) goto done; } @@ -2010,7 +2078,7 @@ done: } static FORCE_INLINE GstFlowReturn -gst_mpegts_demux_pes_buffer_flush (GstMpegTSDemux * demux) +gst_mpegts_demux_pes_buffer_flush (GstMpegTSDemux * demux, gboolean discard) { gint i; GstFlowReturn ret = GST_FLOW_OK; @@ -2018,7 +2086,7 @@ gst_mpegts_demux_pes_buffer_flush (GstMpegTSDemux * demux) for (i = 0; i < MPEGTS_MAX_PID + 1; i++) { GstMpegTSStream *stream = demux->streams[i]; if (stream && stream->pad) { - gst_mpegts_stream_pes_buffer_flush (stream); + gst_mpegts_stream_pes_buffer_flush (stream, discard); stream->pes_buffer_in_sync = FALSE; } } @@ -2093,7 +2161,7 @@ gst_mpegts_demux_parse_stream (GstMpegTSDemux * demux, GstMpegTSStream * stream, data += 3; datalen -= 3; - GST_DEBUG_OBJECT (demux, "afc 0x%x, pusi %d, PID 0x%04x datalen %u", + GST_LOG_OBJECT (demux, "afc 0x%x, pusi %d, PID 0x%04x datalen %u", adaptation_field_control, payload_unit_start_indicator, PID, datalen); ret = GST_FLOW_OK; @@ -2115,13 +2183,12 @@ gst_mpegts_demux_parse_stream (GstMpegTSDemux * demux, GstMpegTSStream * stream, data += consumed; datalen -= consumed; - GST_DEBUG_OBJECT (demux, "consumed: %u datalen: %u", consumed, datalen); + GST_LOG_OBJECT (demux, "consumed: %u datalen: %u", consumed, datalen); } /* If this packet has a payload, handle it */ if (adaptation_field_control & 0x1) { - GST_DEBUG_OBJECT (demux, "Packet payload %d bytes, PID 0x%04x", datalen, - PID); + GST_LOG_OBJECT (demux, "Packet payload %d bytes, PID 0x%04x", datalen, PID); /* For unknown streams, check if the PID is in the partial PIDs * list as an elementary stream and override the type if so @@ -2233,7 +2300,7 @@ gst_mpegts_demux_parse_stream (GstMpegTSDemux * demux, GstMpegTSStream * stream, "bytes of %u bytes in the PES buffer", PID, stream->pes_buffer_used, stream->pes_buffer_size); /* Flush buffered PES data */ - gst_mpegts_stream_pes_buffer_flush (stream); + gst_mpegts_stream_pes_buffer_flush (stream, FALSE); gst_pes_filter_drain (&stream->filter); /* Resize the buffer to half if no overflow detected and * had been used less than half of it */ @@ -2302,7 +2369,7 @@ static FORCE_INLINE GstFlowReturn gst_mpegts_demux_parse_transport_packet (GstMpegTSDemux * demux, const guint8 * data) { - GstFlowReturn ret; + GstFlowReturn ret = GST_FLOW_OK; guint16 PID; GstMpegTSStream *stream; @@ -2312,6 +2379,10 @@ gst_mpegts_demux_parse_transport_packet (GstMpegTSDemux * demux, /* get PID */ PID = ((data[0] & 0x1f) << 8) | data[1]; + /* Skip NULL packets */ + if (G_UNLIKELY (PID == 0x1fff)) + goto beach; + /* get the stream. */ stream = gst_mpegts_demux_get_stream_for_PID (demux, PID); @@ -2319,12 +2390,153 @@ gst_mpegts_demux_parse_transport_packet (GstMpegTSDemux * demux, ret = gst_mpegts_demux_parse_stream (demux, stream, data, MPEGTS_NORMAL_TS_PACKETSIZE - 1); + if (demux->pcr[1] != -1 && demux->bitrate == -1) { + guint64 bitrate; + GST_DEBUG_OBJECT (demux, "pcr[0]:%" G_GUINT64_FORMAT, demux->pcr[0]); + GST_DEBUG_OBJECT (demux, "pcr[1]:%" G_GUINT64_FORMAT, demux->pcr[1]); + GST_DEBUG_OBJECT (demux, "diff in time %" GST_TIME_FORMAT, + GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (demux->pcr[1] - demux->pcr[0]))); + GST_DEBUG_OBJECT (demux, "stream->last_PCR_difference: %" G_GUINT64_FORMAT + ", demux->num_packets %" G_GUINT64_FORMAT, + demux->pcr[1] - demux->pcr[0], demux->num_packets); + bitrate = gst_util_uint64_scale (GST_SECOND, + MPEGTS_NORMAL_TS_PACKETSIZE * demux->num_packets, + MPEGTIME_TO_GSTTIME (demux->pcr[1] - demux->pcr[0])); + /* somehow... I doubt a bitrate below one packet per second is valid */ + if (bitrate > MPEGTS_NORMAL_TS_PACKETSIZE - 1) { + demux->bitrate = bitrate; + GST_DEBUG_OBJECT (demux, "bitrate is %" G_GINT64_FORMAT + " bytes per second", demux->bitrate); + } else { + GST_WARNING_OBJECT (demux, "Couldn't compute valid bitrate, recomputing"); + demux->pcr[0] = demux->pcr[1] = -1; + demux->num_packets = -1; + } + } + +beach: + demux->num_packets++; return ret; /* ERRORS */ } static gboolean +gst_mpegts_demux_handle_seek_push (GstMpegTSDemux * demux, GstEvent * event) +{ + gboolean res = FALSE; + gdouble rate; + GstFormat format; + GstSeekFlags flags; + GstSeekType start_type, stop_type; + gint64 start, stop, bstart, bstop; + GstEvent *bevent; + + gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start, + &stop_type, &stop); + + GST_DEBUG_OBJECT (demux, "seek event, rate: %f start: %" GST_TIME_FORMAT + " stop: %" GST_TIME_FORMAT, rate, GST_TIME_ARGS (start), + GST_TIME_ARGS (stop)); + + if (format == GST_FORMAT_BYTES) { + GST_DEBUG_OBJECT (demux, "seek not supported on format %d", format); + goto beach; + } + + GST_DEBUG_OBJECT (demux, "seek - trying directly upstream first"); + + /* first try original format seek */ + res = gst_pad_push_event (demux->sinkpad, gst_event_ref (event)); + if (res == TRUE) + goto beach; + GST_DEBUG_OBJECT (demux, "seek - no upstream"); + + if (format != GST_FORMAT_TIME) { + /* From here down, we only support time based seeks */ + GST_DEBUG_OBJECT (demux, "seek not supported on format %d", format); + goto beach; + } + + /* We need to convert to byte based seek and we need a scr_rate for that. */ + if (demux->bitrate == -1) { + GST_DEBUG_OBJECT (demux, "seek not possible, no bitrate"); + goto beach; + } + + GST_DEBUG_OBJECT (demux, "try with bitrate"); + + bstart = GSTTIME_TO_BYTES (start); + bstop = GSTTIME_TO_BYTES (stop); + + GST_DEBUG_OBJECT (demux, "in bytes bstart %" G_GINT64_FORMAT " bstop %" + G_GINT64_FORMAT, bstart, bstop); + bevent = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, start_type, + bstart, stop_type, bstop); + + res = gst_pad_push_event (demux->sinkpad, bevent); + +beach: + gst_event_unref (event); + return res; +} + +static gboolean +gst_mpegts_demux_src_event (GstPad * pad, GstEvent * event) +{ + GstMpegTSDemux *demux = GST_MPEGTS_DEMUX (gst_pad_get_parent (pad)); + gboolean res = FALSE; + + GST_DEBUG_OBJECT (demux, "got event %s", + gst_event_type_get_name (GST_EVENT_TYPE (event))); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + res = gst_mpegts_demux_handle_seek_push (demux, event); + break; + default: + res = gst_pad_push_event (demux->sinkpad, event); + break; + } + + gst_object_unref (demux); + + return res; +} + +static void +gst_mpegts_demux_flush (GstMpegTSDemux * demux, gboolean discard) +{ + GstMpegTSStream *PCR_stream; + GstMpegTSStream *PMT_stream; + + GST_DEBUG_OBJECT (demux, "flushing MPEG TS demuxer (discard %d)", discard); + + /* Start by flushing internal buffers */ + gst_mpegts_demux_pes_buffer_flush (demux, discard); + + /* Clear adapter */ + gst_adapter_clear (demux->adapter); + + /* Try resetting the last_PCR value as we will have a discont */ + if (demux->current_PMT == 0) + goto beach; + + PMT_stream = demux->streams[demux->current_PMT]; + if (PMT_stream == NULL) + goto beach; + + PCR_stream = demux->streams[PMT_stream->PMT.PCR_PID]; + if (PCR_stream == NULL) + goto beach; + + PCR_stream->last_PCR = -1; + +beach: + return; +} + +static gboolean gst_mpegts_demux_send_event (GstMpegTSDemux * demux, GstEvent * event) { gint i; @@ -2347,7 +2559,7 @@ static gboolean gst_mpegts_demux_sink_event (GstPad * pad, GstEvent * event) { GstMpegTSDemux *demux = GST_MPEGTS_DEMUX (gst_pad_get_parent (pad)); - gboolean res; + gboolean res = FALSE; GST_DEBUG_OBJECT (demux, "got event %s", gst_event_type_get_name (GST_EVENT_TYPE (event))); @@ -2358,11 +2570,11 @@ gst_mpegts_demux_sink_event (GstPad * pad, GstEvent * event) break; case GST_EVENT_FLUSH_STOP: gst_adapter_clear (demux->adapter); + gst_mpegts_demux_flush (demux, TRUE); res = gst_mpegts_demux_send_event (demux, event); break; case GST_EVENT_EOS: - /* Flush buffered PES data */ - gst_mpegts_demux_pes_buffer_flush (demux); + gst_mpegts_demux_flush (demux, FALSE); /* Send the EOS event on each stream */ if (!(res = gst_mpegts_demux_send_event (demux, event))) { /* we have no streams */ @@ -2371,15 +2583,46 @@ gst_mpegts_demux_sink_event (GstPad * pad, GstEvent * event) } break; case GST_EVENT_NEWSEGMENT: - res = gst_mpegts_demux_send_event (demux, event); + { + gboolean update; + gdouble rate; + GstFormat format; + gint64 start, stop, time; + + gst_event_parse_new_segment (event, &update, &rate, &format, + &start, &stop, &time); + + gst_event_unref (event); + GST_INFO_OBJECT (demux, "received new segment: rate %g " + "format %d, start: %" G_GINT64_FORMAT ", stop: %" G_GINT64_FORMAT + ", time: %" G_GINT64_FORMAT, rate, format, start, stop, time); + if (format == GST_FORMAT_BYTES && demux->bitrate != -1) { + gint64 tstart = 0, tstop = 0, pos = 0; + + if (demux->base_pts != GST_CLOCK_TIME_NONE) { + tstart = tstop = demux->base_pts; + } + tstart += BYTES_TO_GSTTIME (start); + tstop += BYTES_TO_GSTTIME (stop); + pos = BYTES_TO_GSTTIME (time); + + event = gst_event_new_new_segment (update, rate, + GST_FORMAT_TIME, tstart, tstop, pos); + GST_DEBUG_OBJECT (demux, "pushing time newsegment from %" + GST_TIME_FORMAT " to %" GST_TIME_FORMAT " pos %" GST_TIME_FORMAT, + GST_TIME_ARGS (tstart), GST_TIME_ARGS (tstop), GST_TIME_ARGS (pos)); + + res = gst_mpegts_demux_send_event (demux, event); + } break; + } default: res = gst_mpegts_demux_send_event (demux, event); break; } gst_object_unref (demux); - return TRUE; + return res; } static gboolean @@ -2415,8 +2658,7 @@ gst_mpegts_demux_provide_clock (GstElement * element) "MpegTSClock", NULL); demux->clock_base = GST_CLOCK_TIME_NONE; } - - return demux->clock; + return gst_object_ref (demux->clock); } return NULL; @@ -2455,21 +2697,54 @@ gst_mpegts_demux_src_pad_query (GstPad * pad, GstQuery * query) case GST_QUERY_DURATION: { GstFormat format; - gint64 duration; + GstPad *peer; - gst_query_parse_duration (query, &format, &duration); + gst_query_parse_duration (query, &format, NULL); - if (format == GST_FORMAT_BYTES) { - res = FALSE; - } else { - res = gst_pad_query_default (pad, query); + /* Try query upstream first */ + peer = gst_pad_get_peer (demux->sinkpad); + if (peer) { + res = gst_pad_query (peer, query); + /* Try doing something with that query if it failed */ + if (!res && format == GST_FORMAT_TIME && demux->bitrate != -1) { + /* Try using cache first */ + if (GST_CLOCK_TIME_IS_VALID (demux->cache_duration)) { + GST_LOG_OBJECT (demux, "replying duration query from cache %" + GST_TIME_FORMAT, GST_TIME_ARGS (demux->cache_duration)); + gst_query_set_duration (query, GST_FORMAT_TIME, + demux->cache_duration); + res = TRUE; + } else { /* Query upstream and approximate */ + GstQuery *bquery = gst_query_new_duration (GST_FORMAT_BYTES); + gint64 duration = 0; + + /* Query peer for duration in bytes */ + res = gst_pad_query (peer, bquery); + if (res) { + /* Convert to time format */ + gst_query_parse_duration (bquery, &format, &duration); + GST_DEBUG_OBJECT (demux, "query on peer pad reported bytes %" + G_GUINT64_FORMAT, duration); + demux->cache_duration = BYTES_TO_GSTTIME (duration); + GST_DEBUG_OBJECT (demux, "converted to time %" GST_TIME_FORMAT, + GST_TIME_ARGS (demux->cache_duration)); + gst_query_set_duration (query, GST_FORMAT_TIME, + demux->cache_duration); + } + gst_query_unref (bquery); + } + } else { + GST_WARNING_OBJECT (demux, "unsupported query format or no bitrate " + "yet to approximate duration from bytes"); + } + gst_object_unref (peer); } - break; } default: res = gst_pad_query_default (pad, query); } + gst_object_unref (demux); return res; } @@ -2554,9 +2829,7 @@ gst_mpegts_demux_chain (GstPad * pad, GstBuffer * buffer) guint sync_count; if (GST_BUFFER_IS_DISCONT (buffer)) { - /* Flush buffered PES data */ - gst_mpegts_demux_pes_buffer_flush (demux); - gst_adapter_clear (demux->adapter); + gst_mpegts_demux_flush (demux, FALSE); } /* first push the new buffer into the adapter */ gst_adapter_push (demux->adapter, buffer); diff --git a/gst/mpegdemux/gstmpegtsdemux.h b/gst/mpegdemux/gstmpegtsdemux.h index 8a4ca3d2..fde68926 100644 --- a/gst/mpegdemux/gstmpegtsdemux.h +++ b/gst/mpegdemux/gstmpegtsdemux.h @@ -34,9 +34,9 @@ * * The Original Code is Fluendo MPEG Demuxer plugin. * - * The Initial Developer of the Original Code is Fluendo, S.L. - * Portions created by Fluendo, S.L. are Copyright (C) 2005 - * Fluendo, S.L. All Rights Reserved. + * The Initial Developer of the Original Code is Fluendo, S.A. + * Portions created by Fluendo, S.A. are Copyright (C) 2005,2006,2007,2008,2009 + * Fluendo, S.A. All Rights Reserved. * * Contributor(s): Wim Taymans <wim@fluendo.com> */ @@ -205,6 +205,21 @@ struct _GstMpegTSDemux { /* clocking */ GstClock * clock; GstClockTime clock_base; + + /* Additional information required for seeking. + * num_packets: Number of packets outputted + * bitrate: estimated bitrate (based on pcr and num_packets */ + guint64 num_packets; + gint64 bitrate; + + /* Two PCRs observations to calculate bitrate */ + guint64 pcr[2]; + + /* Cached duration estimation */ + GstClockTime cache_duration; + + /* Cached base_PCR in GStreamer time. */ + GstClockTime base_pts; }; struct _GstMpegTSDemuxClass { @@ -213,6 +228,7 @@ struct _GstMpegTSDemuxClass { GstPadTemplate * sink_template; GstPadTemplate * video_template; GstPadTemplate * audio_template; + GstPadTemplate * subpicture_template; GstPadTemplate * private_template; }; diff --git a/gst/mpegdemux/gstpesfilter.c b/gst/mpegdemux/gstpesfilter.c index 1295a193..b520c766 100644 --- a/gst/mpegdemux/gstpesfilter.c +++ b/gst/mpegdemux/gstpesfilter.c @@ -167,6 +167,9 @@ gst_pes_filter_parse (GstPESFilter * filter) avail = MIN (avail, filter->length + 6); } + if (avail < 7) + goto need_more_data; + /* read more data, either the whole packet if there is a length * or whatever we have available if this in an unbounded packet. */ if (!(data = gst_adapter_peek (filter->adapter, avail))) @@ -189,7 +192,8 @@ gst_pes_filter_parse (GstPESFilter * filter) case ID_PROGRAM_STREAM_DIRECTORY: case ID_DSMCC_STREAM: case ID_ITU_TREC_H222_TYPE_E_STREAM: - goto skip; + /* Push directly out */ + goto push_out; case ID_PADDING_STREAM: GST_DEBUG ("skipping padding stream"); goto skip; @@ -197,9 +201,6 @@ gst_pes_filter_parse (GstPESFilter * filter) break; } - if (datalen < 1) - goto need_more_data; - filter->pts = filter->dts = -1; /* stuffing bits, first two bits are '10' for mpeg2 pes so this code is @@ -404,6 +405,7 @@ gst_pes_filter_parse (GstPESFilter * filter) goto lost_sync; } +push_out: { GstBuffer *out; guint16 consumed; @@ -563,14 +565,8 @@ gst_pes_filter_process (GstPESFilter * filter) ret = GST_FLOW_OK; } else { GstBuffer *out; - guint8 *data; - - data = gst_adapter_take (filter->adapter, avail); - out = gst_buffer_new (); - GST_BUFFER_DATA (out) = data; - GST_BUFFER_SIZE (out) = avail; - GST_BUFFER_MALLOCDATA (out) = data; + out = gst_adapter_take_buffer (filter->adapter, avail); ret = gst_pes_filter_data_push (filter, filter->first, out); filter->first = FALSE; diff --git a/gst/mpegdemux/gstpesfilter.h b/gst/mpegdemux/gstpesfilter.h index b35d8746..ccc8461d 100644 --- a/gst/mpegdemux/gstpesfilter.h +++ b/gst/mpegdemux/gstpesfilter.h @@ -84,8 +84,6 @@ struct _GstPESFilter { gboolean unbounded_packet; guint16 length; - guint8 type; - gint64 pts; gint64 dts; }; diff --git a/gst/mpegdemux/mpegtspacketizer.c b/gst/mpegdemux/mpegtspacketizer.c index 1abbf1fe..b986fa60 100644 --- a/gst/mpegdemux/mpegtspacketizer.c +++ b/gst/mpegdemux/mpegtspacketizer.c @@ -241,7 +241,8 @@ mpegts_packetizer_parse_section_header (MpegTSPacketizer * packetizer, data = GST_BUFFER_DATA (section->buffer); section->table_id = *data++; - if ((data[0] & 0x80) == 0) + /* if table_id is 0 (pat) then ignore the subtable extension */ + if ((data[0] & 0x80) == 0 || section->table_id == 0) section->subtable_extension = 0; else section->subtable_extension = GST_READ_UINT16_BE (data + 2); @@ -1844,6 +1845,22 @@ mpegts_packetizer_clear (MpegTSPacketizer * packetizer) gst_adapter_clear (packetizer->adapter); } +void +mpegts_packetizer_remove_stream (MpegTSPacketizer * packetizer, gint16 pid) +{ + MpegTSPacketizerStream *stream = + (MpegTSPacketizerStream *) g_hash_table_lookup (packetizer->streams, + GINT_TO_POINTER ((gint) pid)); + if (stream) { + GST_INFO ("Removing stream for PID %d", pid); + + g_hash_table_remove (packetizer->streams, GINT_TO_POINTER ((gint) pid)); + + g_object_unref (stream->section_adapter); + g_free (stream); + } +} + MpegTSPacketizer * mpegts_packetizer_new () { diff --git a/gst/mpegdemux/mpegtspacketizer.h b/gst/mpegdemux/mpegtspacketizer.h index c54228af..97e15cf2 100644 --- a/gst/mpegdemux/mpegtspacketizer.h +++ b/gst/mpegdemux/mpegtspacketizer.h @@ -116,6 +116,8 @@ gboolean mpegts_packetizer_next_packet (MpegTSPacketizer *packetizer, MpegTSPacketizerPacket *packet); void mpegts_packetizer_clear_packet (MpegTSPacketizer *packetizer, MpegTSPacketizerPacket *packet); +void mpegts_packetizer_remove_stream(MpegTSPacketizer *packetizer, + gint16 pid); gboolean mpegts_packetizer_push_section (MpegTSPacketizer *packetzer, MpegTSPacketizerPacket *packet, MpegTSPacketizerSection *section); diff --git a/gst/mpegdemux/mpegtsparse.c b/gst/mpegdemux/mpegtsparse.c index 3a2d647d..976935f2 100644 --- a/gst/mpegdemux/mpegtsparse.c +++ b/gst/mpegdemux/mpegtsparse.c @@ -266,12 +266,15 @@ mpegts_parse_init (MpegTSParse * parse, MpegTSParseClass * klass) gst_element_add_pad (GST_ELEMENT (parse), parse->sinkpad); parse->disposed = FALSE; + parse->need_sync_program_pads = FALSE; parse->packetizer = mpegts_packetizer_new (); parse->program_numbers = g_strdup (""); parse->pads_to_add = NULL; + parse->pads_to_remove = NULL; parse->programs = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) mpegts_parse_free_program); parse->psi_pids = g_hash_table_new (g_direct_hash, g_direct_equal); + parse->pes_pids = g_hash_table_new (g_direct_hash, g_direct_equal); mpegts_parse_reset (parse); } @@ -301,6 +304,7 @@ mpegts_parse_finalize (GObject * object) } g_hash_table_destroy (parse->programs); g_hash_table_destroy (parse->psi_pids); + g_hash_table_destroy (parse->pes_pids); if (G_OBJECT_CLASS (parent_class)->finalize) G_OBJECT_CLASS (parent_class)->finalize (object); @@ -348,7 +352,7 @@ mpegts_parse_add_program (MpegTSParse * parse, program->pcr_pid = G_MAXUINT16; program->streams = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) mpegts_parse_free_stream); - program->patcount = 1; + program->patcount = 0; program->selected = 0; program->active = FALSE; @@ -422,22 +426,30 @@ mpegts_parse_remove_program (MpegTSParse * parse, gint program_number) } static void -mpegts_parse_sync_program_pads (MpegTSParse * parse, - GList * to_add, GList * to_remove) +mpegts_parse_sync_program_pads (MpegTSParse * parse) { GList *walk; - for (walk = to_remove; walk; walk = walk->next) + GST_INFO_OBJECT (parse, "begin sync pads"); + for (walk = parse->pads_to_remove; walk; walk = walk->next) gst_element_remove_pad (GST_ELEMENT (parse), GST_PAD (walk->data)); - for (walk = to_add; walk; walk = walk->next) + for (walk = parse->pads_to_add; walk; walk = walk->next) gst_element_add_pad (GST_ELEMENT (parse), GST_PAD (walk->data)); - if (to_add) - g_list_free (to_add); + if (parse->pads_to_add) + g_list_free (parse->pads_to_add); + + if (parse->pads_to_remove) + g_list_free (parse->pads_to_remove); + + GST_OBJECT_LOCK (parse); + parse->pads_to_remove = NULL; + parse->pads_to_add = NULL; + parse->need_sync_program_pads = FALSE; + GST_OBJECT_UNLOCK (parse); - if (to_remove) - g_list_free (to_remove); + GST_INFO_OBJECT (parse, "end sync pads"); } @@ -493,9 +505,6 @@ static void mpegts_parse_reset_selected_programs (MpegTSParse * parse, gchar * program_numbers) { - GList *pads_to_add = NULL; - GList *pads_to_remove = NULL; - GST_OBJECT_LOCK (parse); if (parse->program_numbers) g_free (parse->program_numbers); @@ -526,13 +535,9 @@ mpegts_parse_reset_selected_programs (MpegTSParse * parse, g_hash_table_foreach (parse->programs, foreach_program_activate_or_deactivate, parse); - pads_to_add = parse->pads_to_add; - parse->pads_to_add = NULL; - pads_to_remove = parse->pads_to_remove; - parse->pads_to_remove = NULL; + if (parse->pads_to_remove || parse->pads_to_add) + parse->need_sync_program_pads = TRUE; GST_OBJECT_UNLOCK (parse); - - mpegts_parse_sync_program_pads (parse, pads_to_add, pads_to_remove); } static void @@ -804,6 +809,10 @@ mpegts_parse_is_psi (MpegTSParse * parse, MpegTSPacketizerPacket * packet) if (g_hash_table_lookup (parse->psi_pids, GINT_TO_POINTER ((gint) packet->pid)) != NULL) retval = TRUE; + /* check is it is a pes pid */ + if (g_hash_table_lookup (parse->pes_pids, + GINT_TO_POINTER ((gint) packet->pid)) != NULL) + return FALSE; if (!retval) { if (packet->payload_unit_start_indicator) { table_id = *(packet->data); @@ -850,8 +859,6 @@ mpegts_parse_apply_pat (MpegTSParse * parse, GstStructure * pat_info) guint pid; MpegTSParseProgram *program; gint i; - GList *pads_to_add = NULL; - GList *pads_to_remove = NULL; const GValue *programs; gchar *dbg; @@ -889,14 +896,12 @@ mpegts_parse_apply_pat (MpegTSParse * parse, GstStructure * pat_info) g_hash_table_insert (parse->psi_pids, GINT_TO_POINTER ((gint) pid), GINT_TO_POINTER (1)); } - - program->patcount += 1; } else { g_hash_table_insert (parse->psi_pids, GINT_TO_POINTER ((gint) pid), GINT_TO_POINTER (1)); program = mpegts_parse_add_program (parse, program_number, pid); } - + program->patcount += 1; if (program->selected && !program->active) parse->pads_to_add = g_list_append (parse->pads_to_add, mpegts_parse_activate_program (parse, program)); @@ -937,18 +942,15 @@ mpegts_parse_apply_pat (MpegTSParse * parse, GstStructure * pat_info) mpegts_parse_remove_program (parse, program_number); g_hash_table_remove (parse->psi_pids, GINT_TO_POINTER ((gint) pid)); + mpegts_packetizer_remove_stream (parse->packetizer, pid); } gst_structure_free (old_pat); } - pads_to_add = parse->pads_to_add; - parse->pads_to_add = NULL; - pads_to_remove = parse->pads_to_remove; - parse->pads_to_remove = NULL; GST_OBJECT_UNLOCK (parse); - mpegts_parse_sync_program_pads (parse, pads_to_add, pads_to_remove); + mpegts_parse_sync_program_pads (parse); } static void @@ -983,10 +985,13 @@ mpegts_parse_apply_pmt (MpegTSParse * parse, gst_structure_get_uint (stream, "pid", &pid); gst_structure_get_uint (stream, "stream-type", &stream_type); mpegts_parse_program_remove_stream (parse, program, (guint16) pid); + g_hash_table_remove (parse->pes_pids, GINT_TO_POINTER ((gint) pid)); } /* remove pcr stream */ mpegts_parse_program_remove_stream (parse, program, program->pcr_pid); + g_hash_table_remove (parse->pes_pids, + GINT_TO_POINTER ((gint) program->pcr_pid)); gst_structure_free (program->pmt_info); program->pmt_info = NULL; @@ -1003,6 +1008,8 @@ mpegts_parse_apply_pmt (MpegTSParse * parse, program->pmt_pid = pmt_pid; program->pcr_pid = pcr_pid; mpegts_parse_program_add_stream (parse, program, (guint16) pcr_pid, -1); + g_hash_table_insert (parse->pes_pids, GINT_TO_POINTER ((gint) pcr_pid), + GINT_TO_POINTER (1)); for (i = 0; i < gst_value_list_get_size (new_streams); ++i) { value = gst_value_list_get_value (new_streams, i); @@ -1012,6 +1019,9 @@ mpegts_parse_apply_pmt (MpegTSParse * parse, gst_structure_get_uint (stream, "stream-type", &stream_type); mpegts_parse_program_add_stream (parse, program, (guint16) pid, (guint8) stream_type); + g_hash_table_insert (parse->pes_pids, GINT_TO_POINTER ((gint) pid), + GINT_TO_POINTER ((gint) 1)); + } GST_OBJECT_UNLOCK (parse); @@ -1226,6 +1236,9 @@ mpegts_parse_chain (GstPad * pad, GstBuffer * buf) mpegts_packetizer_clear_packet (parse->packetizer, &packet); } + if (parse->need_sync_program_pads) + mpegts_parse_sync_program_pads (parse); + gst_object_unref (parse); return res; } diff --git a/gst/mpegdemux/mpegtsparse.h b/gst/mpegdemux/mpegtsparse.h index 34a78634..36466b81 100644 --- a/gst/mpegdemux/mpegtsparse.h +++ b/gst/mpegdemux/mpegtsparse.h @@ -60,7 +60,9 @@ struct _MpegTSParse { GstStructure *pat; MpegTSPacketizer *packetizer; GHashTable *psi_pids; + GHashTable *pes_pids; gboolean disposed; + gboolean need_sync_program_pads; }; struct _MpegTSParseClass { diff --git a/gst/mpegvideoparse/mpegpacketiser.c b/gst/mpegvideoparse/mpegpacketiser.c index 447c50ae..0312680e 100644 --- a/gst/mpegvideoparse/mpegpacketiser.c +++ b/gst/mpegvideoparse/mpegpacketiser.c @@ -498,6 +498,9 @@ mpeg_util_parse_extension_packet (MPEGSeqHdr * hdr, guint8 * data, guint8 * end) case MPEG_PACKET_EXT_SEQUENCE: { /* Parse a Sequence Extension */ + guint8 profile_level; + gboolean low_delay; + guint8 chroma_format; guint8 horiz_size_ext, vert_size_ext; guint8 fps_n_ext, fps_d_ext; @@ -505,8 +508,12 @@ mpeg_util_parse_extension_packet (MPEGSeqHdr * hdr, guint8 * data, guint8 * end) /* need at least 10 bytes, minus 4 for the start code 000001b5 */ return FALSE; + profile_level = ((data[0] << 4) & 0xf0) | ((data[1]) >> 4); + hdr->progressive = data[1] & 0x08; + chroma_format = (data[1] >> 2) & 0x03; horiz_size_ext = ((data[1] << 1) & 0x02) | ((data[2] >> 7) & 0x01); vert_size_ext = (data[2] >> 5) & 0x03; + low_delay = data[5] >> 7; fps_n_ext = (data[5] >> 5) & 0x03; fps_d_ext = data[5] & 0x1f; diff --git a/gst/mpegvideoparse/mpegpacketiser.h b/gst/mpegvideoparse/mpegpacketiser.h index 426b13aa..549ca621 100644 --- a/gst/mpegvideoparse/mpegpacketiser.h +++ b/gst/mpegvideoparse/mpegpacketiser.h @@ -76,6 +76,8 @@ struct MPEGSeqHdr gint width, height; /* Framerate */ gint fps_n, fps_d; + + gboolean progressive; }; struct MPEGPictureHdr diff --git a/gst/mpegvideoparse/mpegvideoparse.c b/gst/mpegvideoparse/mpegvideoparse.c index 3c930179..2a7f9153 100644 --- a/gst/mpegvideoparse/mpegvideoparse.c +++ b/gst/mpegvideoparse/mpegvideoparse.c @@ -261,6 +261,7 @@ mpegvideoparse_handle_sequence (MpegVideoParse * mpegvideoparse, "height", G_TYPE_INT, new_hdr.height, "framerate", GST_TYPE_FRACTION, new_hdr.fps_n, new_hdr.fps_d, "pixel-aspect-ratio", GST_TYPE_FRACTION, new_hdr.par_w, new_hdr.par_h, + "interlaced", G_TYPE_BOOLEAN, !new_hdr.progressive, "codec_data", GST_TYPE_BUFFER, seq_buf, NULL); GST_DEBUG ("New mpegvideoparse caps: %" GST_PTR_FORMAT, caps); diff --git a/gst/mxf/mxf.c b/gst/mxf/mxf.c index c1a0a716..7469317c 100644 --- a/gst/mxf/mxf.c +++ b/gst/mxf/mxf.c @@ -71,7 +71,8 @@ plugin_init (GstPlugin * plugin) if (!gst_element_register (plugin, "mxfdemux", GST_RANK_PRIMARY, GST_TYPE_MXF_DEMUX) || - !gst_element_register (plugin, "mxfmux", GST_RANK_NONE, GST_TYPE_MXF_MUX)) + !gst_element_register (plugin, "mxfmux", GST_RANK_PRIMARY, + GST_TYPE_MXF_MUX)) return FALSE; return TRUE; diff --git a/gst/mxf/mxfdemux.c b/gst/mxf/mxfdemux.c index 6d7ca034..c33650b5 100644 --- a/gst/mxf/mxfdemux.c +++ b/gst/mxf/mxfdemux.c @@ -203,7 +203,7 @@ gst_mxf_demux_reset_metadata (GstMXFDemux * demux) { GST_DEBUG_OBJECT (demux, "Resetting metadata"); - g_mutex_lock (demux->metadata_lock); + g_static_rw_lock_writer_lock (&demux->metadata_lock); demux->update_metadata = TRUE; demux->metadata_resolved = FALSE; @@ -217,7 +217,7 @@ gst_mxf_demux_reset_metadata (GstMXFDemux * demux) } demux->metadata = mxf_metadata_hash_table_new (); - g_mutex_unlock (demux->metadata_lock); + g_static_rw_lock_writer_unlock (&demux->metadata_lock); } static void @@ -513,11 +513,14 @@ gst_mxf_demux_resolve_references (GstMXFDemux * demux) GstStructure *structure; GstTagList *taglist; + g_static_rw_lock_writer_lock (&demux->metadata_lock); + GST_DEBUG_OBJECT (demux, "Resolve metadata references"); demux->update_metadata = FALSE; if (!demux->metadata) { GST_ERROR_OBJECT (demux, "No metadata yet"); + g_static_rw_lock_writer_unlock (&demux->metadata_lock); return GST_FLOW_ERROR; } #if GLIB_CHECK_VERSION (2, 16, 0) @@ -565,6 +568,8 @@ gst_mxf_demux_resolve_references (GstMXFDemux * demux) g_list_free (values); #endif + g_static_rw_lock_writer_unlock (&demux->metadata_lock); + return ret; error: @@ -573,6 +578,7 @@ error: #endif demux->metadata_resolved = FALSE; + g_static_rw_lock_writer_unlock (&demux->metadata_lock); return ret; } @@ -913,24 +919,29 @@ gst_mxf_demux_update_tracks (GstMXFDemux * demux) gboolean first_run; guint component_index; GstFlowReturn ret; + GList *pads = NULL, *l; + g_static_rw_lock_writer_lock (&demux->metadata_lock); GST_DEBUG_OBJECT (demux, "Updating tracks"); if ((ret = gst_mxf_demux_update_essence_tracks (demux)) != GST_FLOW_OK) { - return ret; + goto error; } current_package = gst_mxf_demux_choose_package (demux); if (!current_package) { GST_ERROR_OBJECT (demux, "Unable to find current package"); - return GST_FLOW_ERROR; + ret = GST_FLOW_ERROR; + goto error; } else if (!current_package->tracks) { GST_ERROR_OBJECT (demux, "Current package has no (resolved) tracks"); - return GST_FLOW_ERROR; + ret = GST_FLOW_ERROR; + goto error; } else if (!current_package->n_essence_tracks) { GST_ERROR_OBJECT (demux, "Current package has no essence tracks"); - return GST_FLOW_ERROR; + ret = GST_FLOW_ERROR; + goto error; } first_run = (demux->src->len == 0); @@ -977,10 +988,12 @@ gst_mxf_demux_update_tracks (GstMXFDemux * demux) if (!track->parent.sequence) { GST_WARNING_OBJECT (demux, "Track with no sequence"); - if (!pad) + if (!pad) { continue; - else - return GST_FLOW_ERROR; + } else { + ret = GST_FLOW_ERROR; + goto error; + } } sequence = track->parent.sequence; @@ -1021,20 +1034,24 @@ gst_mxf_demux_update_tracks (GstMXFDemux * demux) if (track->parent.type && (track->parent.type & 0xf0) != 0x30) { GST_DEBUG_OBJECT (demux, "No essence track"); - if (!pad) + if (!pad) { continue; - else - return GST_FLOW_ERROR; + } else { + ret = GST_FLOW_ERROR; + goto error; + } } if (!source_package || track->parent.type == MXF_METADATA_TRACK_UNKNOWN || !source_track) { GST_WARNING_OBJECT (demux, "No source package or track type for track found"); - if (!pad) + if (!pad) { continue; - else - return GST_FLOW_ERROR; + } else { + ret = GST_FLOW_ERROR; + goto error; + } } for (k = 0; k < demux->essence_tracks->len; k++) { @@ -1050,44 +1067,54 @@ gst_mxf_demux_update_tracks (GstMXFDemux * demux) if (!etrack) { GST_WARNING_OBJECT (demux, "No essence track for this track found"); - if (!pad) + if (!pad) { continue; - else - return GST_FLOW_ERROR; + } else { + ret = GST_FLOW_ERROR; + goto error; + } } if (track->edit_rate.n <= 0 || track->edit_rate.d <= 0 || source_track->edit_rate.n <= 0 || source_track->edit_rate.d <= 0) { GST_WARNING_OBJECT (demux, "Track has an invalid edit rate"); - if (!pad) + if (!pad) { continue; - else - return GST_FLOW_ERROR; + } else { + ret = GST_FLOW_ERROR; + goto error; + } } if (MXF_IS_METADATA_MATERIAL_PACKAGE (current_package) && !component) { GST_WARNING_OBJECT (demux, "Playing material package but found no component for track"); - if (!pad) + if (!pad) { continue; - else - return GST_FLOW_ERROR; + } else { + ret = GST_FLOW_ERROR; + goto error; + } } if (!source_package->descriptor) { GST_WARNING_OBJECT (demux, "Source package has no descriptors"); - if (!pad) + if (!pad) { continue; - else - return GST_FLOW_ERROR; + } else { + ret = GST_FLOW_ERROR; + goto error; + } } if (!source_track->parent.descriptor) { GST_WARNING_OBJECT (demux, "No descriptor found for track"); - if (!pad) + if (!pad) { continue; - else - return GST_FLOW_ERROR; + } else { + ret = GST_FLOW_ERROR; + goto error; + } } if (!pad && first_run) { @@ -1178,31 +1205,43 @@ gst_mxf_demux_update_tracks (GstMXFDemux * demux) gst_pad_use_fixed_caps (GST_PAD_CAST (pad)); gst_pad_set_active (GST_PAD_CAST (pad), TRUE); - gst_element_add_pad (GST_ELEMENT_CAST (demux), gst_object_ref (pad)); + pads = g_list_prepend (pads, gst_object_ref (pad)); g_ptr_array_add (demux->src, pad); pad->discont = TRUE; } } - if (first_run) - gst_element_no_more_pads (GST_ELEMENT_CAST (demux)); - if (demux->src->len > 0) { for (i = 0; i < demux->src->len; i++) { GstMXFDemuxPad *pad = g_ptr_array_index (demux->src, i); if (!pad->material_track || !pad->material_package) { GST_ERROR_OBJECT (demux, "Unable to update existing pad"); - return GST_FLOW_ERROR; + ret = GST_FLOW_ERROR; + goto error; } } } else { GST_ERROR_OBJECT (demux, "Couldn't create any streams"); - return GST_FLOW_ERROR; + ret = GST_FLOW_ERROR; + goto error; } + g_static_rw_lock_writer_unlock (&demux->metadata_lock); + + for (l = pads; l; l = l->next) + gst_element_add_pad (GST_ELEMENT_CAST (demux), l->data); + g_list_free (pads); + + if (first_run) + gst_element_no_more_pads (GST_ELEMENT_CAST (demux)); + return GST_FLOW_OK; + +error: + g_static_rw_lock_writer_unlock (&demux->metadata_lock); + return ret; } static GstFlowReturn @@ -1276,7 +1315,7 @@ gst_mxf_demux_handle_metadata (GstMXFDemux * demux, const MXFUL * key, return GST_FLOW_OK; } - g_mutex_lock (demux->metadata_lock); + g_static_rw_lock_writer_lock (&demux->metadata_lock); demux->update_metadata = TRUE; if (MXF_IS_METADATA_PREFACE (metadata)) { @@ -1287,7 +1326,7 @@ gst_mxf_demux_handle_metadata (GstMXFDemux * demux, const MXFUL * key, g_hash_table_replace (demux->metadata, &MXF_METADATA_BASE (metadata)->instance_uid, metadata); - g_mutex_unlock (demux->metadata_lock); + g_static_rw_lock_writer_unlock (&demux->metadata_lock); return ret; } @@ -1365,7 +1404,7 @@ gst_mxf_demux_handle_descriptive_metadata (GstMXFDemux * demux, return GST_FLOW_OK; } - g_mutex_lock (demux->metadata_lock); + g_static_rw_lock_writer_lock (&demux->metadata_lock); demux->update_metadata = TRUE; gst_mxf_demux_reset_linked_metadata (demux); @@ -1373,7 +1412,7 @@ gst_mxf_demux_handle_descriptive_metadata (GstMXFDemux * demux, g_hash_table_replace (demux->metadata, &MXF_METADATA_BASE (m)->instance_uid, m); - g_mutex_unlock (demux->metadata_lock); + g_static_rw_lock_writer_unlock (&demux->metadata_lock); return ret; } @@ -2224,17 +2263,14 @@ next_try: /* resolve references etc */ - g_mutex_lock (demux->metadata_lock); if (gst_mxf_demux_resolve_references (demux) != GST_FLOW_OK || gst_mxf_demux_update_tracks (demux) != GST_FLOW_OK) { demux->current_partition->parsed_metadata = TRUE; demux->offset = demux->run_in + demux->current_partition->partition.this_partition - demux->current_partition->partition.prev_partition; - g_mutex_unlock (demux->metadata_lock); goto next_try; } - g_mutex_unlock (demux->metadata_lock); out: if (buffer) @@ -2253,7 +2289,6 @@ gst_mxf_demux_handle_klv_packet (GstMXFDemux * demux, const MXFUL * key, #endif GstFlowReturn ret = GST_FLOW_OK; - g_mutex_lock (demux->metadata_lock); if (demux->update_metadata && demux->preface && (demux->offset >= @@ -2265,16 +2300,13 @@ gst_mxf_demux_handle_klv_packet (GstMXFDemux * demux, const MXFUL * key, demux->current_partition->parsed_metadata = TRUE; if ((ret = gst_mxf_demux_resolve_references (demux)) != GST_FLOW_OK || (ret = gst_mxf_demux_update_tracks (demux)) != GST_FLOW_OK) { - g_mutex_unlock (demux->metadata_lock); goto beach; } } else if (demux->metadata_resolved && demux->requested_package_string) { if ((ret = gst_mxf_demux_update_tracks (demux)) != GST_FLOW_OK) { - g_mutex_unlock (demux->metadata_lock); goto beach; } } - g_mutex_unlock (demux->metadata_lock); if (!mxf_is_mxf_packet (key)) { GST_WARNING_OBJECT (demux, @@ -3106,15 +3138,12 @@ gst_mxf_demux_seek_push (GstMXFDemux * demux, GstEvent * event) guint64 new_offset = -1; GstEvent *e; - g_mutex_lock (demux->metadata_lock); if (!demux->metadata_resolved || demux->update_metadata) { if (gst_mxf_demux_resolve_references (demux) != GST_FLOW_OK || gst_mxf_demux_update_tracks (demux) != GST_FLOW_OK) { - g_mutex_unlock (demux->metadata_lock); goto unresolved_metadata; } } - g_mutex_unlock (demux->metadata_lock); /* Do the actual seeking */ for (i = 0; i < demux->src->len; i++) { @@ -3266,15 +3295,12 @@ gst_mxf_demux_seek_pull (GstMXFDemux * demux, GstEvent * event) if (flush || seeksegment.last_stop != demux->segment.last_stop) { guint64 new_offset = -1; - g_mutex_lock (demux->metadata_lock); if (!demux->metadata_resolved || demux->update_metadata) { if (gst_mxf_demux_resolve_references (demux) != GST_FLOW_OK || gst_mxf_demux_update_tracks (demux) != GST_FLOW_OK) { - g_mutex_unlock (demux->metadata_lock); goto unresolved_metadata; } } - g_mutex_unlock (demux->metadata_lock); /* Do the actual seeking */ for (i = 0; i < demux->src->len; i++) { @@ -3455,11 +3481,11 @@ gst_mxf_demux_src_query (GstPad * pad, GstQuery * query) pos = mxfpad->last_stop; - g_mutex_lock (demux->metadata_lock); + g_static_rw_lock_reader_lock (&demux->metadata_lock); if (format == GST_FORMAT_DEFAULT && pos != GST_CLOCK_TIME_NONE) { if (!mxfpad->material_track || mxfpad->material_track->edit_rate.n == 0 || mxfpad->material_track->edit_rate.d == 0) { - g_mutex_unlock (demux->metadata_lock); + g_static_rw_lock_reader_unlock (&demux->metadata_lock); goto error; } @@ -3468,7 +3494,7 @@ gst_mxf_demux_src_query (GstPad * pad, GstQuery * query) mxfpad->material_track->edit_rate.n, mxfpad->material_track->edit_rate.d * GST_SECOND); } - g_mutex_unlock (demux->metadata_lock); + g_static_rw_lock_reader_unlock (&demux->metadata_lock); GST_DEBUG_OBJECT (pad, "Returning position %" G_GINT64_FORMAT " in format %s", pos, @@ -3487,9 +3513,9 @@ gst_mxf_demux_src_query (GstPad * pad, GstQuery * query) if (format != GST_FORMAT_TIME && format != GST_FORMAT_DEFAULT) goto error; - g_mutex_lock (demux->metadata_lock); + g_static_rw_lock_reader_lock (&demux->metadata_lock); if (!mxfpad->material_track || !mxfpad->material_track->parent.sequence) { - g_mutex_unlock (demux->metadata_lock); + g_static_rw_lock_reader_unlock (&demux->metadata_lock); goto error; } @@ -3500,7 +3526,7 @@ gst_mxf_demux_src_query (GstPad * pad, GstQuery * query) if (duration != -1 && format == GST_FORMAT_TIME) { if (mxfpad->material_track->edit_rate.n == 0 || mxfpad->material_track->edit_rate.d == 0) { - g_mutex_unlock (demux->metadata_lock); + g_static_rw_lock_reader_unlock (&demux->metadata_lock); goto error; } @@ -3509,7 +3535,7 @@ gst_mxf_demux_src_query (GstPad * pad, GstQuery * query) GST_SECOND * mxfpad->material_track->edit_rate.d, mxfpad->material_track->edit_rate.n); } - g_mutex_unlock (demux->metadata_lock); + g_static_rw_lock_reader_unlock (&demux->metadata_lock); GST_DEBUG_OBJECT (pad, "Returning duration %" G_GINT64_FORMAT " in format %s", duration, @@ -3746,7 +3772,7 @@ gst_mxf_demux_query (GstElement * element, GstQuery * query) if (demux->src->len == 0) goto done; - g_mutex_lock (demux->metadata_lock); + g_static_rw_lock_reader_lock (&demux->metadata_lock); for (i = 0; i < demux->src->len; i++) { GstMXFDemuxPad *pad = g_ptr_array_index (demux->src, i); gint64 pdur = -1; @@ -3765,7 +3791,7 @@ gst_mxf_demux_query (GstElement * element, GstQuery * query) pad->material_track->edit_rate.n); duration = MAX (duration, pdur); } - g_mutex_unlock (demux->metadata_lock); + g_static_rw_lock_reader_unlock (&demux->metadata_lock); if (duration == -1) { GST_DEBUG_OBJECT (demux, "No duration known (yet)"); @@ -3862,7 +3888,7 @@ gst_mxf_demux_get_property (GObject * object, guint prop_id, case PROP_STRUCTURE:{ GstStructure *s; - g_mutex_lock (demux->metadata_lock); + g_static_rw_lock_reader_lock (&demux->metadata_lock); if (demux->preface) s = mxf_metadata_base_to_structure (MXF_METADATA_BASE (demux->preface)); else @@ -3873,7 +3899,7 @@ gst_mxf_demux_get_property (GObject * object, guint prop_id, if (s) gst_structure_free (s); - g_mutex_unlock (demux->metadata_lock); + g_static_rw_lock_reader_unlock (&demux->metadata_lock); break; } default: @@ -3911,10 +3937,7 @@ gst_mxf_demux_finalize (GObject * object) g_hash_table_destroy (demux->metadata); - if (demux->metadata_lock) { - g_mutex_free (demux->metadata_lock); - demux->metadata_lock = NULL; - } + g_static_rw_lock_free (&demux->metadata_lock); G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -3990,7 +4013,7 @@ gst_mxf_demux_init (GstMXFDemux * demux, GstMXFDemuxClass * g_class) demux->max_drift = 500 * GST_MSECOND; demux->adapter = gst_adapter_new (); - demux->metadata_lock = g_mutex_new (); + g_static_rw_lock_init (&demux->metadata_lock); demux->src = g_ptr_array_new (); demux->essence_tracks = diff --git a/gst/mxf/mxfdemux.h b/gst/mxf/mxfdemux.h index c8439cf6..e8b90261 100644 --- a/gst/mxf/mxfdemux.h +++ b/gst/mxf/mxfdemux.h @@ -154,7 +154,7 @@ struct _GstMXFDemux GArray *random_index_pack; /* Metadata */ - GMutex *metadata_lock; + GStaticRWLock metadata_lock; gboolean update_metadata; gboolean pull_footer_metadata; diff --git a/gst/mxf/mxfmetadata.c b/gst/mxf/mxfmetadata.c index 2237bc60..7d1af0c1 100644 --- a/gst/mxf/mxfmetadata.c +++ b/gst/mxf/mxfmetadata.c @@ -4984,7 +4984,7 @@ gboolean s = gst_caps_get_structure (caps, 0); if (!gst_structure_get_boolean (s, "interlaced", &interlaced) || !interlaced) - self->frame_layout = 1; + self->frame_layout = 0; else self->frame_layout = 3; diff --git a/gst/mxf/mxfmux.c b/gst/mxf/mxfmux.c index 3ca60a4e..fdc1bc61 100644 --- a/gst/mxf/mxfmux.c +++ b/gst/mxf/mxfmux.c @@ -163,6 +163,8 @@ gst_mxf_mux_finalize (GObject * object) if (mux->metadata) { g_hash_table_destroy (mux->metadata); mux->metadata = NULL; + g_list_free (mux->metadata_list); + mux->metadata_list = NULL; } gst_object_unref (mux->collect); @@ -216,6 +218,8 @@ gst_mxf_mux_reset (GstMXFMux * mux) if (mux->metadata) { g_hash_table_destroy (mux->metadata); mux->preface = NULL; + g_list_free (mux->metadata_list); + mux->metadata_list = NULL; } mux->metadata = mxf_metadata_hash_table_new (); @@ -280,6 +284,7 @@ gst_mxf_mux_setcaps (GstPad * pad, GstCaps * caps) gboolean ret = TRUE; MXFUUID d_instance_uid = { {0,} }; MXFMetadataFileDescriptor *old_descriptor = cpad->descriptor; + GList *l; GST_DEBUG_OBJECT (pad, "Setting caps %" GST_PTR_FORMAT, caps); @@ -309,6 +314,19 @@ gst_mxf_mux_setcaps (GstPad * pad, GstCaps * caps) memcpy (&MXF_METADATA_BASE (cpad->descriptor)->instance_uid, &d_instance_uid, 16); + if (old_descriptor) { + for (l = mux->metadata_list; l; l = l->next) { + MXFMetadataBase *tmp = l->data; + + if (mxf_uuid_is_equal (&d_instance_uid, &tmp->instance_uid)) { + l->data = cpad->descriptor; + break; + } + } + } else { + mux->metadata_list = g_list_prepend (mux->metadata_list, cpad->descriptor); + } + g_hash_table_replace (mux->metadata, &MXF_METADATA_BASE (cpad->descriptor)->instance_uid, cpad->descriptor); @@ -449,6 +467,7 @@ gst_mxf_mux_create_metadata (GstMXFMux * mux) mux->metadata); g_hash_table_insert (mux->metadata, &MXF_METADATA_BASE (mux->preface)->instance_uid, mux->preface); + mux->metadata_list = g_list_prepend (mux->metadata_list, mux->preface); mxf_timestamp_set_now (&mux->preface->last_modified_date); mux->preface->version = 258; @@ -505,6 +524,7 @@ gst_mxf_mux_create_metadata (GstMXFMux * mux) mux->metadata); g_hash_table_insert (mux->metadata, &MXF_METADATA_BASE (identification)->instance_uid, identification); + mux->metadata_list = g_list_prepend (mux->metadata_list, identification); mxf_uuid_init (&identification->this_generation_uid, NULL); @@ -561,6 +581,7 @@ gst_mxf_mux_create_metadata (GstMXFMux * mux) mxf_uuid_init (&MXF_METADATA_BASE (cstorage)->instance_uid, mux->metadata); g_hash_table_insert (mux->metadata, &MXF_METADATA_BASE (cstorage)->instance_uid, cstorage); + mux->metadata_list = g_list_prepend (mux->metadata_list, cstorage); cstorage->n_packages = 2; cstorage->packages = g_new0 (MXFMetadataGenericPackage *, 2); @@ -576,6 +597,8 @@ gst_mxf_mux_create_metadata (GstMXFMux * mux) g_hash_table_insert (mux->metadata, &MXF_METADATA_BASE (cstorage->packages[1])->instance_uid, cstorage->packages[1]); + mux->metadata_list = + g_list_prepend (mux->metadata_list, cstorage->packages[1]); p = (MXFMetadataSourcePackage *) cstorage->packages[1]; mxf_umid_init (&p->parent.package_uid); @@ -601,6 +624,7 @@ gst_mxf_mux_create_metadata (GstMXFMux * mux) mxf_uuid_init (&MXF_METADATA_BASE (d)->instance_uid, mux->metadata); g_hash_table_insert (mux->metadata, &MXF_METADATA_BASE (d)->instance_uid, d); + mux->metadata_list = g_list_prepend (mux->metadata_list, d); } /* Tracks */ @@ -621,6 +645,7 @@ gst_mxf_mux_create_metadata (GstMXFMux * mux) mux->metadata); g_hash_table_insert (mux->metadata, &MXF_METADATA_BASE (track)->instance_uid, track); + mux->metadata_list = g_list_prepend (mux->metadata_list, track); track->parent.track_id = n + 1; track->parent.track_number = @@ -637,6 +662,7 @@ gst_mxf_mux_create_metadata (GstMXFMux * mux) mux->metadata); g_hash_table_insert (mux->metadata, &MXF_METADATA_BASE (sequence)->instance_uid, sequence); + mux->metadata_list = g_list_prepend (mux->metadata_list, sequence); memcpy (&sequence->data_definition, &cpad->writer->data_definition, 16); @@ -653,6 +679,7 @@ gst_mxf_mux_create_metadata (GstMXFMux * mux) mux->metadata); g_hash_table_insert (mux->metadata, &MXF_METADATA_BASE (clip)->instance_uid, clip); + mux->metadata_list = g_list_prepend (mux->metadata_list, clip); memcpy (&clip->parent.data_definition, &sequence->data_definition, 16); @@ -687,6 +714,8 @@ gst_mxf_mux_create_metadata (GstMXFMux * mux) g_hash_table_insert (mux->metadata, &MXF_METADATA_BASE (cstorage->packages[0])->instance_uid, cstorage->packages[0]); + mux->metadata_list = + g_list_prepend (mux->metadata_list, cstorage->packages[0]); p = (MXFMetadataMaterialPackage *) cstorage->packages[0]; mxf_umid_init (&p->package_uid); @@ -724,6 +753,7 @@ gst_mxf_mux_create_metadata (GstMXFMux * mux) mux->metadata); g_hash_table_insert (mux->metadata, &MXF_METADATA_BASE (track)->instance_uid, track); + mux->metadata_list = g_list_prepend (mux->metadata_list, track); track->parent.track_id = n + 1; track->parent.track_number = 0; @@ -757,6 +787,7 @@ gst_mxf_mux_create_metadata (GstMXFMux * mux) mux->metadata); g_hash_table_insert (mux->metadata, &MXF_METADATA_BASE (sequence)->instance_uid, sequence); + mux->metadata_list = g_list_prepend (mux->metadata_list, sequence); memcpy (&sequence->data_definition, &cpad->writer->data_definition, 16); @@ -772,6 +803,7 @@ gst_mxf_mux_create_metadata (GstMXFMux * mux) mux->metadata); g_hash_table_insert (mux->metadata, &MXF_METADATA_BASE (clip)->instance_uid, clip); + mux->metadata_list = g_list_prepend (mux->metadata_list, clip); memcpy (&clip->parent.data_definition, &sequence->data_definition, 16); @@ -798,6 +830,7 @@ gst_mxf_mux_create_metadata (GstMXFMux * mux) mux->metadata); g_hash_table_insert (mux->metadata, &MXF_METADATA_BASE (track)->instance_uid, track); + mux->metadata_list = g_list_prepend (mux->metadata_list, track); track->parent.track_id = n + 1; track->parent.track_number = 0; @@ -811,6 +844,7 @@ gst_mxf_mux_create_metadata (GstMXFMux * mux) mux->metadata); g_hash_table_insert (mux->metadata, &MXF_METADATA_BASE (sequence)->instance_uid, sequence); + mux->metadata_list = g_list_prepend (mux->metadata_list, sequence); memcpy (&sequence->data_definition, mxf_metadata_track_identifier_get @@ -828,6 +862,7 @@ gst_mxf_mux_create_metadata (GstMXFMux * mux) mux->metadata); g_hash_table_insert (mux->metadata, &MXF_METADATA_BASE (component)->instance_uid, component); + mux->metadata_list = g_list_prepend (mux->metadata_list, component); memcpy (&component->parent.data_definition, &sequence->data_definition, 16); @@ -884,6 +919,9 @@ gst_mxf_mux_create_metadata (GstMXFMux * mux) g_hash_table_insert (mux->metadata, &MXF_METADATA_BASE (cstorage->essence_container_data[0])->instance_uid, cstorage->essence_container_data[0]); + mux->metadata_list = + g_list_prepend (mux->metadata_list, + cstorage->essence_container_data[0]); cstorage->essence_container_data[0]->linked_package = MXF_METADATA_SOURCE_PACKAGE (cstorage->packages[1]); @@ -891,6 +929,43 @@ gst_mxf_mux_create_metadata (GstMXFMux * mux) cstorage->essence_container_data[0]->body_sid = 1; } + /* Sort descriptors at the correct places */ + { + GList *l; + GList *descriptors = NULL; + + for (l = mux->metadata_list; l; l = l->next) { + MXFMetadataBase *m = l->data; + + if (MXF_IS_METADATA_GENERIC_DESCRIPTOR (m) + && !MXF_IS_METADATA_MULTIPLE_DESCRIPTOR (m)) { + descriptors = l; + l->prev->next = NULL; + l->prev = NULL; + break; + } + } + + g_assert (descriptors != NULL); + + for (l = mux->metadata_list; l; l = l->next) { + MXFMetadataBase *m = l->data; + GList *s; + + if (MXF_IS_METADATA_MULTIPLE_DESCRIPTOR (m) || + MXF_IS_METADATA_SOURCE_PACKAGE (m)) { + s = l->prev; + l->prev = g_list_last (descriptors); + s->next = descriptors; + descriptors->prev = s; + l->prev->next = l; + break; + } + } + } + + mux->metadata_list = g_list_reverse (mux->metadata_list); + return ret; } @@ -953,38 +1028,17 @@ gst_mxf_mux_write_header_metadata (GstMXFMux * mux) GstFlowReturn ret = GST_FLOW_OK; GstBuffer *buf; GList *buffers = NULL; -#if GLIB_CHECK_VERSION (2, 16, 0) - GHashTableIter iter; -#else - GList *values; -#endif - MXFMetadataBase *m; GList *l; + MXFMetadataBase *m; guint64 header_byte_count = 0; - buf = - mxf_metadata_base_to_buffer (MXF_METADATA_BASE (mux->preface), - &mux->primer); - header_byte_count += GST_BUFFER_SIZE (buf); - buffers = g_list_prepend (buffers, buf); - -#if GLIB_CHECK_VERSION (2, 16, 0) - g_hash_table_iter_init (&iter, mux->metadata); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) & m)) { -#else - values = g_hash_table_get_values (mux->metadata); - for (l = values; l; l = l->next) { + for (l = mux->metadata_list; l; l = l->next) { m = l->data; -#endif buf = mxf_metadata_base_to_buffer (m, &mux->primer); header_byte_count += GST_BUFFER_SIZE (buf); buffers = g_list_prepend (buffers, buf); } -#if !GLIB_CHECK_VERSION (2, 16, 0) - g_list_free (values); -#endif - buffers = g_list_reverse (buffers); buf = mxf_primer_pack_to_buffer (&mux->primer); header_byte_count += GST_BUFFER_SIZE (buf); @@ -1006,7 +1060,7 @@ gst_mxf_mux_write_header_metadata (GstMXFMux * mux) if ((ret = gst_mxf_mux_push (mux, buf)) != GST_FLOW_OK) { GST_ERROR_OBJECT (mux, "Failed pushing buffer: %s", gst_flow_get_name (ret)); - g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL); + g_list_foreach (l, (GFunc) gst_mini_object_unref, NULL); g_list_free (buffers); return ret; } @@ -1150,10 +1204,11 @@ gst_mxf_mux_handle_eos (GstMXFMux * mux) best = cpad; break; } - } else if (have_data && !l->next) { + } + + if (have_data && !l->next) { mux->last_gc_position++; mux->last_gc_timestamp = next_gc_timestamp; - have_data = FALSE; best = NULL; break; } diff --git a/gst/mxf/mxfmux.h b/gst/mxf/mxfmux.h index c8fd0b09..94330c46 100644 --- a/gst/mxf/mxfmux.h +++ b/gst/mxf/mxfmux.h @@ -85,6 +85,7 @@ typedef struct _GstMXFMux { MXFPrimerPack primer; GHashTable *metadata; + GList *metadata_list; MXFMetadataPreface *preface; MXFFraction min_edit_rate; diff --git a/gst/qtmux/atoms.c b/gst/qtmux/atoms.c index df6958e0..a5d8fe38 100644 --- a/gst/qtmux/atoms.c +++ b/gst/qtmux/atoms.c @@ -1855,13 +1855,11 @@ atom_stsd_copy_data (AtomSTSD * stsd, guint8 ** buffer, guint64 * size, break; default: if (se->kind == VIDEO) { - size += - sample_entry_mp4v_copy_data ((SampleTableEntryMP4V *) walker-> - data, buffer, size, offset); + size += sample_entry_mp4v_copy_data ((SampleTableEntryMP4V *) + walker->data, buffer, size, offset); } else if (se->kind == AUDIO) { - size += - sample_entry_mp4a_copy_data ((SampleTableEntryMP4A *) walker-> - data, buffer, size, offset); + size += sample_entry_mp4a_copy_data ((SampleTableEntryMP4A *) + walker->data, buffer, size, offset); } else { if (!atom_hint_sample_entry_copy_data ( (AtomHintSampleEntry *) walker->data, buffer, size, offset)) { @@ -2547,6 +2545,7 @@ atom_moov_append_tag (AtomMOOV * moov, AtomInfo * tag) { AtomILST *ilst; + atom_moov_init_metatags (moov); ilst = moov->udta->meta->ilst; ilst->entries = g_list_append (ilst->entries, tag); } @@ -2563,7 +2562,6 @@ atom_moov_add_tag (AtomMOOV * moov, guint32 fourcc, guint32 flags, atom_tag_data_alloc_data (tdata, size); g_memmove (tdata->data, data, size); - atom_moov_init_metatags (moov); atom_moov_append_tag (moov, build_atom_info_wrapper ((Atom *) tag, atom_tag_copy_data, atom_tag_free)); diff --git a/gst/qtmux/gstqtmux.c b/gst/qtmux/gstqtmux.c index f3e85f5c..b0df9d71 100644 --- a/gst/qtmux/gstqtmux.c +++ b/gst/qtmux/gstqtmux.c @@ -1886,7 +1886,7 @@ gst_qt_mux_register (GstPlugin * plugin) g_type_set_qdata (type, GST_QT_MUX_PARAMS_QDATA, (gpointer) params); g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info); - if (!gst_element_register (plugin, prop->name, GST_RANK_NONE, type)) + if (!gst_element_register (plugin, prop->name, GST_RANK_PRIMARY, type)) return FALSE; i++; diff --git a/gst/qtmux/gstqtmuxmap.c b/gst/qtmux/gstqtmuxmap.c index a4972661..11913670 100644 --- a/gst/qtmux/gstqtmuxmap.c +++ b/gst/qtmux/gstqtmuxmap.c @@ -174,7 +174,7 @@ GstQTMuxFormatProp gst_qt_mux_format_list[] = { "3GPP", "GstGPPMux", GST_STATIC_CAPS ("video/quicktime, variant = (string) 3gpp"), - GST_STATIC_CAPS (H263_CAPS "; " H264_CAPS), + GST_STATIC_CAPS (H263_CAPS "; " MPEG4V_CAPS "; " H264_CAPS), GST_STATIC_CAPS (AMR_CAPS "; " MP3_CAPS "; " AAC_CAPS) } , diff --git a/gst/rtpmanager/gstrtpbin-marshal.list b/gst/rtpmanager/gstrtpbin-marshal.list index c4bc0bb2..ed73e43b 100644 --- a/gst/rtpmanager/gstrtpbin-marshal.list +++ b/gst/rtpmanager/gstrtpbin-marshal.list @@ -3,5 +3,6 @@ BOXED:UINT BOXED:UINT,UINT OBJECT:UINT VOID:UINT,OBJECT +VOID:UINT VOID:UINT,UINT VOID:OBJECT,OBJECT diff --git a/gst/rtpmanager/gstrtpbin.c b/gst/rtpmanager/gstrtpbin.c index 7d3b9823..19de4f1b 100644 --- a/gst/rtpmanager/gstrtpbin.c +++ b/gst/rtpmanager/gstrtpbin.c @@ -297,12 +297,17 @@ struct _GstRtpBinStream /* the jitterbuffer of the SSRC */ GstElement *buffer; + gulong buffer_handlesync_sig; + gulong buffer_ptreq_sig; + gulong buffer_ntpstop_sig; /* the PT demuxer of the SSRC */ GstElement *demux; gulong demux_newpad_sig; gulong demux_ptreq_sig; gulong demux_pt_change_sig; + /* ghostpads from the ptdemuxer */ + GSList *pads; /* if we have calculated a valid unix_delta for this stream */ gboolean have_sync; @@ -332,6 +337,7 @@ struct _GstRtpBinSession /* the SSRC demuxer */ GstElement *demux; gulong demux_newpad_sig; + gulong demux_padremoved_sig; GMutex *lock; @@ -343,12 +349,17 @@ struct _GstRtpBinSession /* the pads of the session */ GstPad *recv_rtp_sink; + GstPad *recv_rtp_sink_ghost; GstPad *recv_rtp_src; GstPad *recv_rtcp_sink; + GstPad *recv_rtcp_sink_ghost; GstPad *sync_src; GstPad *send_rtp_sink; + GstPad *send_rtp_sink_ghost; GstPad *send_rtp_src; + GstPad *send_rtp_src_ghost; GstPad *send_rtcp_src; + GstPad *send_rtcp_src_ghost; }; /* Manages the RTP streams that come from one client and should therefore be @@ -389,9 +400,10 @@ find_session_by_pad (GstRtpBin * rtpbin, GstPad * pad) for (walk = rtpbin->sessions; walk; walk = g_slist_next (walk)) { GstRtpBinSession *sess = (GstRtpBinSession *) walk->data; - if ((sess->recv_rtp_sink == pad) || - (sess->recv_rtcp_sink == pad) || - (sess->send_rtp_sink == pad) || (sess->send_rtcp_src == pad)) + if ((sess->recv_rtp_sink_ghost == pad) || + (sess->recv_rtcp_sink_ghost == pad) || + (sess->send_rtp_sink_ghost == pad) + || (sess->send_rtcp_src_ghost == pad)) return sess; } return NULL; @@ -467,6 +479,36 @@ on_npt_stop (GstElement * jbuf, GstRtpBinStream * stream) stream->session->id, stream->ssrc); } +/* must be called with the SESSION lock */ +static GstRtpBinStream * +find_stream_by_ssrc (GstRtpBinSession * session, guint32 ssrc) +{ + GSList *walk; + + for (walk = session->streams; walk; walk = g_slist_next (walk)) { + GstRtpBinStream *stream = (GstRtpBinStream *) walk->data; + + if (stream->ssrc == ssrc) + return stream; + } + return NULL; +} + +static void +ssrc_demux_pad_removed (GstElement * element, guint ssrc, GstPad * pad, + GstRtpBinSession * session) +{ + GstRtpBinStream *stream = NULL; + + GST_RTP_SESSION_LOCK (session); + if ((stream = find_stream_by_ssrc (session, ssrc))) + session->streams = g_slist_remove (session->streams, stream); + GST_RTP_SESSION_UNLOCK (session); + + if (stream) + free_stream (stream); +} + /* create a session with the given id. Must be called with RTP_BIN_LOCK */ static GstRtpBinSession * create_session (GstRtpBin * rtpbin, gint id) @@ -474,6 +516,7 @@ create_session (GstRtpBin * rtpbin, gint id) GstRtpBinSession *sess; GstElement *session, *demux; gint i; + GstState target; if (!(session = gst_element_factory_make ("gstrtpsession", NULL))) goto no_session; @@ -522,11 +565,16 @@ create_session (GstRtpBin * rtpbin, gint id) g_signal_connect (sess->session, "on-sender-timeout", (GCallback) on_sender_timeout, sess); - /* FIXME, change state only to what's needed */ gst_bin_add (GST_BIN_CAST (rtpbin), session); - gst_element_set_state (session, GST_STATE_PLAYING); gst_bin_add (GST_BIN_CAST (rtpbin), demux); - gst_element_set_state (demux, GST_STATE_PLAYING); + + GST_OBJECT_LOCK (rtpbin); + target = GST_STATE_TARGET (rtpbin); + GST_OBJECT_UNLOCK (rtpbin); + + /* change state only to what's needed */ + gst_element_set_state (demux, target); + gst_element_set_state (session, target); return sess; @@ -545,16 +593,12 @@ no_demux: } static void -free_session (GstRtpBinSession * sess) +free_session (GstRtpBinSession * sess, GstRtpBin * bin) { - GstRtpBin *bin; - - bin = sess->bin; - GST_DEBUG_OBJECT (bin, "freeing session %p", sess); - gst_element_set_state (sess->session, GST_STATE_NULL); gst_element_set_state (sess->demux, GST_STATE_NULL); + gst_element_set_state (sess->session, GST_STATE_NULL); if (sess->recv_rtp_sink != NULL) { gst_element_release_request_pad (sess->session, sess->recv_rtp_sink); @@ -588,27 +632,9 @@ free_session (GstRtpBinSession * sess) g_mutex_free (sess->lock); g_hash_table_destroy (sess->ptmap); - bin->sessions = g_slist_remove (bin->sessions, sess); - g_free (sess); } -#if 0 -static GstRtpBinStream * -find_stream_by_ssrc (GstRtpBinSession * session, guint32 ssrc) -{ - GSList *walk; - - for (walk = session->streams; walk; walk = g_slist_next (walk)) { - GstRtpBinStream *stream = (GstRtpBinStream *) walk->data; - - if (stream->ssrc == ssrc) - return stream; - } - return NULL; -} -#endif - /* get the payload type caps for the specific payload @pt in @session */ static GstCaps * get_pt_map (GstRtpBinSession * session, guint pt) @@ -822,8 +848,9 @@ get_client (GstRtpBin * bin, guint8 len, guint8 * data, gboolean * created) } static void -free_client (GstRtpBinClient * client) +free_client (GstRtpBinClient * client, GstRtpBin * bin) { + GST_DEBUG_OBJECT (bin, "freeing client %p", client); g_slist_free (client->streams); g_free (client->cname); g_free (client); @@ -1079,6 +1106,8 @@ create_stream (GstRtpBinSession * session, guint32 ssrc) { GstElement *buffer, *demux; GstRtpBinStream *stream; + GstRtpBin *rtpbin; + GstState target; if (!(buffer = gst_element_factory_make ("gstrtpjitterbuffer", NULL))) goto no_jitterbuffer; @@ -1086,9 +1115,11 @@ create_stream (GstRtpBinSession * session, guint32 ssrc) if (!(demux = gst_element_factory_make ("gstrtpptdemux", NULL))) goto no_demux; + rtpbin = session->bin; + stream = g_new0 (GstRtpBinStream, 1); stream->ssrc = ssrc; - stream->bin = session->bin; + stream->bin = rtpbin; stream->session = session; stream->buffer = buffer; stream->demux = demux; @@ -1097,22 +1128,29 @@ create_stream (GstRtpBinSession * session, guint32 ssrc) session->streams = g_slist_prepend (session->streams, stream); /* provide clock_rate to the jitterbuffer when needed */ - g_signal_connect (buffer, "request-pt-map", + stream->buffer_ptreq_sig = g_signal_connect (buffer, "request-pt-map", (GCallback) pt_map_requested, session); - g_signal_connect (buffer, "on-npt-stop", (GCallback) on_npt_stop, stream); + stream->buffer_ntpstop_sig = g_signal_connect (buffer, "on-npt-stop", + (GCallback) on_npt_stop, stream); /* configure latency and packet lost */ - g_object_set (buffer, "latency", session->bin->latency, NULL); - g_object_set (buffer, "do-lost", session->bin->do_lost, NULL); + g_object_set (buffer, "latency", rtpbin->latency, NULL); + g_object_set (buffer, "do-lost", rtpbin->do_lost, NULL); - gst_bin_add (GST_BIN_CAST (session->bin), buffer); - gst_element_set_state (buffer, GST_STATE_PLAYING); - gst_bin_add (GST_BIN_CAST (session->bin), demux); - gst_element_set_state (demux, GST_STATE_PLAYING); + gst_bin_add (GST_BIN_CAST (rtpbin), demux); + gst_bin_add (GST_BIN_CAST (rtpbin), buffer); /* link stuff */ gst_element_link (buffer, demux); + GST_OBJECT_LOCK (rtpbin); + target = GST_STATE_TARGET (rtpbin); + GST_OBJECT_UNLOCK (rtpbin); + + /* from sink to source */ + gst_element_set_state (demux, target); + gst_element_set_state (buffer, target); + return stream; /* ERRORS */ @@ -1133,16 +1171,29 @@ static void free_stream (GstRtpBinStream * stream) { GstRtpBinSession *session; + GSList *walk; session = stream->session; - gst_element_set_state (stream->buffer, GST_STATE_NULL); + g_signal_handler_disconnect (stream->demux, stream->demux_newpad_sig); + g_signal_handler_disconnect (stream->demux, stream->demux_ptreq_sig); + g_signal_handler_disconnect (stream->buffer, stream->buffer_handlesync_sig); + g_signal_handler_disconnect (stream->buffer, stream->buffer_ptreq_sig); + g_signal_handler_disconnect (stream->buffer, stream->buffer_ntpstop_sig); + gst_element_set_state (stream->demux, GST_STATE_NULL); + gst_element_set_state (stream->buffer, GST_STATE_NULL); gst_bin_remove (GST_BIN_CAST (session->bin), stream->buffer); gst_bin_remove (GST_BIN_CAST (session->bin), stream->demux); - session->streams = g_slist_remove (session->streams, stream); + for (walk = stream->pads; walk; walk = g_slist_next (walk)) { + GstPad *gpad = GST_PAD_CAST (walk->data); + + gst_pad_set_active (gpad, FALSE); + gst_element_remove_pad (GST_ELEMENT_CAST (session->bin), gpad); + } + g_slist_free (stream->pads); g_free (stream); } @@ -1481,11 +1532,11 @@ gst_rtp_bin_dispose (GObject * object) rtpbin = GST_RTP_BIN (object); GST_DEBUG_OBJECT (object, "freeing sessions"); - g_slist_foreach (rtpbin->sessions, (GFunc) free_session, NULL); + g_slist_foreach (rtpbin->sessions, (GFunc) free_session, rtpbin); g_slist_free (rtpbin->sessions); rtpbin->sessions = NULL; GST_DEBUG_OBJECT (object, "freeing clients"); - g_slist_foreach (rtpbin->clients, (GFunc) free_client, NULL); + g_slist_foreach (rtpbin->clients, (GFunc) free_client, rtpbin); g_slist_free (rtpbin->clients); rtpbin->clients = NULL; @@ -1556,6 +1607,8 @@ gst_rtp_bin_set_sdes_string (GstRtpBin * bin, GstRTCPSDESType type, if (type < 0 || type > 8) return; + GST_RTP_BIN_LOCK (bin); + GST_OBJECT_LOCK (bin); g_free (bin->sdes[type]); bin->sdes[type] = g_strdup (data); @@ -1564,6 +1617,8 @@ gst_rtp_bin_set_sdes_string (GstRtpBin * bin, GstRTCPSDESType type, for (item = bin->sessions; item; item = g_slist_next (item)) g_object_set (item->data, name, bin->sdes[type], NULL); GST_OBJECT_UNLOCK (bin); + + GST_RTP_BIN_UNLOCK (bin); } static gchar * @@ -1836,6 +1891,8 @@ new_payload_found (GstElement * element, guint pt, GstPad * pad, gst_pad_set_active (gpad, TRUE); gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), gpad); + stream->pads = g_slist_prepend (stream->pads, gpad); + GST_RTP_BIN_SHUTDOWN_UNLOCK (rtpbin); return; @@ -1947,7 +2004,7 @@ new_ssrc_pad_found (GstElement * element, guint ssrc, GstPad * pad, /* connect to the RTCP sync signal from the jitterbuffer */ GST_DEBUG_OBJECT (rtpbin, "connecting sync signal"); - g_signal_connect (stream->buffer, + stream->buffer_handlesync_sig = g_signal_connect (stream->buffer, "handle-sync", (GCallback) gst_rtp_bin_handle_sync, stream); /* connect to the new-pad signal of the payload demuxer, this will expose the @@ -1986,7 +2043,7 @@ no_stream: static GstPad * create_recv_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) { - GstPad *result, *sinkdpad; + GstPad *sinkdpad; guint sessid; GstRtpBinSession *session; GstPadLinkReturn lres; @@ -2008,8 +2065,8 @@ create_recv_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) } /* check if pad was requested */ - if (session->recv_rtp_sink != NULL) - goto existed; + if (session->recv_rtp_sink_ghost != NULL) + return session->recv_rtp_sink_ghost; GST_DEBUG_OBJECT (rtpbin, "getting RTP sink pad"); /* get recv_rtp pad and store */ @@ -2039,14 +2096,16 @@ create_recv_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) /* connect to the new-ssrc-pad signal of the SSRC demuxer */ session->demux_newpad_sig = g_signal_connect (session->demux, "new-ssrc-pad", (GCallback) new_ssrc_pad_found, session); + session->demux_padremoved_sig = g_signal_connect (session->demux, + "removed-ssrc-pad", (GCallback) ssrc_demux_pad_removed, session); GST_DEBUG_OBJECT (rtpbin, "ghosting session sink pad"); - result = + session->recv_rtp_sink_ghost = gst_ghost_pad_new_from_template (name, session->recv_rtp_sink, templ); - gst_pad_set_active (result, TRUE); - gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), result); + gst_pad_set_active (session->recv_rtp_sink_ghost, TRUE); + gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->recv_rtp_sink_ghost); - return result; + return session->recv_rtp_sink_ghost; /* ERRORS */ no_name: @@ -2059,12 +2118,6 @@ create_error: /* create_session already warned */ return NULL; } -existed: - { - g_warning ("gstrtpbin: recv_rtp pad already requested for session %d", - sessid); - return NULL; - } pad_failed: { g_warning ("gstrtpbin: failed to get session pad"); @@ -2078,10 +2131,31 @@ link_failed: } static void -remove_recv_rtp (GstRtpBin * rtpbin, GstRtpBinSession * session, GstPad * pad) +remove_recv_rtp (GstRtpBin * rtpbin, GstRtpBinSession * session) { - g_warning ("gstrtpbin: releasing pad %s:%s is not implemented", - GST_DEBUG_PAD_NAME (pad)); + if (session->demux_newpad_sig) { + g_signal_handler_disconnect (session->demux, session->demux_newpad_sig); + session->demux_newpad_sig = 0; + } + if (session->demux_padremoved_sig) { + g_signal_handler_disconnect (session->demux, session->demux_padremoved_sig); + session->demux_padremoved_sig = 0; + } + if (session->recv_rtp_src) { + gst_object_unref (session->recv_rtp_src); + session->recv_rtp_src = NULL; + } + if (session->recv_rtp_sink) { + gst_element_release_request_pad (session->session, session->recv_rtp_sink); + gst_object_unref (session->recv_rtp_sink); + session->recv_rtp_sink = NULL; + } + if (session->recv_rtp_sink_ghost) { + gst_pad_set_active (session->recv_rtp_sink_ghost, FALSE); + gst_element_remove_pad (GST_ELEMENT_CAST (rtpbin), + session->recv_rtp_sink_ghost); + session->recv_rtp_sink_ghost = NULL; + } } /* Create a pad for receiving RTCP for the session in @name. Must be called with @@ -2091,7 +2165,6 @@ static GstPad * create_recv_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) { - GstPad *result; guint sessid; GstRtpBinSession *session; GstPad *sinkdpad; @@ -2114,8 +2187,8 @@ create_recv_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, } /* check if pad was requested */ - if (session->recv_rtcp_sink != NULL) - goto existed; + if (session->recv_rtcp_sink_ghost != NULL) + return session->recv_rtcp_sink_ghost; /* get recv_rtp pad and store */ GST_DEBUG_OBJECT (rtpbin, "getting RTCP sink pad"); @@ -2137,12 +2210,13 @@ create_recv_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, if (lres != GST_PAD_LINK_OK) goto link_failed; - result = + session->recv_rtcp_sink_ghost = gst_ghost_pad_new_from_template (name, session->recv_rtcp_sink, templ); - gst_pad_set_active (result, TRUE); - gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), result); + gst_pad_set_active (session->recv_rtcp_sink_ghost, TRUE); + gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), + session->recv_rtcp_sink_ghost); - return result; + return session->recv_rtcp_sink_ghost; /* ERRORS */ no_name: @@ -2155,12 +2229,6 @@ create_error: /* create_session already warned */ return NULL; } -existed: - { - g_warning ("gstrtpbin: recv_rtcp pad already requested for session %d", - sessid); - return NULL; - } pad_failed: { g_warning ("gstrtpbin: failed to get session pad"); @@ -2174,10 +2242,24 @@ link_failed: } static void -remove_recv_rtcp (GstRtpBin * rtpbin, GstRtpBinSession * session, GstPad * pad) +remove_recv_rtcp (GstRtpBin * rtpbin, GstRtpBinSession * session) { - g_warning ("gstrtpbin: releasing pad %s:%s is not implemented", - GST_DEBUG_PAD_NAME (pad)); + if (session->recv_rtcp_sink_ghost) { + gst_pad_set_active (session->recv_rtcp_sink_ghost, FALSE); + gst_element_remove_pad (GST_ELEMENT_CAST (rtpbin), + session->recv_rtcp_sink_ghost); + session->recv_rtcp_sink_ghost = NULL; + } + if (session->sync_src) { + /* releasing the request pad should also unref the sync pad */ + gst_object_unref (session->sync_src); + session->sync_src = NULL; + } + if (session->recv_rtcp_sink) { + gst_element_release_request_pad (session->session, session->recv_rtcp_sink); + gst_object_unref (session->recv_rtcp_sink); + session->recv_rtcp_sink = NULL; + } } /* Create a pad for sending RTP for the session in @name. Must be called with @@ -2186,7 +2268,6 @@ remove_recv_rtcp (GstRtpBin * rtpbin, GstRtpBinSession * session, GstPad * pad) static GstPad * create_send_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) { - GstPad *result, *srcghost; gchar *gname; guint sessid; GstRtpBinSession *session; @@ -2206,8 +2287,8 @@ create_send_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) } /* check if pad was requested */ - if (session->send_rtp_sink != NULL) - goto existed; + if (session->send_rtp_sink_ghost != NULL) + return session->send_rtp_sink_ghost; /* get send_rtp pad and store */ session->send_rtp_sink = @@ -2215,10 +2296,10 @@ create_send_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) if (session->send_rtp_sink == NULL) goto pad_failed; - result = + session->send_rtp_sink_ghost = gst_ghost_pad_new_from_template (name, session->send_rtp_sink, templ); - gst_pad_set_active (result, TRUE); - gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), result); + gst_pad_set_active (session->send_rtp_sink_ghost, TRUE); + gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->send_rtp_sink_ghost); /* get srcpad */ session->send_rtp_src = @@ -2230,13 +2311,13 @@ create_send_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) klass = GST_ELEMENT_GET_CLASS (rtpbin); gname = g_strdup_printf ("send_rtp_src_%d", sessid); templ = gst_element_class_get_pad_template (klass, "send_rtp_src_%d"); - srcghost = + session->send_rtp_src_ghost = gst_ghost_pad_new_from_template (gname, session->send_rtp_src, templ); - gst_pad_set_active (srcghost, TRUE); - gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), srcghost); + gst_pad_set_active (session->send_rtp_src_ghost, TRUE); + gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->send_rtp_src_ghost); g_free (gname); - return result; + return session->send_rtp_sink_ghost; /* ERRORS */ no_name: @@ -2249,12 +2330,6 @@ create_error: /* create_session already warned */ return NULL; } -existed: - { - g_warning ("gstrtpbin: send_rtp pad already requested for session %d", - sessid); - return NULL; - } pad_failed: { g_warning ("gstrtpbin: failed to get session pad for session %d", sessid); @@ -2269,10 +2344,30 @@ no_srcpad: } static void -remove_send_rtp (GstRtpBin * rtpbin, GstRtpBinSession * session, GstPad * pad) +remove_send_rtp (GstRtpBin * rtpbin, GstRtpBinSession * session) { - g_warning ("gstrtpbin: releasing pad %s:%s is not implemented", - GST_DEBUG_PAD_NAME (pad)); + if (session->send_rtp_src_ghost) { + gst_pad_set_active (session->send_rtp_src_ghost, FALSE); + gst_element_remove_pad (GST_ELEMENT_CAST (rtpbin), + session->send_rtp_src_ghost); + session->send_rtp_src_ghost = NULL; + } + if (session->send_rtp_src) { + gst_object_unref (session->send_rtp_src); + session->send_rtp_src = NULL; + } + if (session->send_rtp_sink) { + gst_element_release_request_pad (GST_ELEMENT_CAST (session->session), + session->send_rtp_sink); + gst_object_unref (session->send_rtp_sink); + session->send_rtp_sink = NULL; + } + if (session->send_rtp_sink_ghost) { + gst_pad_set_active (session->send_rtp_sink_ghost, FALSE); + gst_element_remove_pad (GST_ELEMENT_CAST (rtpbin), + session->send_rtp_sink_ghost); + session->send_rtp_sink_ghost = NULL; + } } /* Create a pad for sending RTCP for the session in @name. Must be called with @@ -2281,7 +2376,6 @@ remove_send_rtp (GstRtpBin * rtpbin, GstRtpBinSession * session, GstPad * pad) static GstPad * create_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) { - GstPad *result; guint sessid; GstRtpBinSession *session; @@ -2295,8 +2389,8 @@ create_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) goto no_session; /* check if pad was requested */ - if (session->send_rtcp_src != NULL) - goto existed; + if (session->send_rtcp_src_ghost != NULL) + return session->send_rtcp_src_ghost; /* get rtcp_src pad and store */ session->send_rtcp_src = @@ -2304,12 +2398,12 @@ create_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name) if (session->send_rtcp_src == NULL) goto pad_failed; - result = + session->send_rtcp_src_ghost = gst_ghost_pad_new_from_template (name, session->send_rtcp_src, templ); - gst_pad_set_active (result, TRUE); - gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), result); + gst_pad_set_active (session->send_rtcp_src_ghost, TRUE); + gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->send_rtcp_src_ghost); - return result; + return session->send_rtcp_src_ghost; /* ERRORS */ no_name: @@ -2322,12 +2416,6 @@ no_session: g_warning ("gstrtpbin: session with id %d does not exist", sessid); return NULL; } -existed: - { - g_warning ("gstrtpbin: send_rtcp_src pad already requested for session %d", - sessid); - return NULL; - } pad_failed: { g_warning ("gstrtpbin: failed to get rtcp pad for session %d", sessid); @@ -2336,10 +2424,19 @@ pad_failed: } static void -remove_rtcp (GstRtpBin * rtpbin, GstRtpBinSession * session, GstPad * pad) +remove_rtcp (GstRtpBin * rtpbin, GstRtpBinSession * session) { - g_warning ("gstrtpbin: releasing pad %s:%s is not implemented", - GST_DEBUG_PAD_NAME (pad)); + if (session->send_rtcp_src_ghost) { + gst_pad_set_active (session->send_rtcp_src_ghost, FALSE); + gst_element_remove_pad (GST_ELEMENT_CAST (rtpbin), + session->send_rtcp_src_ghost); + session->send_rtcp_src_ghost = NULL; + } + if (session->send_rtcp_src) { + gst_element_release_request_pad (session->session, session->send_rtcp_src); + gst_object_unref (session->send_rtcp_src); + session->send_rtcp_src = NULL; + } } /* If the requested name is NULL we should create a name with @@ -2403,7 +2500,7 @@ gst_rtp_bin_request_new_pad (GstElement * element, pad_name = g_strdup (name); } - GST_DEBUG ("Trying to request a pad with name %s", pad_name); + GST_DEBUG_OBJECT (rtpbin, "Trying to request a pad with name %s", pad_name); /* figure out the template */ if (templ == gst_element_class_get_pad_template (klass, "recv_rtp_sink_%d")) { @@ -2440,32 +2537,39 @@ gst_rtp_bin_release_pad (GstElement * element, GstPad * pad) { GstRtpBinSession *session; GstRtpBin *rtpbin; - GstPad *target = NULL; g_return_if_fail (GST_IS_GHOST_PAD (pad)); g_return_if_fail (GST_IS_RTP_BIN (element)); rtpbin = GST_RTP_BIN (element); - target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad)); - g_return_if_fail (target); - GST_RTP_BIN_LOCK (rtpbin); - if (!(session = find_session_by_pad (rtpbin, target))) + GST_DEBUG_OBJECT (rtpbin, "Trying to release pad %s:%s", + GST_DEBUG_PAD_NAME (pad)); + + if (!(session = find_session_by_pad (rtpbin, pad))) goto unknown_pad; - if (session->recv_rtp_sink == target) { - remove_recv_rtp (rtpbin, session, pad); - } else if (session->recv_rtcp_sink == target) { - remove_recv_rtcp (rtpbin, session, pad); - } else if (session->send_rtp_sink == target) { - remove_send_rtp (rtpbin, session, pad); - } else if (session->send_rtcp_src == target) { - remove_rtcp (rtpbin, session, pad); + if (session->recv_rtp_sink_ghost == pad) { + remove_recv_rtp (rtpbin, session); + } else if (session->recv_rtcp_sink_ghost == pad) { + remove_recv_rtcp (rtpbin, session); + } else if (session->send_rtp_sink_ghost == pad) { + remove_send_rtp (rtpbin, session); + } else if (session->send_rtcp_src_ghost == pad) { + remove_rtcp (rtpbin, session); } - GST_RTP_BIN_UNLOCK (rtpbin); - gst_object_unref (target); + /* no more request pads, free the complete session */ + if (session->recv_rtp_sink_ghost == NULL + && session->recv_rtcp_sink_ghost == NULL + && session->send_rtp_sink_ghost == NULL + && session->send_rtcp_src_ghost == NULL) { + GST_DEBUG_OBJECT (rtpbin, "no more pads for session %p", session); + rtpbin->sessions = g_slist_remove (rtpbin->sessions, session); + free_session (session, rtpbin); + } + GST_RTP_BIN_UNLOCK (rtpbin); return; @@ -2473,7 +2577,6 @@ gst_rtp_bin_release_pad (GstElement * element, GstPad * pad) unknown_pad: { GST_RTP_BIN_UNLOCK (rtpbin); - gst_object_unref (target); g_warning ("gstrtpbin: %s:%s is not one of our request pads", GST_DEBUG_PAD_NAME (pad)); return; diff --git a/gst/rtpmanager/gstrtpsession.c b/gst/rtpmanager/gstrtpsession.c index 035d82a8..c33fdfc6 100644 --- a/gst/rtpmanager/gstrtpsession.c +++ b/gst/rtpmanager/gstrtpsession.c @@ -323,63 +323,6 @@ on_ssrc_active (RTPSession * session, RTPSource * src, GstRtpSession * sess) src->ssrc); } -static GstStructure * -source_get_sdes_structure (RTPSource * src) -{ - GstStructure *result; - GValue val = { 0 }; - gchar *str; - - result = gst_structure_empty_new ("GstRTPSessionSDES"); - - gst_structure_set (result, "ssrc", G_TYPE_UINT, src->ssrc, NULL); - - g_value_init (&val, G_TYPE_STRING); - str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_CNAME); - if (str) { - g_value_take_string (&val, str); - gst_structure_set_value (result, "cname", &val); - } - str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_NAME); - if (str) { - g_value_take_string (&val, str); - gst_structure_set_value (result, "name", &val); - } - str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_EMAIL); - if (str) { - g_value_take_string (&val, str); - gst_structure_set_value (result, "email", &val); - } - str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_PHONE); - if (str) { - g_value_take_string (&val, str); - gst_structure_set_value (result, "phone", &val); - } - str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_LOC); - if (str) { - g_value_take_string (&val, str); - gst_structure_set_value (result, "location", &val); - } - str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_TOOL); - if (str) { - g_value_take_string (&val, str); - gst_structure_set_value (result, "tool", &val); - } - str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_NOTE); - if (str) { - g_value_take_string (&val, str); - gst_structure_set_value (result, "note", &val); - } - str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_PRIV); - if (str) { - g_value_take_string (&val, str); - gst_structure_set_value (result, "priv", &val); - } - g_value_unset (&val); - - return result; -} - static void on_ssrc_sdes (RTPSession * session, RTPSource * src, GstRtpSession * sess) { @@ -388,8 +331,9 @@ on_ssrc_sdes (RTPSession * session, RTPSource * src, GstRtpSession * sess) /* convert the new SDES info into a message */ RTP_SESSION_LOCK (session); - s = source_get_sdes_structure (src); + g_object_get (src, "sdes", &s, NULL); RTP_SESSION_UNLOCK (session); + m = gst_message_new_custom (GST_MESSAGE_ELEMENT, GST_OBJECT (sess), s); gst_element_post_message (GST_ELEMENT_CAST (sess), m); diff --git a/gst/rtpmanager/gstrtpssrcdemux.c b/gst/rtpmanager/gstrtpssrcdemux.c index 64394c45..6a305d8e 100644 --- a/gst/rtpmanager/gstrtpssrcdemux.c +++ b/gst/rtpmanager/gstrtpssrcdemux.c @@ -97,6 +97,8 @@ static GstElementDetails gst_rtp_ssrc_demux_details = { enum { SIGNAL_NEW_SSRC_PAD, + SIGNAL_REMOVED_SSRC_PAD, + SIGNAL_CLEAR_SSRC, LAST_SIGNAL }; @@ -112,6 +114,9 @@ static void gst_rtp_ssrc_demux_finalize (GObject * object); static GstStateChangeReturn gst_rtp_ssrc_demux_change_state (GstElement * element, GstStateChange transition); +static void gst_rtp_ssrc_demux_clear_ssrc (GstRtpSsrcDemux * demux, + guint32 ssrc); + /* sinkpad stuff */ static GstFlowReturn gst_rtp_ssrc_demux_chain (GstPad * pad, GstBuffer * buf); static gboolean gst_rtp_ssrc_demux_sink_event (GstPad * pad, GstEvent * event); @@ -245,9 +250,11 @@ gst_rtp_ssrc_demux_class_init (GstRtpSsrcDemuxClass * klass) { GObjectClass *gobject_klass; GstElementClass *gstelement_klass; + GstRtpSsrcDemuxClass *gstrtpssrcdemux_klass; gobject_klass = (GObjectClass *) klass; gstelement_klass = (GstElementClass *) klass; + gstrtpssrcdemux_klass = (GstRtpSsrcDemuxClass *) klass; gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_rtp_ssrc_demux_dispose); gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_rtp_ssrc_demux_finalize); @@ -267,8 +274,38 @@ gst_rtp_ssrc_demux_class_init (GstRtpSsrcDemuxClass * klass) NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_OBJECT, G_TYPE_NONE, 2, G_TYPE_UINT, GST_TYPE_PAD); + /** + * GstRtpSsrcDemux::removed-ssrc-pad: + * @demux: the object which received the signal + * @ssrc: the SSRC of the pad + * @pad: the removed pad. + * + * Emited when a SSRC pad has been removed. + */ + gst_rtp_ssrc_demux_signals[SIGNAL_REMOVED_SSRC_PAD] = + g_signal_new ("removed-ssrc-pad", + G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GstRtpSsrcDemuxClass, removed_ssrc_pad), + NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_OBJECT, + G_TYPE_NONE, 2, G_TYPE_UINT, GST_TYPE_PAD); + + /** + * GstRtpSsrcDemux::clear-ssrc: + * @demux: the object which received the signal + * @ssrc: the SSRC of the pad + * + * Action signal to remove the pad for SSRC. + */ + gst_rtp_ssrc_demux_signals[SIGNAL_CLEAR_SSRC] = + g_signal_new ("clear-ssrc", + G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GstRtpSsrcDemuxClass, clear_ssrc), + NULL, NULL, gst_rtp_bin_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); + gstelement_klass->change_state = GST_DEBUG_FUNCPTR (gst_rtp_ssrc_demux_change_state); + gstrtpssrcdemux_klass->clear_ssrc = + GST_DEBUG_FUNCPTR (gst_rtp_ssrc_demux_clear_ssrc); GST_DEBUG_CATEGORY_INIT (gst_rtp_ssrc_demux_debug, "rtpssrcdemux", 0, "RTP SSRC demuxer"); @@ -342,6 +379,43 @@ gst_rtp_ssrc_demux_finalize (GObject * object) G_OBJECT_CLASS (parent_class)->finalize (object); } +static void +gst_rtp_ssrc_demux_clear_ssrc (GstRtpSsrcDemux * demux, guint32 ssrc) +{ + GstRtpSsrcDemuxPad *dpad; + + GST_PAD_LOCK (demux); + dpad = find_demux_pad_for_ssrc (demux, ssrc); + if (dpad != NULL) + goto unknown_pad; + + GST_DEBUG_OBJECT (demux, "clearing pad for SSRC %08x", ssrc); + + demux->srcpads = g_slist_remove (demux->srcpads, dpad); + GST_PAD_UNLOCK (demux); + + gst_pad_set_active (dpad->rtp_pad, FALSE); + gst_pad_set_active (dpad->rtcp_pad, FALSE); + + g_signal_emit (G_OBJECT (demux), + gst_rtp_ssrc_demux_signals[SIGNAL_REMOVED_SSRC_PAD], 0, ssrc, + dpad->rtp_pad); + + gst_element_remove_pad (GST_ELEMENT_CAST (demux), dpad->rtp_pad); + gst_element_remove_pad (GST_ELEMENT_CAST (demux), dpad->rtcp_pad); + + g_free (dpad); + + return; + + /* ERRORS */ +unknown_pad: + { + g_warning ("unknown SSRC %08x", ssrc); + return; + } +} + static gboolean gst_rtp_ssrc_demux_sink_event (GstPad * pad, GstEvent * event) { @@ -483,7 +557,7 @@ gst_rtp_ssrc_demux_rtcp_chain (GstPad * pad, GstBuffer * buf) NULL); break; default: - goto invalid_rtcp; + goto unexpected_rtcp; } GST_DEBUG_OBJECT (demux, "received RTCP of SSRC %08x", ssrc); @@ -511,6 +585,12 @@ invalid_rtcp: gst_buffer_unref (buf); return GST_FLOW_ERROR; } +unexpected_rtcp: + { + GST_DEBUG_OBJECT (demux, "dropping unexpected RTCP packet"); + gst_buffer_unref (buf); + return GST_FLOW_OK; + } create_failed: { GST_ELEMENT_ERROR (demux, STREAM, DECODE, (NULL), diff --git a/gst/rtpmanager/gstrtpssrcdemux.h b/gst/rtpmanager/gstrtpssrcdemux.h index d89472af..d5a13caf 100644 --- a/gst/rtpmanager/gstrtpssrcdemux.h +++ b/gst/rtpmanager/gstrtpssrcdemux.h @@ -50,7 +50,11 @@ struct _GstRtpSsrcDemuxClass GstElementClass parent_class; /* signals */ - void (*new_ssrc_pad) (GstElement *element, guint32 ssrc, GstPad *pad); + void (*new_ssrc_pad) (GstRtpSsrcDemux *demux, guint32 ssrc, GstPad *pad); + void (*removed_ssrc_pad) (GstRtpSsrcDemux *demux, guint32 ssrc, GstPad *pad); + + /* actions */ + void (*clear_ssrc) (GstRtpSsrcDemux *demux, guint32 ssrc); }; GType gst_rtp_ssrc_demux_get_type (void); diff --git a/gst/rtpmanager/rtpsource.c b/gst/rtpmanager/rtpsource.c index a40d974f..355526ee 100644 --- a/gst/rtpmanager/rtpsource.c +++ b/gst/rtpmanager/rtpsource.c @@ -188,12 +188,51 @@ rtp_source_finalize (GObject * object) G_OBJECT_CLASS (rtp_source_parent_class)->finalize (object); } +#define MAX_ADDRESS 64 +static void +make_address_string (GstNetAddress * addr, gchar * dest, gulong n) +{ + switch (gst_netaddress_get_net_type (addr)) { + case GST_NET_TYPE_IP4: + { + guint32 address; + guint16 port; + + gst_netaddress_get_ip4_address (addr, &address, &port); + + g_snprintf (dest, n, "%d.%d.%d.%d:%d", (address >> 24) & 0xff, + (address >> 16) & 0xff, (address >> 8) & 0xff, address & 0xff, + g_ntohs (port)); + break; + } + case GST_NET_TYPE_IP6: + { + guint8 address[16]; + guint16 port; + + gst_netaddress_get_ip6_address (addr, address, &port); + + g_snprintf (dest, n, "[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]:%d", + (address[0] << 8) | address[1], (address[2] << 8) | address[3], + (address[4] << 8) | address[5], (address[6] << 8) | address[7], + (address[8] << 8) | address[9], (address[10] << 8) | address[11], + (address[12] << 8) | address[13], (address[14] << 8) | address[15], + g_ntohs (port)); + break; + } + default: + dest[0] = 0; + break; + } +} + static GstStructure * rtp_source_create_stats (RTPSource * src) { GstStructure *s; gboolean is_sender = src->is_sender; gboolean internal = src->internal; + gchar address_str[MAX_ADDRESS]; /* common data for all types of sources */ s = gst_structure_new ("application/x-rtp-source-stats", @@ -204,6 +243,16 @@ rtp_source_create_stats (RTPSource * src) "is-csrc", G_TYPE_BOOLEAN, src->is_csrc, "is-sender", G_TYPE_BOOLEAN, is_sender, NULL); + /* add address and port */ + if (src->have_rtp_from) { + make_address_string (&src->rtp_from, address_str, sizeof (address_str)); + gst_structure_set (s, "rtp-from", G_TYPE_STRING, address_str, NULL); + } + if (src->have_rtcp_from) { + make_address_string (&src->rtcp_from, address_str, sizeof (address_str)); + gst_structure_set (s, "rtcp-from", G_TYPE_STRING, address_str, NULL); + } + if (internal) { /* our internal source */ if (is_sender) { diff --git a/gst/sdp/gstsdpdemux.c b/gst/sdp/gstsdpdemux.c index be34a22a..4deac870 100644 --- a/gst/sdp/gstsdpdemux.c +++ b/gst/sdp/gstsdpdemux.c @@ -51,6 +51,22 @@ #include <unistd.h> #endif +#ifdef G_OS_WIN32 +#ifdef _MSC_VER +#include <Winsock2.h> +#endif +/* ws2_32.dll has getaddrinfo and freeaddrinfo on Windows XP and later. + * * minwg32 headers check WINVER before allowing the use of these */ +#ifndef WINVER +#define WINVER 0x0501 +#endif +#include <ws2tcpip.h> +#else +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#endif + #include <stdlib.h> #include <string.h> #include <locale.h> @@ -348,6 +364,39 @@ gst_sdp_demux_stream_free (GstSDPDemux * demux, GstSDPStream * stream) g_free (stream); } +static gboolean +is_multicast_address (const gchar * host_name) +{ + struct addrinfo hints; + struct addrinfo *ai; + struct addrinfo *res; + gboolean ret = FALSE; + int err; + + memset (&hints, 0, sizeof (hints)); + hints.ai_socktype = SOCK_DGRAM; + + g_return_val_if_fail (host_name, FALSE); + + if ((err = getaddrinfo (host_name, NULL, &hints, &res)) < 0) + return FALSE; + + for (ai = res; !ret && ai; ai = ai->ai_next) { + if (ai->ai_family == AF_INET) + ret = + IN_MULTICAST (ntohl (((struct sockaddr_in *) ai->ai_addr)-> + sin_addr.s_addr)); + else + ret = + IN6_IS_ADDR_MULTICAST (&((struct sockaddr_in6 *) ai-> + ai_addr)->sin6_addr); + } + + freeaddrinfo (res); + + return ret; +} + static GstSDPStream * gst_sdp_demux_create_stream (GstSDPDemux * demux, GstSDPMessage * sdp, gint idx) { @@ -395,6 +444,7 @@ gst_sdp_demux_create_stream (GstSDPDemux * demux, GstSDPMessage * sdp, gint idx) stream->destination = conn->address; stream->ttl = conn->ttl; + stream->multicast = is_multicast_address (stream->destination); stream->rtp_port = gst_sdp_media_get_port (media); if ((rtcp = gst_sdp_media_get_attribute_val (media, "rtcp"))) { @@ -885,18 +935,24 @@ start_session_failure: static gboolean gst_sdp_demux_stream_configure_udp (GstSDPDemux * demux, GstSDPStream * stream) { - gchar *uri, *name; + gchar *uri, *name, *destination; GstPad *pad; GST_DEBUG_OBJECT (demux, "creating UDP sources for multicast"); + /* if the destination is not a multicast address, we just want to listen on + * our local ports */ + if (!stream->multicast) + destination = "0.0.0.0"; + else + destination = stream->destination; + /* creating UDP source */ if (stream->rtp_port != -1) { - GST_DEBUG_OBJECT (demux, "receiving RTP from %s:%d", stream->destination, + GST_DEBUG_OBJECT (demux, "receiving RTP from %s:%d", destination, stream->rtp_port); - uri = g_strdup_printf ("udp://%s:%d", stream->destination, - stream->rtp_port); + uri = g_strdup_printf ("udp://%s:%d", destination, stream->rtp_port); stream->udpsrc[0] = gst_element_make_from_uri (GST_URI_SRC, uri, NULL); g_free (uri); if (stream->udpsrc[0] == NULL) @@ -933,10 +989,9 @@ gst_sdp_demux_stream_configure_udp (GstSDPDemux * demux, GstSDPStream * stream) /* creating another UDP source */ if (stream->rtcp_port != -1) { - GST_DEBUG_OBJECT (demux, "receiving RTCP from %s:%d", stream->destination, + GST_DEBUG_OBJECT (demux, "receiving RTCP from %s:%d", destination, stream->rtcp_port); - uri = - g_strdup_printf ("udp://%s:%d", stream->destination, stream->rtcp_port); + uri = g_strdup_printf ("udp://%s:%d", destination, stream->rtcp_port); stream->udpsrc[1] = gst_element_make_from_uri (GST_URI_SRC, uri, NULL); g_free (uri); if (stream->udpsrc[1] == NULL) @@ -988,6 +1043,13 @@ gst_sdp_demux_stream_configure_udp_sink (GstSDPDemux * demux, if (stream->udpsink == NULL) goto no_sink_element; + /* we clear all destinations because we don't really know where to send the + * RTCP to and we want to avoid sending it to our own ports. + * FIXME when we get an RTCP packet from the sender, we could look at its + * source port and address and try to send RTCP there. */ + if (!stream->multicast) + g_signal_emit_by_name (stream->udpsink, "clear"); + /* no sync needed */ g_object_set (G_OBJECT (stream->udpsink), "sync", FALSE, NULL); /* no async state changes needed */ diff --git a/gst/sdp/gstsdpdemux.h b/gst/sdp/gstsdpdemux.h index 0ae60742..32a53293 100644 --- a/gst/sdp/gstsdpdemux.h +++ b/gst/sdp/gstsdpdemux.h @@ -68,6 +68,7 @@ struct _GstSDPStream { gchar *destination; guint ttl; + gboolean multicast; /* our udp sink back to the server */ GstElement *udpsink; diff --git a/gst/y4m/Makefile.am b/gst/y4m/Makefile.am deleted file mode 100644 index 4415e9e2..00000000 --- a/gst/y4m/Makefile.am +++ /dev/null @@ -1,10 +0,0 @@ - -plugin_LTLIBRARIES = libgsty4menc.la - -libgsty4menc_la_SOURCES = gsty4mencode.c -libgsty4menc_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) -libgsty4menc_la_LIBADD = $(GST_LIBS) -libgsty4menc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgsty4menc_la_LIBTOOLFLAGS = --tag=disable-static - -noinst_HEADERS = gsty4mencode.h diff --git a/gst/y4m/gsty4mencode.c b/gst/y4m/gsty4mencode.c deleted file mode 100644 index dc218643..00000000 --- a/gst/y4m/gsty4mencode.c +++ /dev/null @@ -1,332 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> - * Copyright (C) <2006> Mark Nauwelaerts <mnauw@users.sourceforge.net> - * - * 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. - */ -/** - * SECTION:element-y4menc - * - * <refsect2> - * <para> - * Creates a YU4MPEG2 raw video stream as defined by the mjpegtools project. - * </para> - * <title>Example launch line</title> - * <para> - * (write everything in one line, without the backslash characters) - * <programlisting> - * gst-launch-0.10 videotestsrc num-buffers=250 \ - * ! 'video/x-raw-yuv,format=(fourcc)I420,width=320,height=240,framerate=(fraction)25/1' \ - * ! y4menc ! filesink location=test.yuv - * </programlisting> - * </para> - * </refsect2> - * - */ - -/* see mjpegtools/yuv4mpeg.h for yuv4mpeg format */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#include <string.h> -#include <gst/gst.h> -#include <gst/video/video.h> -#include "gsty4mencode.h" - -static const GstElementDetails y4mencode_details = -GST_ELEMENT_DETAILS ("YUV4MPEG video encoder", - "Codec/Encoder/Video", - "Encodes a YUV frame into the yuv4mpeg format (mjpegtools)", - "Wim Taymans <wim.taymans@chello.be>"); - - -/* Filter signals and args */ -enum -{ - /* FILL ME */ - LAST_SIGNAL -}; - -enum -{ - ARG_0 -}; - -static GstStaticPadTemplate y4mencode_src_factory = -GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("application/x-yuv4mpeg, " "y4mversion = (int) 2") - ); - -static GstStaticPadTemplate y4mencode_sink_factory = -GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ IYUV, I420 }")) - ); - - -static void gst_y4m_encode_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec); -static void gst_y4m_encode_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec); - -static void gst_y4m_encode_reset (GstY4mEncode * filter); - -static gboolean gst_y4m_encode_setcaps (GstPad * pad, GstCaps * vscaps); -static GstFlowReturn gst_y4m_encode_chain (GstPad * pad, GstBuffer * buf); -static GstStateChangeReturn gst_y4m_encode_change_state (GstElement * element, - GstStateChange transition); - -GST_BOILERPLATE (GstY4mEncode, gst_y4m_encode, GstElement, GST_TYPE_ELEMENT); - - -static void -gst_y4m_encode_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&y4mencode_src_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&y4mencode_sink_factory)); - gst_element_class_set_details (element_class, &y4mencode_details); -} - -static void -gst_y4m_encode_class_init (GstY4mEncodeClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; - - gstelement_class->change_state = - GST_DEBUG_FUNCPTR (gst_y4m_encode_change_state); - - gobject_class->set_property = gst_y4m_encode_set_property; - gobject_class->get_property = gst_y4m_encode_get_property; -} - -static void -gst_y4m_encode_init (GstY4mEncode * filter, GstY4mEncodeClass * klass) -{ - filter->sinkpad = - gst_pad_new_from_static_template (&y4mencode_sink_factory, "sink"); - gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad); - gst_pad_set_chain_function (filter->sinkpad, - GST_DEBUG_FUNCPTR (gst_y4m_encode_chain)); - gst_pad_set_setcaps_function (filter->sinkpad, - GST_DEBUG_FUNCPTR (gst_y4m_encode_setcaps)); - - filter->srcpad = - gst_pad_new_from_static_template (&y4mencode_src_factory, "src"); - gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad); - gst_pad_use_fixed_caps (filter->srcpad); - - /* init properties */ - gst_y4m_encode_reset (filter); -} - -static void -gst_y4m_encode_reset (GstY4mEncode * filter) -{ - filter->width = filter->height = -1; - filter->fps_num = filter->fps_den = 1; - filter->par_num = filter->par_den = 1; -} - -static gboolean -gst_y4m_encode_setcaps (GstPad * pad, GstCaps * vscaps) -{ - GstY4mEncode *filter; - GstStructure *structure; - gboolean res; - gint w, h; - const GValue *fps, *par; - - filter = GST_Y4M_ENCODE (GST_PAD_PARENT (pad)); - - structure = gst_caps_get_structure (vscaps, 0); - - res = gst_structure_get_int (structure, "width", &w); - res &= gst_structure_get_int (structure, "height", &h); - res &= ((fps = gst_structure_get_value (structure, "framerate")) != NULL); - - if (!res || w <= 0 || h <= 0 || !GST_VALUE_HOLDS_FRACTION (fps)) - return FALSE; - - /* optional par info */ - par = gst_structure_get_value (structure, "pixel-aspect-ratio"); - - filter->width = w; - filter->height = h; - filter->fps_num = gst_value_get_fraction_numerator (fps); - filter->fps_den = gst_value_get_fraction_denominator (fps); - if ((par != NULL) && GST_VALUE_HOLDS_FRACTION (par)) { - filter->par_num = gst_value_get_fraction_numerator (par); - filter->par_den = gst_value_get_fraction_denominator (par); - } else { /* indicates unknown */ - filter->par_num = 0; - filter->par_den = 0; - } - - /* the template caps will do for the src pad, should always accept */ - return gst_pad_set_caps (filter->srcpad, - gst_static_pad_template_get_caps (&y4mencode_src_factory)); -} - -static inline GstBuffer * -gst_y4m_encode_get_stream_header (GstY4mEncode * filter) -{ - gpointer header; - GstBuffer *buf; - - header = g_strdup_printf ("YUV4MPEG2 W%d H%d I? F%d:%d A%d:%d\n", - filter->width, filter->height, - filter->fps_num, filter->fps_den, filter->par_num, filter->par_den); - - buf = gst_buffer_new (); - gst_buffer_set_data (buf, header, strlen (header)); - /* so it gets free'd when needed */ - GST_BUFFER_MALLOCDATA (buf) = header; - - return buf; -} - -static inline GstBuffer * -gst_y4m_encode_get_frame_header (GstY4mEncode * filter) -{ - gpointer header; - GstBuffer *buf; - - header = g_strdup_printf ("FRAME\n"); - - buf = gst_buffer_new (); - gst_buffer_set_data (buf, header, strlen (header)); - /* so it gets free'd when needed */ - GST_BUFFER_MALLOCDATA (buf) = header; - - return buf; -} - -static GstFlowReturn -gst_y4m_encode_chain (GstPad * pad, GstBuffer * buf) -{ - GstY4mEncode *filter = GST_Y4M_ENCODE (GST_PAD_PARENT (pad)); - GstBuffer *outbuf; - - /* check we got some decent info from caps */ - if (filter->width < 0) { - GST_ELEMENT_ERROR ("filter", CORE, NEGOTIATION, (NULL), - ("format wasn't negotiated before chain function")); - gst_buffer_unref (buf); - return GST_FLOW_NOT_NEGOTIATED; - } - - if (G_UNLIKELY (!filter->header)) { - outbuf = gst_y4m_encode_get_stream_header (filter); - filter->header = TRUE; - outbuf = gst_buffer_join (outbuf, gst_y4m_encode_get_frame_header (filter)); - } else { - outbuf = gst_y4m_encode_get_frame_header (filter); - } - /* join with data */ - outbuf = gst_buffer_join (outbuf, buf); - /* decorate */ - gst_buffer_make_metadata_writable (outbuf); - gst_buffer_set_caps (outbuf, GST_PAD_CAPS (filter->srcpad)); - /* strip to avoid sink dropping on time-base, decorate and send */ - GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE; - return gst_pad_push (filter->srcpad, outbuf); -} - -static void -gst_y4m_encode_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstY4mEncode *filter; - - g_return_if_fail (GST_IS_Y4M_ENCODE (object)); - filter = GST_Y4M_ENCODE (object); - - switch (prop_id) { - default: - break; - } -} - -static void -gst_y4m_encode_get_property (GObject * object, guint prop_id, GValue * value, - GParamSpec * pspec) -{ - GstY4mEncode *filter; - - g_return_if_fail (GST_IS_Y4M_ENCODE (object)); - filter = GST_Y4M_ENCODE (object); - - switch (prop_id) { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static GstStateChangeReturn -gst_y4m_encode_change_state (GstElement * element, GstStateChange transition) -{ - GstY4mEncode *filter = GST_Y4M_ENCODE (element); - GstStateChangeReturn ret; - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - case GST_STATE_CHANGE_READY_TO_PAUSED: - break; - default: - break; - } - - ret = GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, change_state, - (element, transition), GST_STATE_CHANGE_SUCCESS); - if (ret != GST_STATE_CHANGE_SUCCESS) - return ret; - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_y4m_encode_reset (filter); - break; - default: - break; - } - - return GST_STATE_CHANGE_SUCCESS; -} - -static gboolean -plugin_init (GstPlugin * plugin) -{ - return gst_element_register (plugin, "y4menc", GST_RANK_NONE, - GST_TYPE_Y4M_ENCODE); -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "y4menc", - "Encodes a YUV frame into the yuv4mpeg format (mjpegtools)", - plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/gst/y4m/gsty4mencode.h b/gst/y4m/gsty4mencode.h deleted file mode 100644 index 1ca8105a..00000000 --- a/gst/y4m/gsty4mencode.h +++ /dev/null @@ -1,67 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> - * - * 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_Y4MENCODE_H__ -#define __GST_Y4MENCODE_H__ - - -#include <gst/gst.h> - -G_BEGIN_DECLS - -#define GST_TYPE_Y4M_ENCODE \ - (gst_y4m_encode_get_type()) -#define GST_Y4M_ENCODE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_Y4M_ENCODE, GstY4mEncode)) -#define GST_Y4M_ENCODE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_Y4M_ENCODE, GstY4mEncodeClass)) -#define GST_Y4M_ENCODE_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_Y4M_ENCODE, GstY4mEncodeClass)) -#define GST_IS_Y4M_ENCODE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_Y4M_ENCODE)) -#define GST_IS_Y4M_ENCODE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_Y4M_ENCODE)) - -typedef struct _GstY4mEncode GstY4mEncode; -typedef struct _GstY4mEncodeClass GstY4mEncodeClass; - -struct _GstY4mEncode { - GstElement element; - - GstPad *sinkpad,*srcpad; - - /* caps information */ - gint width, height; - gint fps_num, fps_den; - gint par_num, par_den; - - /* state information */ - gboolean header; -}; - -struct _GstY4mEncodeClass { - GstElementClass parent_class; -}; - -GType gst_y4m_encode_get_type(void); - -G_END_DECLS - -#endif /* __GST_Y4MENCODE_H__ */ diff --git a/gst/y4m/y4menc.vcproj b/gst/y4m/y4menc.vcproj deleted file mode 100644 index 7251140e..00000000 --- a/gst/y4m/y4menc.vcproj +++ /dev/null @@ -1,148 +0,0 @@ -<?xml version="1.0" encoding="Windows-1252"?> -<VisualStudioProject - ProjectType="Visual C++" - Version="7.10" - Name="y4mencode" - ProjectGUID="{979C216F-0ACF-4956-AE00-055A42D678E5}" - Keyword="Win32Proj"> - <Platforms> - <Platform - Name="Win32"/> - </Platforms> - <Configurations> - <Configuration - Name="Debug|Win32" - OutputDirectory="../../win32/Debug" - IntermediateDirectory="../../win32/Debug" - ConfigurationType="2" - CharacterSet="2"> - <Tool - Name="VCCLCompilerTool" - Optimization="0" - AdditionalIncludeDirectories="../../../gstreamer/win32;../../../gstreamer;../../../gstreamer/libs;../../../glib;../../../glib/glib;../../../glib/gmodule;"../../gst-libs";../../../popt/include;../../../libxml2/include/libxml2" - PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;y4mencode_EXPORTS;HAVE_CONFIG_H;_USE_MATH_DEFINES" - MinimalRebuild="TRUE" - BasicRuntimeChecks="3" - RuntimeLibrary="3" - UsePrecompiledHeader="0" - WarningLevel="3" - Detect64BitPortabilityProblems="TRUE" - DebugInformationFormat="4"/> - <Tool - Name="VCCustomBuildTool"/> - <Tool - Name="VCLinkerTool" - AdditionalDependencies="glib-2.0.lib gmodule-2.0.lib gthread-2.0.lib gobject-2.0.lib libgstreamer.lib gstbytestream.lib iconv.lib intl.lib" - OutputFile="$(OutDir)/gsty4mencode.dll" - LinkIncremental="2" - AdditionalLibraryDirectories="../../../gstreamer/win32/Debug;../../../glib/glib;../../../glib/gmodule;../../../glib/gthread;../../../glib/gobject;../../../gettext/lib;../../../libiconv/lib" - ModuleDefinitionFile="" - GenerateDebugInformation="TRUE" - ProgramDatabaseFile="$(OutDir)/y4mencode.pdb" - SubSystem="2" - OptimizeReferences="2" - ImportLibrary="$(OutDir)/gsty4mencode.lib" - TargetMachine="1"/> - <Tool - Name="VCMIDLTool"/> - <Tool - Name="VCPostBuildEventTool" - CommandLine="copy /Y $(TargetPath) c:\gstreamer\plugins"/> - <Tool - Name="VCPreBuildEventTool"/> - <Tool - Name="VCPreLinkEventTool"/> - <Tool - Name="VCResourceCompilerTool"/> - <Tool - Name="VCWebServiceProxyGeneratorTool"/> - <Tool - Name="VCXMLDataGeneratorTool"/> - <Tool - Name="VCWebDeploymentTool"/> - <Tool - Name="VCManagedWrapperGeneratorTool"/> - <Tool - Name="VCAuxiliaryManagedWrapperGeneratorTool"/> - </Configuration> - <Configuration - Name="Release|Win32" - OutputDirectory="../../win32/Release" - IntermediateDirectory="../../win32/Release" - ConfigurationType="2" - CharacterSet="2"> - <Tool - Name="VCCLCompilerTool" - AdditionalIncludeDirectories="../../../gstreamer/win32;../../../gstreamer;../../../gstreamer/libs;../../../glib;../../../glib/glib;../../../glib/gmodule;"../../gst-libs";../../../popt/include;../../../libxml2/include/libxml2" - PreprocessorDefinitions="WIN32;NDEBUG;GST_DISABLE_GST_DEBUG;_WINDOWS;_USRDLL;y4mencode_EXPORTS;HAVE_CONFIG_H;_USE_MATH_DEFINES" - RuntimeLibrary="2" - UsePrecompiledHeader="0" - WarningLevel="3" - Detect64BitPortabilityProblems="TRUE" - DebugInformationFormat="3"/> - <Tool - Name="VCCustomBuildTool"/> - <Tool - Name="VCLinkerTool" - AdditionalDependencies="glib-2.0.lib gmodule-2.0.lib gthread-2.0.lib gobject-2.0.lib libgstreamer.lib gstbytestream.lib iconv.lib intl.lib" - OutputFile="$(OutDir)/gsty4mencode.dll" - LinkIncremental="1" - AdditionalLibraryDirectories="../../../gstreamer/win32/Release;../../../glib/glib;../../../glib/gmodule;../../../glib/gthread;../../../glib/gobject;../../../gettext/lib;../../../libiconv/lib" - ModuleDefinitionFile="" - GenerateDebugInformation="TRUE" - SubSystem="2" - OptimizeReferences="2" - EnableCOMDATFolding="2" - ImportLibrary="$(OutDir)/gsty4mencode.lib" - TargetMachine="1"/> - <Tool - Name="VCMIDLTool"/> - <Tool - Name="VCPostBuildEventTool" - CommandLine="copy /Y $(TargetPath) c:\gstreamer\plugins"/> - <Tool - Name="VCPreBuildEventTool"/> - <Tool - Name="VCPreLinkEventTool"/> - <Tool - Name="VCResourceCompilerTool"/> - <Tool - Name="VCWebServiceProxyGeneratorTool"/> - <Tool - Name="VCXMLDataGeneratorTool"/> - <Tool - Name="VCWebDeploymentTool"/> - <Tool - Name="VCManagedWrapperGeneratorTool"/> - <Tool - Name="VCAuxiliaryManagedWrapperGeneratorTool"/> - </Configuration> - </Configurations> - <References> - </References> - <Files> - <Filter - Name="Source Files" - Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx" - UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"> - <File - RelativePath=".\gsty4mencode.c"> - </File> - </Filter> - <Filter - Name="Header Files" - Filter="h;hpp;hxx;hm;inl;inc;xsd" - UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"> - <File - RelativePath=".\gsty4mencode.h"> - </File> - </Filter> - <Filter - Name="Resource Files" - Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx" - UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"> - </Filter> - </Files> - <Globals> - </Globals> -</VisualStudioProject> |