diff options
100 files changed, 7542 insertions, 4588 deletions
diff --git a/common b/common -Subproject fedaaee6fa5c0006f5b7264732cb4e29584ef10 +Subproject 94f95e3d83fe677bfc4aaebb23badc3abc56366 diff --git a/configure.ac b/configure.ac index 1f180beb..5f5a4bc8 100644 --- a/configure.ac +++ b/configure.ac @@ -254,6 +254,7 @@ AG_GST_CHECK_PLUGIN(aacparse) AG_GST_CHECK_PLUGIN(adpcmdec) AG_GST_CHECK_PLUGIN(aiffparse) AG_GST_CHECK_PLUGIN(amrparse) +AG_GST_CHECK_PLUGIN(asfmux) AG_GST_CHECK_PLUGIN(autoconvert) AG_GST_CHECK_PLUGIN(camerabin) AG_GST_CHECK_PLUGIN(legacyresample) @@ -353,6 +354,20 @@ fi dnl *** sys plug-ins *** +dnl DirectSound +translit(dnm, m, l) AM_CONDITIONAL(USE_DIRECTSOUND, true) +AG_GST_CHECK_FEATURE(DIRECTSOUND, [DirectSound], [directsound], [ + AC_CHECK_HEADER(dsound.h, HAVE_DIRECTSOUND="yes", HAVE_DIRECTSOUND="no") +]) + +AC_CHECK_LIB( dsound, DXGetErrorString9, [ + echo "Errorstrings in DirectSoundSink - enabled" + AM_CONDITIONAL( DSOUND9, true ) +], [ + echo "Errorstrings in DirectSoundSink - disabled" + AM_CONDITIONAL( DSOUND9, false ) +] ) + dnl DirectDraw translit(dnm, m, l) AM_CONDITIONAL(USE_DIRECTDRAW, true) AG_GST_CHECK_FEATURE(DIRECTDRAW, [DirectDraw plug-in], directdrawsink, [ @@ -449,12 +464,12 @@ AG_GST_CHECK_FEATURE(ASSRENDER, [ASS/SSA renderer], assrender, [ AC_SUBST(ASSRENDER_CFLAGS) AC_SUBST(ASSRENDER_LIBS) -dnl *** amrwb *** +dnl *** amrwb-enc *** translit(dnm, m, l) AM_CONDITIONAL(USE_AMRWB, true) -AG_GST_CHECK_FEATURE(AMRWB, [amrwb library], amrwb, [ +AG_GST_CHECK_FEATURE(AMRWB, [amrwb library], amrwbenc, [ AG_GST_CHECK_LIBHEADER(AMRWB, amrwb, - GP3D_IF_decode, , - amrwb/dec.h, + GP3E_IF_encode, , + amrwb/enc.h, AMRWB_LIBS="-lamrwb" AC_SUBST(AMRWB_LIBS)) ]) @@ -1370,6 +1385,28 @@ AG_GST_CHECK_FEATURE(SPC, [spc decoder], spc, [ ]) ]) +dnl *** gme *** +translit(dnm, m, l) AM_CONDITIONAL(USE_GME, true) +AG_GST_CHECK_FEATURE(GME, [gme decoder], gme, [ + AC_CHECK_HEADER(gme/gme.h, [ + AC_CHECK_LIB(gme, gme_new_emu, [ + GME_LIBS="-lgme" + AC_SUBST(GME_LIBS) + HAVE_GME=yes + ], [ + HAVE_GME=no + ]) + ], [ + HAVE_GME=no + ]) + + if test "x$HAVE_GME" = "xyes"; then + AC_CHECK_LIB(gme, gme_enable_accuracy, [ + AC_DEFINE(HAVE_LIBGME_ACCURACY, 1, [Define if gme 0.5.6 or newer is available]) + ]) + fi +]) + dnl *** swfdec *** translit(dnm, m, l) AM_CONDITIONAL(USE_SWFDEC, true) AG_GST_CHECK_FEATURE(SWFDEC, [swfdec plug-in], swfdec, [ @@ -1515,6 +1552,7 @@ AM_CONDITIONAL(USE_CELT, false) AM_CONDITIONAL(USE_DC1394, false) AM_CONDITIONAL(USE_DIRECTFB, false) AM_CONDITIONAL(USE_DIRECTDRAW, false) +AM_CONDITIONAL(USE_DIRECTSOUND, false) AM_CONDITIONAL(USE_DTS, false) AM_CONDITIONAL(USE_DIRAC, false) AM_CONDITIONAL(USE_DIVX, false) @@ -1544,6 +1582,7 @@ AM_CONDITIONAL(USE_OSS4, false) AM_CONDITIONAL(USE_SDL, false) AM_CONDITIONAL(USE_SNDFILE, false) AM_CONDITIONAL(USE_SOUNDTOUCH, false) +AM_CONDITIONAL(USE_GME, false) AM_CONDITIONAL(USE_SPC, false) AM_CONDITIONAL(USE_SWFDEC, false) AM_CONDITIONAL(USE_THEORADEC, false) @@ -1626,6 +1665,7 @@ gst/aacparse/Makefile gst/adpcmdec/Makefile gst/aiffparse/Makefile gst/amrparse/Makefile +gst/asfmux/Makefile gst/autoconvert/Makefile gst/bayer/Makefile gst/camerabin/Makefile @@ -1681,6 +1721,7 @@ sys/dshowdecwrapper/Makefile sys/acmenc/Makefile sys/acmmp3dec/Makefile sys/directdraw/Makefile +sys/directsound/Makefile sys/dshowsrcwrapper/Makefile sys/dshowvideosink/Makefile sys/dvb/Makefile @@ -1705,7 +1746,7 @@ tests/examples/shapewipe/Makefile tests/examples/switch/Makefile tests/icles/Makefile ext/alsaspdif/Makefile -ext/amrwb/Makefile +ext/amrwbenc/Makefile ext/assrender/Makefile ext/apexsink/Makefile ext/bz2/Makefile @@ -1743,6 +1784,7 @@ ext/schroedinger/Makefile ext/sdl/Makefile ext/sndfile/Makefile ext/soundtouch/Makefile +ext/gme/Makefile ext/spc/Makefile ext/swfdec/Makefile ext/theora/Makefile diff --git a/docs/plugins/Makefile.am b/docs/plugins/Makefile.am index 0a43f4ca..7ac083ae 100644 --- a/docs/plugins/Makefile.am +++ b/docs/plugins/Makefile.am @@ -89,9 +89,7 @@ EXAMPLE_CFILES = \ $(top_srcdir)/ext/directfb/dfb-example.c EXTRA_HFILES = \ - $(top_srcdir)/ext/amrwb/gstamrwbdec.h \ - $(top_srcdir)/ext/amrwb/gstamrwbenc.h \ - $(top_srcdir)/ext/amrwb/gstamrwbparse.h \ + $(top_srcdir)/ext/amrwbenc/gstamrwbenc.h \ $(top_srcdir)/ext/celt/gstceltdec.h \ $(top_srcdir)/ext/celt/gstceltenc.h \ $(top_srcdir)/ext/dc1394/gstdc1394.h \ diff --git a/docs/plugins/gst-plugins-bad-plugins-docs.sgml b/docs/plugins/gst-plugins-bad-plugins-docs.sgml index f1a36d70..ef8eb36b 100644 --- a/docs/plugins/gst-plugins-bad-plugins-docs.sgml +++ b/docs/plugins/gst-plugins-bad-plugins-docs.sgml @@ -19,10 +19,7 @@ <title>gst-plugins-bad Elements</title> <xi:include href="xml/element-aacparse.xml" /> <xi:include href="xml/element-amrparse.xml" /> - <xi:include href="xml/element-amrwbdec.xml" /> <xi:include href="xml/element-amrwbenc.xml" /> - <xi:include href="xml/element-amrwbparse.xml" /> - <xi:include href="xml/element-amrwbparse.xml" /> <xi:include href="xml/element-autoconvert.xml" /> <xi:include href="xml/element-camerabin.xml" /> <xi:include href="xml/element-capssetter.xml" /> @@ -100,7 +97,7 @@ <xi:include href="xml/plugin-autoconvert.xml" /> <xi:include href="xml/plugin-legacyresample.xml" /> <xi:include href="xml/plugin-alsaspdif.xml" /> - <xi:include href="xml/plugin-amrwb.xml" /> + <xi:include href="xml/plugin-amrwbenc.xml" /> <xi:include href="xml/plugin-bayer.xml" /> <xi:include href="xml/plugin-bz2.xml" /> <xi:include href="xml/plugin-camerabin.xml" /> diff --git a/docs/plugins/gst-plugins-bad-plugins-sections.txt b/docs/plugins/gst-plugins-bad-plugins-sections.txt index 09e0e3fc..c60e127c 100644 --- a/docs/plugins/gst-plugins-bad-plugins-sections.txt +++ b/docs/plugins/gst-plugins-bad-plugins-sections.txt @@ -27,20 +27,6 @@ gst_amrparse_get_type </SECTION> <SECTION> -<FILE>element-amrwbdec</FILE> -<TITLE>amrwbdec</TITLE> -GstAmrwbDec -<SUBSECTION Standard> -GstAmrwbDecClass -GST_AMRWBDEC -GST_AMRWBDEC_CLASS -GST_IS_AMRWBDEC -GST_IS_AMRWBDEC_CLASS -GST_TYPE_AMRWBDEC -gst_amrwbdec_get_type -</SECTION> - -<SECTION> <FILE>element-amrwbenc</FILE> <TITLE>amrwbenc</TITLE> GstAmrwbEnc @@ -55,20 +41,6 @@ gst_amrwbenc_get_type </SECTION> <SECTION> -<FILE>element-amrwbparse</FILE> -<TITLE>amrwbparse</TITLE> -GstAmrwbParse -<SUBSECTION Standard> -GstAmrwbParseClass -GST_AMRWBPARSE -GST_AMRWBPARSE_CLASS -GST_IS_AMRWBPARSE -GST_IS_AMRWBPARSE_CLASS -GST_TYPE_AMRWBPARSE -gst_amrwbparse_get_type -</SECTION> - -<SECTION> <FILE>element-audioparse</FILE> <TITLE>audioparse</TITLE> GstAudioParse diff --git a/docs/plugins/inspect/plugin-amrwb.xml b/docs/plugins/inspect/plugin-amrwb.xml deleted file mode 100644 index bd617351..00000000 --- a/docs/plugins/inspect/plugin-amrwb.xml +++ /dev/null @@ -1,76 +0,0 @@ -<plugin> - <name>amrwb</name> - <description>Adaptive Multi-Rate Wide-Band</description> - <filename>../../ext/amrwb/.libs/libgstamrwb.so</filename> - <basename>libgstamrwb.so</basename> - <version>0.10.13.1</version> - <license>unknown</license> - <source>gst-plugins-bad</source> - <package>GStreamer Bad Plug-ins git/prerelease</package> - <origin>http://gstreamer.freedesktop.org</origin> - <elements> - <element> - <name>amrwbdec</name> - <longname>AMR-WB audio decoder</longname> - <class>Codec/Decoder/Audio</class> - <description>Adaptive Multi-Rate Wideband audio decoder</description> - <author>Renato Araujo <renato.filho@indt.org.br></author> - <pads> - <caps> - <name>src</name> - <direction>source</direction> - <presence>always</presence> - <details>audio/x-raw-int, width=(int)16, depth=(int)16, signed=(boolean)true, endianness=(int)1234, rate=(int)16000, channels=(int)1</details> - </caps> - <caps> - <name>sink</name> - <direction>sink</direction> - <presence>always</presence> - <details>audio/AMR-WB, rate=(int)16000, channels=(int)1</details> - </caps> - </pads> - </element> - <element> - <name>amrwbenc</name> - <longname>AMR-WB audio encoder</longname> - <class>Codec/Encoder/Audio</class> - <description>Adaptive Multi-Rate Wideband audio encoder</description> - <author>Renato Araujo <renato.filho@indt.org.br></author> - <pads> - <caps> - <name>src</name> - <direction>source</direction> - <presence>always</presence> - <details>audio/AMR-WB, rate=(int)16000, channels=(int)1</details> - </caps> - <caps> - <name>sink</name> - <direction>sink</direction> - <presence>always</presence> - <details>audio/x-raw-int, width=(int)16, depth=(int)16, signed=(boolean)true, endianness=(int)1234, rate=(int)16000, channels=(int)1</details> - </caps> - </pads> - </element> - <element> - <name>amrwbparse</name> - <longname>AMR-WB audio stream parser</longname> - <class>Codec/Parser/Audio</class> - <description>Adaptive Multi-Rate WideBand audio parser</description> - <author>Renato Filho <renato.filho@indt.org.br></author> - <pads> - <caps> - <name>src</name> - <direction>source</direction> - <presence>always</presence> - <details>audio/AMR-WB, rate=(int)16000, channels=(int)1</details> - </caps> - <caps> - <name>sink</name> - <direction>sink</direction> - <presence>always</presence> - <details>audio/x-amr-wb-sh</details> - </caps> - </pads> - </element> - </elements> -</plugin>
\ No newline at end of file diff --git a/docs/plugins/inspect/plugin-amrwbenc.xml b/docs/plugins/inspect/plugin-amrwbenc.xml new file mode 100644 index 00000000..9a7ada08 --- /dev/null +++ b/docs/plugins/inspect/plugin-amrwbenc.xml @@ -0,0 +1,34 @@ +<plugin> + <name>amrwbenc</name> + <description>Adaptive Multi-Rate Wide-Band Encoder</description> + <filename>../../ext/amrwb/.libs/libgstamrwbenc.so</filename> + <basename>libgstamrwbenc.so</basename> + <version>0.10.12.1</version> + <license>unknown</license> + <source>gst-plugins-bad</source> + <package>GStreamer Bad Plug-ins git/prerelease</package> + <origin>Unknown package origin</origin> + <elements> + <element> + <name>amrwbenc</name> + <longname>AMR-WB audio encoder</longname> + <class>Codec/Encoder/Audio</class> + <description>Adaptive Multi-Rate Wideband audio encoder</description> + <author>Renato Araujo <renato.filho@indt.org.br></author> + <pads> + <caps> + <name>src</name> + <direction>source</direction> + <presence>always</presence> + <details>audio/AMR-WB, rate=(int)16000, channels=(int)1</details> + </caps> + <caps> + <name>sink</name> + <direction>sink</direction> + <presence>always</presence> + <details>audio/x-raw-int, width=(int)16, depth=(int)16, signed=(boolean)true, endianness=(int)1234, rate=(int)16000, channels=(int)1</details> + </caps> + </pads> + </element> + </elements> +</plugin> diff --git a/ext/Makefile.am b/ext/Makefile.am index fafa1146..31dd00a4 100644 --- a/ext/Makefile.am +++ b/ext/Makefile.am @@ -11,7 +11,7 @@ ASSRENDER_DIR = endif if USE_AMRWB -AMRWB_DIR = amrwb +AMRWB_DIR = amrwbenc else AMRWB_DIR = endif @@ -307,11 +307,17 @@ SOUNDTOUCH_DIR= endif if USE_SPC -SPC_DIR=spc +SPC_DIR=gme else SPC_DIR= endif +if USE_GME +GME_DIR=gme +else +GME_DIR= +endif + if USE_SWFDEC #SWFDEC_DIR=swfdec else @@ -392,6 +398,7 @@ SUBDIRS=\ $(SMOOTHWAVE_DIR) \ $(SNDFILE_DIR) \ $(SOUNDTOUCH_DIR) \ + $(GME_DIR) \ $(SPC_DIR) \ $(SWFDEC_DIR) \ $(TARKIN_DIR) \ @@ -402,7 +409,7 @@ SUBDIRS=\ DIST_SUBDIRS = \ alsaspdif \ - amrwb \ + amrwbenc \ assrender \ apexsink \ bz2 \ @@ -440,6 +447,7 @@ DIST_SUBDIRS = \ sndfile \ soundtouch \ spc \ + gme \ swfdec \ theora \ timidity \ diff --git a/ext/amrwb/Makefile.am b/ext/amrwb/Makefile.am deleted file mode 100644 index c4060d7f..00000000 --- a/ext/amrwb/Makefile.am +++ /dev/null @@ -1,18 +0,0 @@ -plugin_LTLIBRARIES = libgstamrwb.la - -libgstamrwb_la_SOURCES = \ - gstamrwb.c \ - gstamrwbdec.c \ - gstamrwbenc.c \ - gstamrwbparse.c - -libgstamrwb_la_CFLAGS = $(GST_CFLAGS) $(AMRWB_CFLAGS) -libgstamrwb_la_LIBADD = $(GST_BASE_LIBS) $(AMRWB_LIBS) -libgstamrwb_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgstamrwb_la_LIBTOOLFLAGS = --tag=disable-static - -noinst_HEADERS = \ - gstamrwbdec.h \ - gstamrwbenc.h \ - gstamrwbparse.h - diff --git a/ext/amrwb/gstamrwbdec.c b/ext/amrwb/gstamrwbdec.c deleted file mode 100644 index 54e8a830..00000000 --- a/ext/amrwb/gstamrwbdec.c +++ /dev/null @@ -1,372 +0,0 @@ -/* GStreamer Adaptive Multi-Rate Narrow-Band (AMR-NB) plugin - * Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.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-amrwbdec - * @see_also: #GstAmrwbEnc, #GstAmrwbParse - * - * AMR wideband decoder based on the - * <ulink url="http://www.penguin.cz/~utx/amr">reference codec implementation</ulink>. - * - * <refsect2> - * <title>Example launch line</title> - * |[ - * gst-launch filesrc location=abc.amr ! amrwbparse ! amrwbdec ! audioresample ! audioconvert ! alsasink - * ]| - * </refsect2> - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstamrwbdec.h" - -static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/AMR-WB, " - "rate = (int) 16000, " "channels = (int) 1") - ); - -static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-raw-int, " - "width = (int) 16, " - "depth = (int) 16, " - "signed = (boolean) TRUE, " - "endianness = (int) BYTE_ORDER, " - "rate = (int) 16000, " "channels = (int) 1") - ); - -GST_DEBUG_CATEGORY_STATIC (gst_amrwbdec_debug); -#define GST_CAT_DEFAULT gst_amrwbdec_debug - -extern const UWord8 block_size[]; - -static gboolean gst_amrwbdec_event (GstPad * pad, GstEvent * event); -static GstFlowReturn gst_amrwbdec_chain (GstPad * pad, GstBuffer * buffer); -static gboolean gst_amrwbdec_setcaps (GstPad * pad, GstCaps * caps); -static GstStateChangeReturn gst_amrwbdec_state_change (GstElement * element, - GstStateChange transition); - -static void gst_amrwbdec_finalize (GObject * object); - -#define _do_init(bla) \ - GST_DEBUG_CATEGORY_INIT (gst_amrwbdec_debug, "amrwbdec", 0, "AMR-WB audio decoder"); - -GST_BOILERPLATE_FULL (GstAmrwbDec, gst_amrwbdec, GstElement, GST_TYPE_ELEMENT, - _do_init); - -static void -gst_amrwbdec_base_init (gpointer klass) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - GstElementDetails details = GST_ELEMENT_DETAILS ("AMR-WB audio decoder", - "Codec/Decoder/Audio", - "Adaptive Multi-Rate Wideband audio decoder", - "Renato Araujo <renato.filho@indt.org.br>"); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&sink_template)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&src_template)); - - gst_element_class_set_details (element_class, &details); -} - -static void -gst_amrwbdec_class_init (GstAmrwbDecClass * klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - - object_class->finalize = gst_amrwbdec_finalize; - - element_class->change_state = GST_DEBUG_FUNCPTR (gst_amrwbdec_state_change); -} - -static void -gst_amrwbdec_init (GstAmrwbDec * amrwbdec, GstAmrwbDecClass * klass) -{ - /* create the sink pad */ - amrwbdec->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink"); - gst_pad_set_setcaps_function (amrwbdec->sinkpad, gst_amrwbdec_setcaps); - gst_pad_set_event_function (amrwbdec->sinkpad, gst_amrwbdec_event); - gst_pad_set_chain_function (amrwbdec->sinkpad, gst_amrwbdec_chain); - gst_element_add_pad (GST_ELEMENT (amrwbdec), amrwbdec->sinkpad); - - /* create the src pad */ - amrwbdec->srcpad = gst_pad_new_from_static_template (&src_template, "src"); - gst_pad_use_fixed_caps (amrwbdec->srcpad); - gst_element_add_pad (GST_ELEMENT (amrwbdec), amrwbdec->srcpad); - - amrwbdec->adapter = gst_adapter_new (); - - /* init rest */ - amrwbdec->handle = NULL; - amrwbdec->channels = 0; - amrwbdec->rate = 0; - amrwbdec->duration = 0; - amrwbdec->ts = -1; -} - -static void -gst_amrwbdec_finalize (GObject * object) -{ - GstAmrwbDec *amrwbdec; - - amrwbdec = GST_AMRWBDEC (object); - - gst_adapter_clear (amrwbdec->adapter); - g_object_unref (amrwbdec->adapter); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static gboolean -gst_amrwbdec_setcaps (GstPad * pad, GstCaps * caps) -{ - GstStructure *structure; - GstAmrwbDec *amrwbdec; - GstCaps *copy; - - amrwbdec = GST_AMRWBDEC (gst_pad_get_parent (pad)); - - structure = gst_caps_get_structure (caps, 0); - - /* get channel count */ - gst_structure_get_int (structure, "channels", &amrwbdec->channels); - gst_structure_get_int (structure, "rate", &amrwbdec->rate); - - /* create reverse caps */ - copy = gst_caps_new_simple ("audio/x-raw-int", - "channels", G_TYPE_INT, amrwbdec->channels, - "width", G_TYPE_INT, 16, - "depth", G_TYPE_INT, 16, - "endianness", G_TYPE_INT, G_BYTE_ORDER, - "rate", G_TYPE_INT, amrwbdec->rate, "signed", G_TYPE_BOOLEAN, TRUE, NULL); - - amrwbdec->duration = gst_util_uint64_scale_int (GST_SECOND, L_FRAME16k, - amrwbdec->rate * amrwbdec->channels); - - gst_pad_set_caps (amrwbdec->srcpad, copy); - gst_caps_unref (copy); - - gst_object_unref (amrwbdec); - - return TRUE; -} - -static gboolean -gst_amrwbdec_event (GstPad * pad, GstEvent * event) -{ - GstAmrwbDec *amrwbdec; - gboolean ret = TRUE; - - amrwbdec = GST_AMRWBDEC (gst_pad_get_parent (pad)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_START: - ret = gst_pad_push_event (amrwbdec->srcpad, event); - break; - case GST_EVENT_FLUSH_STOP: - ret = gst_pad_push_event (amrwbdec->srcpad, event); - gst_adapter_clear (amrwbdec->adapter); - amrwbdec->ts = -1; - break; - case GST_EVENT_EOS: - gst_adapter_clear (amrwbdec->adapter); - ret = gst_pad_push_event (amrwbdec->srcpad, event); - break; - case GST_EVENT_NEWSEGMENT: - { - GstFormat format; - gdouble rate, arate; - gint64 start, stop, time; - gboolean update; - - gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, - &start, &stop, &time); - - /* we need time for now */ - if (format != GST_FORMAT_TIME) - goto newseg_wrong_format; - - GST_DEBUG_OBJECT (amrwbdec, - "newsegment: update %d, rate %g, arate %g, start %" GST_TIME_FORMAT - ", stop %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, - update, rate, arate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop), - GST_TIME_ARGS (time)); - - /* now configure the values */ - gst_segment_set_newsegment_full (&amrwbdec->segment, update, - rate, arate, format, start, stop, time); - ret = gst_pad_push_event (amrwbdec->srcpad, event); - } - break; - default: - ret = gst_pad_push_event (amrwbdec->srcpad, event); - break; - } -done: - gst_object_unref (amrwbdec); - - return ret; - - /* ERRORS */ -newseg_wrong_format: - { - GST_DEBUG_OBJECT (amrwbdec, "received non TIME newsegment"); - goto done; - } -} - -static GstFlowReturn -gst_amrwbdec_chain (GstPad * pad, GstBuffer * buffer) -{ - GstAmrwbDec *amrwbdec; - GstFlowReturn ret = GST_FLOW_OK; - - amrwbdec = GST_AMRWBDEC (gst_pad_get_parent (pad)); - - if (amrwbdec->rate == 0 || amrwbdec->channels == 0) - goto not_negotiated; - - /* discontinuity, don't combine samples before and after the - * DISCONT */ - if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) { - gst_adapter_clear (amrwbdec->adapter); - amrwbdec->ts = -1; - amrwbdec->discont = TRUE; - } - - /* take latest timestamp, FIXME timestamp is the one of the - * first buffer in the adapter. */ - if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) - amrwbdec->ts = GST_BUFFER_TIMESTAMP (buffer); - - gst_adapter_push (amrwbdec->adapter, buffer); - - while (TRUE) { - GstBuffer *out; - const guint8 *data; - gint block, mode; - - /* need to peek data to get the size */ - if (gst_adapter_available (amrwbdec->adapter) < 1) - break; - data = gst_adapter_peek (amrwbdec->adapter, 1); - - /* get size */ - mode = (data[0] >> 3) & 0x0F; - block = block_size[mode]; - - GST_DEBUG_OBJECT (amrwbdec, "mode %d, block %d", mode, block); - - if (!block || gst_adapter_available (amrwbdec->adapter) < block) - break; - - /* the library seems to write into the source data, hence the copy. */ - data = gst_adapter_take (amrwbdec->adapter, block); - - /* get output */ - out = gst_buffer_new_and_alloc (sizeof (gint16) * L_FRAME16k); - - GST_BUFFER_DURATION (out) = amrwbdec->duration; - GST_BUFFER_TIMESTAMP (out) = amrwbdec->ts; - - if (amrwbdec->ts != -1) - amrwbdec->ts += amrwbdec->duration; - if (amrwbdec->discont) { - GST_BUFFER_FLAG_SET (out, GST_BUFFER_FLAG_DISCONT); - amrwbdec->discont = FALSE; - } - - gst_buffer_set_caps (out, GST_PAD_CAPS (amrwbdec->srcpad)); - - /* decode */ - D_IF_decode (amrwbdec->handle, (UWord8 *) data, - (Word16 *) GST_BUFFER_DATA (out), _good_frame); - - g_free ((gpointer) data); - - /* send out */ - ret = gst_pad_push (amrwbdec->srcpad, out); - } - - gst_object_unref (amrwbdec); - return ret; - - /* ERRORS */ -not_negotiated: - { - GST_ELEMENT_ERROR (amrwbdec, STREAM, TYPE_NOT_FOUND, (NULL), - ("Decoder is not initialized")); - gst_object_unref (amrwbdec); - return GST_FLOW_NOT_NEGOTIATED; - } -} - -static GstStateChangeReturn -gst_amrwbdec_state_change (GstElement * element, GstStateChange transition) -{ - GstAmrwbDec *amrwbdec; - GstStateChangeReturn ret; - - amrwbdec = GST_AMRWBDEC (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - if (!(amrwbdec->handle = D_IF_init ())) - goto init_failed; - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - gst_adapter_clear (amrwbdec->adapter); - amrwbdec->rate = 0; - amrwbdec->channels = 0; - amrwbdec->ts = -1; - amrwbdec->discont = TRUE; - gst_segment_init (&amrwbdec->segment, GST_FORMAT_TIME); - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_NULL: - D_IF_exit (amrwbdec->handle); - break; - default: - break; - } - - return ret; - - /* ERRORS */ -init_failed: - { - GST_ELEMENT_ERROR (amrwbdec, LIBRARY, INIT, (NULL), - ("Failed to open AMR Decoder")); - return GST_STATE_CHANGE_FAILURE; - } -} diff --git a/ext/amrwb/gstamrwbdec.h b/ext/amrwb/gstamrwbdec.h deleted file mode 100644 index a7a5ce71..00000000 --- a/ext/amrwb/gstamrwbdec.h +++ /dev/null @@ -1,78 +0,0 @@ -/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) plugin - * Copyright (C) 2006 Edgard Lima <edgard.lima@indt.org.br> - * - * 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_AMRWBDEC_H__ -#define __GST_AMRWBDEC_H__ - -#include <gst/gst.h> -#include <gst/base/gstadapter.h> -#include <amrwb/typedef.h> -#include <amrwb/dec_if.h> -#include <amrwb/if_rom.h> - -G_BEGIN_DECLS - -#define GST_TYPE_AMRWBDEC \ - (gst_amrwbdec_get_type()) -#define GST_AMRWBDEC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_AMRWBDEC, GstAmrwbDec)) -#define GST_AMRWBDEC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_AMRWBDEC, GstAmrwbDecClass)) -#define GST_IS_AMRWBDEC(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_AMRWBDEC)) -#define GST_IS_AMRWBDEC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_AMRWBDEC)) - -typedef struct _GstAmrwbDec GstAmrwbDec; -typedef struct _GstAmrwbDecClass GstAmrwbDecClass; - -/** - * GstAmrwbDec: - * - * Opaque data structure. - */ -struct _GstAmrwbDec { - GstElement element; - - /* pads */ - GstPad *sinkpad, *srcpad; - guint64 ts; - - GstAdapter *adapter; - - /* library handle */ - void *handle; - - /* output settings */ - gint channels, rate; - gint duration; - - GstSegment segment; - gboolean discont; -}; - -struct _GstAmrwbDecClass { - GstElementClass parent_class; -}; - -GType gst_amrwbdec_get_type (void); - -G_END_DECLS - -#endif /* __GST_AMRWBDEC_H__ */ diff --git a/ext/amrwb/gstamrwbparse.c b/ext/amrwb/gstamrwbparse.c deleted file mode 100644 index bcee1704..00000000 --- a/ext/amrwb/gstamrwbparse.c +++ /dev/null @@ -1,767 +0,0 @@ -/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) plugin - * Copyright (C) 2006 Edgard Lima <edgard.lima@indt.org.br> - * - * 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-amrwbparse - * @see_also: #GstAmrwbDec, #GstAmrwbEnc - * - * This is an AMR wideband parser. - * - * <refsect2> - * <title>Example launch line</title> - * |[ - * gst-launch filesrc location=abc.amr ! amrwbparse ! amrwbdec ! audioresample ! audioconvert ! alsasink - * ]| - * </refsect2> - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <string.h> -#include "gstamrwbparse.h" - -static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/AMR-WB, " - "rate = (int) 16000, " "channels = (int) 1") - ); - -static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-amr-wb-sh") - ); - -GST_DEBUG_CATEGORY_STATIC (gst_amrwbparse_debug); -#define GST_CAT_DEFAULT gst_amrwbparse_debug - -extern const UWord8 block_size[]; - -#define AMRWB_HEADER_SIZE 9 -#define AMRWB_HEADER_STR "#!AMR-WB\n" - -static void gst_amrwbparse_base_init (gpointer klass); -static void gst_amrwbparse_class_init (GstAmrwbParseClass * klass); -static void gst_amrwbparse_init (GstAmrwbParse * amrwbparse, - GstAmrwbParseClass * klass); - -static const GstQueryType *gst_amrwbparse_querytypes (GstPad * pad); -static gboolean gst_amrwbparse_query (GstPad * pad, GstQuery * query); - -static gboolean gst_amrwbparse_sink_event (GstPad * pad, GstEvent * event); -static gboolean gst_amrwbparse_src_event (GstPad * pad, GstEvent * event); -static GstFlowReturn gst_amrwbparse_chain (GstPad * pad, GstBuffer * buffer); -static void gst_amrwbparse_loop (GstPad * pad); -static gboolean gst_amrwbparse_sink_activate (GstPad * sinkpad); -static gboolean gst_amrwbparse_sink_activate_pull (GstPad * sinkpad, - gboolean active); -static gboolean gst_amrwbparse_sink_activate_push (GstPad * sinkpad, - gboolean active); -static GstStateChangeReturn gst_amrwbparse_state_change (GstElement * element, - GstStateChange transition); - -static void gst_amrwbparse_finalize (GObject * object); - -#define _do_init(bla) \ - GST_DEBUG_CATEGORY_INIT (gst_amrwbparse_debug, "amrwbparse", 0, "AMR-WB audio stream parser"); - -GST_BOILERPLATE_FULL (GstAmrwbParse, gst_amrwbparse, GstElement, - GST_TYPE_ELEMENT, _do_init); - -static void -gst_amrwbparse_base_init (gpointer klass) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - GstElementDetails details = GST_ELEMENT_DETAILS ("AMR-WB audio stream parser", - "Codec/Parser/Audio", - "Adaptive Multi-Rate WideBand audio parser", - "Renato Filho <renato.filho@indt.org.br>"); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&sink_template)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&src_template)); - - gst_element_class_set_details (element_class, &details); - -} - -static void -gst_amrwbparse_class_init (GstAmrwbParseClass * klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - - object_class->finalize = gst_amrwbparse_finalize; - - element_class->change_state = GST_DEBUG_FUNCPTR (gst_amrwbparse_state_change); -} - -static void -gst_amrwbparse_init (GstAmrwbParse * amrwbparse, GstAmrwbParseClass * klass) -{ - /* create the sink pad */ - amrwbparse->sinkpad = - gst_pad_new_from_static_template (&sink_template, "sink"); - gst_pad_set_chain_function (amrwbparse->sinkpad, - GST_DEBUG_FUNCPTR (gst_amrwbparse_chain)); - gst_pad_set_event_function (amrwbparse->sinkpad, - GST_DEBUG_FUNCPTR (gst_amrwbparse_sink_event)); - gst_pad_set_activate_function (amrwbparse->sinkpad, - gst_amrwbparse_sink_activate); - gst_pad_set_activatepull_function (amrwbparse->sinkpad, - gst_amrwbparse_sink_activate_pull); - gst_pad_set_activatepush_function (amrwbparse->sinkpad, - gst_amrwbparse_sink_activate_push); - gst_element_add_pad (GST_ELEMENT (amrwbparse), amrwbparse->sinkpad); - - /* create the src pad */ - amrwbparse->srcpad = gst_pad_new_from_static_template (&src_template, "src"); - gst_pad_set_event_function (amrwbparse->srcpad, - GST_DEBUG_FUNCPTR (gst_amrwbparse_src_event)); - gst_pad_set_query_function (amrwbparse->srcpad, - GST_DEBUG_FUNCPTR (gst_amrwbparse_query)); - gst_pad_set_query_type_function (amrwbparse->srcpad, - GST_DEBUG_FUNCPTR (gst_amrwbparse_querytypes)); - gst_element_add_pad (GST_ELEMENT (amrwbparse), amrwbparse->srcpad); - - amrwbparse->adapter = gst_adapter_new (); - - /* init rest */ - amrwbparse->ts = 0; -} - -static void -gst_amrwbparse_finalize (GObject * object) -{ - GstAmrwbParse *amrwbparse; - - amrwbparse = GST_AMRWBPARSE (object); - - gst_adapter_clear (amrwbparse->adapter); - g_object_unref (amrwbparse->adapter); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - - -static const GstQueryType * -gst_amrwbparse_querytypes (GstPad * pad) -{ - static const GstQueryType list[] = { - GST_QUERY_POSITION, - GST_QUERY_DURATION, - 0 - }; - - return list; -} - -static gboolean -gst_amrwbparse_query (GstPad * pad, GstQuery * query) -{ - GstAmrwbParse *amrwbparse; - gboolean res = TRUE; - - amrwbparse = GST_AMRWBPARSE (gst_pad_get_parent (pad)); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_POSITION: - { - GstFormat format; - gint64 cur; - - gst_query_parse_position (query, &format, NULL); - - if (format != GST_FORMAT_TIME) { - res = FALSE; - break; - } - - cur = amrwbparse->ts; - - gst_query_set_position (query, GST_FORMAT_TIME, cur); - res = TRUE; - break; - } - case GST_QUERY_DURATION: - { - GstFormat format; - gint64 tot; - GstPad *peer; - - gst_query_parse_duration (query, &format, NULL); - - if (format != GST_FORMAT_TIME) { - res = FALSE; - break; - } - - tot = -1; - res = FALSE; - - peer = gst_pad_get_peer (amrwbparse->sinkpad); - if (peer) { - GstFormat pformat; - gint64 ptot; - - pformat = GST_FORMAT_BYTES; - res = gst_pad_query_duration (peer, &pformat, &ptot); - if (res && amrwbparse->block) { - tot = gst_util_uint64_scale_int (ptot, 20 * GST_MSECOND, - amrwbparse->block); - } - gst_object_unref (GST_OBJECT (peer)); - } - gst_query_set_duration (query, GST_FORMAT_TIME, tot); - res = TRUE; - break; - } - default: - res = gst_pad_query_default (pad, query); - break; - } - - gst_object_unref (amrwbparse); - return res; -} - - -static gboolean -gst_amrwbparse_handle_pull_seek (GstAmrwbParse * amrwbparse, GstPad * pad, - GstEvent * event) -{ - GstFormat format; - gdouble rate; - GstSeekFlags flags; - GstSeekType cur_type, stop_type; - gint64 cur, stop; - gint64 byte_cur = -1, byte_stop = -1; - gboolean flush; - - gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, - &stop_type, &stop); - - GST_DEBUG_OBJECT (amrwbparse, "Performing seek to %" GST_TIME_FORMAT, - GST_TIME_ARGS (cur)); - - /* For any format other than TIME, see if upstream handles - * it directly or fail. For TIME, try upstream, but do it ourselves if - * it fails upstream */ - if (format != GST_FORMAT_TIME) { - return gst_pad_push_event (amrwbparse->sinkpad, event); - } else { - if (gst_pad_push_event (amrwbparse->sinkpad, event)) - return TRUE; - } - - flush = flags & GST_SEEK_FLAG_FLUSH; - - /* send flush start */ - if (flush) - gst_pad_push_event (amrwbparse->sinkpad, gst_event_new_flush_start ()); - /* we only handle FLUSH seeks at the moment */ - else - return FALSE; - - /* grab streaming lock, this should eventually be possible, either - * because the task is paused or our streaming thread stopped - * because our peer is flushing. */ - GST_PAD_STREAM_LOCK (amrwbparse->sinkpad); - - /* Convert the TIME to the appropriate BYTE position at which to resume - * decoding. */ - cur = cur / (20 * GST_MSECOND) * (20 * GST_MSECOND); - if (cur != -1) - byte_cur = amrwbparse->block * (cur / 20 / GST_MSECOND) + AMRWB_HEADER_SIZE; - if (stop != -1) - byte_stop = - amrwbparse->block * (stop / 20 / GST_MSECOND) + AMRWB_HEADER_SIZE; - amrwbparse->offset = byte_cur; - amrwbparse->ts = cur; - - GST_DEBUG_OBJECT (amrwbparse, "Seeking to byte range %" G_GINT64_FORMAT - " to %" G_GINT64_FORMAT, byte_cur, cur); - - /* and prepare to continue streaming */ - /* send flush stop, peer will accept data and events again. We - * are not yet providing data as we still have the STREAM_LOCK. */ - gst_pad_push_event (amrwbparse->sinkpad, gst_event_new_flush_stop ()); - gst_pad_push_event (amrwbparse->srcpad, gst_event_new_new_segment (FALSE, - rate, format, cur, -1, cur)); - - /* and restart the task in case it got paused explicitely or by - * the FLUSH_START event we pushed out. */ - gst_pad_start_task (amrwbparse->sinkpad, - (GstTaskFunction) gst_amrwbparse_loop, amrwbparse->sinkpad); - - /* and release the lock again so we can continue streaming */ - GST_PAD_STREAM_UNLOCK (amrwbparse->sinkpad); - - return TRUE; -} - -static gboolean -gst_amrwbparse_handle_push_seek (GstAmrwbParse * amrwbparse, GstPad * pad, - GstEvent * event) -{ - GstFormat format; - gdouble rate; - GstSeekFlags flags; - GstSeekType cur_type, stop_type; - gint64 cur, stop; - gint64 byte_cur = -1, byte_stop = -1; - - gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, - &stop_type, &stop); - - GST_DEBUG_OBJECT (amrwbparse, "Performing seek to %" GST_TIME_FORMAT, - GST_TIME_ARGS (cur)); - - /* For any format other than TIME, see if upstream handles - * it directly or fail. For TIME, try upstream, but do it ourselves if - * it fails upstream */ - if (format != GST_FORMAT_TIME) { - return gst_pad_push_event (amrwbparse->sinkpad, event); - } else { - if (gst_pad_push_event (amrwbparse->sinkpad, event)) - return TRUE; - } - - /* Convert the TIME to the appropriate BYTE position at which to resume - * decoding. */ - cur = cur / (20 * GST_MSECOND) * (20 * GST_MSECOND); - if (cur != -1) - byte_cur = amrwbparse->block * (cur / 20 / GST_MSECOND) + AMRWB_HEADER_SIZE; - if (stop != -1) - byte_stop = - amrwbparse->block * (stop / 20 / GST_MSECOND) + AMRWB_HEADER_SIZE; - amrwbparse->ts = cur; - - GST_DEBUG_OBJECT (amrwbparse, "Seeking to byte range %" G_GINT64_FORMAT - " to %" G_GINT64_FORMAT, byte_cur, byte_stop); - - /* Send BYTE based seek upstream */ - event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, - byte_cur, stop_type, byte_stop); - - return gst_pad_push_event (amrwbparse->sinkpad, event); -} - -static gboolean -gst_amrwbparse_src_event (GstPad * pad, GstEvent * event) -{ - GstAmrwbParse *amrwbparse = GST_AMRWBPARSE (gst_pad_get_parent (pad)); - gboolean res; - - GST_DEBUG_OBJECT (amrwbparse, "handling event %d", GST_EVENT_TYPE (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK: - if (amrwbparse->seek_handler) - res = amrwbparse->seek_handler (amrwbparse, pad, event); - else - res = FALSE; - break; - default: - res = gst_pad_push_event (amrwbparse->sinkpad, event); - break; - } - gst_object_unref (amrwbparse); - - return res; -} - - -/* - * Data reading. - */ -static gboolean -gst_amrwbparse_sink_event (GstPad * pad, GstEvent * event) -{ - GstAmrwbParse *amrwbparse; - gboolean res; - - amrwbparse = GST_AMRWBPARSE (gst_pad_get_parent (pad)); - - GST_LOG ("handling event %d", GST_EVENT_TYPE (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_START: - res = gst_pad_push_event (amrwbparse->srcpad, event); - break; - case GST_EVENT_FLUSH_STOP: - gst_adapter_clear (amrwbparse->adapter); - gst_segment_init (&amrwbparse->segment, GST_FORMAT_TIME); - res = gst_pad_push_event (amrwbparse->srcpad, event); - break; - case GST_EVENT_EOS: - res = gst_pad_push_event (amrwbparse->srcpad, event); - break; - case GST_EVENT_NEWSEGMENT: - { - /* eat for now, we send a newsegment at start with infinite - * duration. */ - gst_event_unref (event); - res = TRUE; - break; - } - default: - res = gst_pad_push_event (amrwbparse->srcpad, event); - break; - } - gst_object_unref (amrwbparse); - - return res; -} - -/* streaming mode */ -static GstFlowReturn -gst_amrwbparse_chain (GstPad * pad, GstBuffer * buffer) -{ - GstAmrwbParse *amrwbparse; - GstFlowReturn res = GST_FLOW_OK; - gint mode; - const guint8 *data; - GstBuffer *out; - GstClockTime timestamp; - - amrwbparse = GST_AMRWBPARSE (gst_pad_get_parent (pad)); - - timestamp = GST_BUFFER_TIMESTAMP (buffer); - if (GST_CLOCK_TIME_IS_VALID (timestamp)) { - GST_DEBUG_OBJECT (amrwbparse, "Lock on timestamp %" GST_TIME_FORMAT, - GST_TIME_ARGS (timestamp)); - amrwbparse->ts = timestamp; - } - - gst_adapter_push (amrwbparse->adapter, buffer); - - /* init */ - if (amrwbparse->need_header) { - GstEvent *segev; - GstCaps *caps; - - if (gst_adapter_available (amrwbparse->adapter) < AMRWB_HEADER_SIZE) - goto done; - - data = gst_adapter_peek (amrwbparse->adapter, AMRWB_HEADER_SIZE); - if (memcmp (data, AMRWB_HEADER_STR, AMRWB_HEADER_SIZE) != 0) - goto done; - - gst_adapter_flush (amrwbparse->adapter, AMRWB_HEADER_SIZE); - - amrwbparse->need_header = FALSE; - - caps = gst_caps_new_simple ("audio/AMR-WB", - "rate", G_TYPE_INT, 16000, "channels", G_TYPE_INT, 1, NULL); - gst_pad_set_caps (amrwbparse->srcpad, caps); - gst_caps_unref (caps); - - GST_DEBUG_OBJECT (amrwbparse, "Sending first segment"); - segev = gst_event_new_new_segment_full (FALSE, 1.0, 1.0, - GST_FORMAT_TIME, 0, -1, 0); - - gst_pad_push_event (amrwbparse->srcpad, segev); - } - - while (TRUE) { - if (gst_adapter_available (amrwbparse->adapter) < 1) - break; - - data = gst_adapter_peek (amrwbparse->adapter, 1); - - /* get size */ - mode = (data[0] >> 3) & 0x0F; - amrwbparse->block = block_size[mode] + 1; /* add one for the mode */ - - if (gst_adapter_available (amrwbparse->adapter) < amrwbparse->block) - break; - - out = gst_buffer_new_and_alloc (amrwbparse->block); - - data = gst_adapter_peek (amrwbparse->adapter, amrwbparse->block); - memcpy (GST_BUFFER_DATA (out), data, amrwbparse->block); - - /* timestamp, all constants that won't overflow */ - GST_BUFFER_DURATION (out) = GST_SECOND * L_FRAME16k / 16000; - GST_BUFFER_TIMESTAMP (out) = amrwbparse->ts; - if (GST_CLOCK_TIME_IS_VALID (amrwbparse->ts)) - amrwbparse->ts += GST_BUFFER_DURATION (out); - - gst_buffer_set_caps (out, GST_PAD_CAPS (amrwbparse->srcpad)); - - GST_DEBUG_OBJECT (amrwbparse, "Pushing %d bytes of data", - amrwbparse->block); - - res = gst_pad_push (amrwbparse->srcpad, out); - - gst_adapter_flush (amrwbparse->adapter, amrwbparse->block); - } -done: - - gst_object_unref (amrwbparse); - return res; -} - -static gboolean -gst_amrwbparse_pull_header (GstAmrwbParse * amrwbparse) -{ - GstBuffer *buffer; - GstFlowReturn ret; - guint8 *data; - gint size; - - ret = gst_pad_pull_range (amrwbparse->sinkpad, G_GUINT64_CONSTANT (0), - AMRWB_HEADER_SIZE, &buffer); - if (ret != GST_FLOW_OK) - return FALSE; - - data = GST_BUFFER_DATA (buffer); - size = GST_BUFFER_SIZE (buffer); - - if (size < AMRWB_HEADER_SIZE) - goto not_enough; - - if (memcmp (data, AMRWB_HEADER_STR, AMRWB_HEADER_SIZE)) - goto no_header; - - gst_buffer_unref (buffer); - - amrwbparse->offset = AMRWB_HEADER_SIZE; - return TRUE; - -not_enough: - { - gst_buffer_unref (buffer); - return FALSE; - } -no_header: - { - gst_buffer_unref (buffer); - return FALSE; - } -} - -/* random access mode, could just read a fixed size buffer and push it to - * the chain function but we don't... */ -static void -gst_amrwbparse_loop (GstPad * pad) -{ - GstAmrwbParse *amrwbparse; - GstBuffer *buffer; - guint8 *data; - gint size; - gint block, mode; - GstFlowReturn ret = GST_FLOW_OK; - - amrwbparse = GST_AMRWBPARSE (GST_PAD_PARENT (pad)); - - /* init */ - if (G_UNLIKELY (amrwbparse->need_header)) { - GstCaps *caps; - - if (!gst_amrwbparse_pull_header (amrwbparse)) { - GST_ELEMENT_ERROR (amrwbparse, STREAM, WRONG_TYPE, (NULL), (NULL)); - GST_LOG_OBJECT (amrwbparse, "could not read header"); - goto need_pause; - } - - caps = gst_caps_new_simple ("audio/AMR-WB", - "rate", G_TYPE_INT, 16000, "channels", G_TYPE_INT, 1, NULL); - gst_pad_set_caps (amrwbparse->srcpad, caps); - gst_caps_unref (caps); - - GST_DEBUG_OBJECT (amrwbparse, "Sending newsegment event"); - gst_pad_push_event (amrwbparse->srcpad, - gst_event_new_new_segment_full (FALSE, 1.0, 1.0, - GST_FORMAT_TIME, 0, -1, 0)); - - amrwbparse->need_header = FALSE; - } - - ret = - gst_pad_pull_range (amrwbparse->sinkpad, amrwbparse->offset, 1, &buffer); - - if (ret == GST_FLOW_UNEXPECTED) - goto eos; - else if (ret != GST_FLOW_OK) - goto need_pause; - - data = GST_BUFFER_DATA (buffer); - size = GST_BUFFER_SIZE (buffer); - - /* EOS */ - if (size < 1) { - gst_buffer_unref (buffer); - goto eos; - } - - /* get size */ - mode = (data[0] >> 3) & 0x0F; - block = block_size[mode]; /* add one for the mode */ - - gst_buffer_unref (buffer); - - ret = gst_pad_pull_range (amrwbparse->sinkpad, - amrwbparse->offset, block, &buffer); - - if (ret == GST_FLOW_UNEXPECTED) - goto eos; - else if (ret != GST_FLOW_OK) - goto need_pause; - - amrwbparse->offset += block; - - /* output */ - GST_BUFFER_DURATION (buffer) = GST_SECOND * L_FRAME16k / 16000; - GST_BUFFER_TIMESTAMP (buffer) = amrwbparse->ts; - - gst_buffer_set_caps (buffer, - (GstCaps *) gst_pad_get_pad_template_caps (amrwbparse->srcpad)); - - ret = gst_pad_push (amrwbparse->srcpad, buffer); - - if (ret != GST_FLOW_OK) { - GST_DEBUG_OBJECT (amrwbparse, "Flow: %s", gst_flow_get_name (ret)); - if (GST_FLOW_IS_FATAL (ret)) { - GST_ELEMENT_ERROR (amrwbparse, STREAM, FAILED, (NULL), /* _("Internal data flow error.")), */ - ("streaming task paused, reason: %s", gst_flow_get_name (ret))); - gst_pad_push_event (pad, gst_event_new_eos ()); - } - goto need_pause; - } - - amrwbparse->ts += GST_BUFFER_DURATION (buffer); - - return; - -need_pause: - { - GST_LOG_OBJECT (amrwbparse, "pausing task"); - gst_pad_pause_task (pad); - return; - } -eos: - { - GST_LOG_OBJECT (amrwbparse, "pausing task %d", ret); - gst_pad_push_event (amrwbparse->srcpad, gst_event_new_eos ()); - gst_pad_pause_task (pad); - return; - } -} - -static gboolean -gst_amrwbparse_sink_activate (GstPad * sinkpad) -{ - gboolean result = FALSE; - GstAmrwbParse *amrwbparse; - - amrwbparse = GST_AMRWBPARSE (gst_pad_get_parent (sinkpad)); - - if (gst_pad_check_pull_range (sinkpad)) { - GST_DEBUG ("Trying to activate in pull mode"); - amrwbparse->seekable = TRUE; - amrwbparse->ts = 0; - result = gst_pad_activate_pull (sinkpad, TRUE); - } else { - GST_DEBUG ("Try to activate in push mode"); - amrwbparse->seekable = FALSE; - result = gst_pad_activate_push (sinkpad, TRUE); - } - - gst_object_unref (amrwbparse); - return result; -} - - - -static gboolean -gst_amrwbparse_sink_activate_push (GstPad * sinkpad, gboolean active) -{ - GstAmrwbParse *amrwbparse = GST_AMRWBPARSE (gst_pad_get_parent (sinkpad)); - - if (active) { - amrwbparse->seek_handler = gst_amrwbparse_handle_push_seek; - } else { - amrwbparse->seek_handler = NULL; - } - - gst_object_unref (amrwbparse); - return TRUE; -} - -static gboolean -gst_amrwbparse_sink_activate_pull (GstPad * sinkpad, gboolean active) -{ - gboolean result; - GstAmrwbParse *amrwbparse; - - amrwbparse = GST_AMRWBPARSE (gst_pad_get_parent (sinkpad)); - if (active) { - amrwbparse->seek_handler = gst_amrwbparse_handle_pull_seek; - result = gst_pad_start_task (sinkpad, - (GstTaskFunction) gst_amrwbparse_loop, sinkpad); - } else { - amrwbparse->seek_handler = NULL; - result = gst_pad_stop_task (sinkpad); - } - - gst_object_unref (amrwbparse); - return result; -} - - -static GstStateChangeReturn -gst_amrwbparse_state_change (GstElement * element, GstStateChange transition) -{ - GstAmrwbParse *amrwbparse; - GstStateChangeReturn ret; - - amrwbparse = GST_AMRWBPARSE (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - amrwbparse->need_header = TRUE; - amrwbparse->ts = -1; - amrwbparse->block = 0; - gst_segment_init (&amrwbparse->segment, GST_FORMAT_TIME); - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_NULL: - break; - default: - break; - } - - return ret; -} diff --git a/ext/amrwb/gstamrwbparse.h b/ext/amrwb/gstamrwbparse.h deleted file mode 100644 index 6cbe7b74..00000000 --- a/ext/amrwb/gstamrwbparse.h +++ /dev/null @@ -1,77 +0,0 @@ -/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) plugin - * Copyright (C) 2006 Edgard Lima <edgard.lima@indt.org.br> - * - * 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_AMRWBPARSE_H__ -#define __GST_AMRWBPARSE_H__ - -#include <gst/gst.h> -#include <gst/base/gstadapter.h> -#include <amrwb/typedef.h> -#include <amrwb/dec_if.h> -#include <amrwb/if_rom.h> - -G_BEGIN_DECLS - -#define GST_TYPE_AMRWBPARSE \ - (gst_amrwbparse_get_type()) -#define GST_AMRWBPARSE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_AMRWBPARSE, GstAmrwbParse)) -#define GST_AMRWBPARSE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_AMRWBPARSE, GstAmrwbParseClass)) -#define GST_IS_AMRWBPARSE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_AMRWBPARSE)) -#define GST_IS_AMRWBPARSE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_AMRWBPARSE)) - -typedef struct _GstAmrwbParse GstAmrwbParse; -typedef struct _GstAmrwbParseClass GstAmrwbParseClass; - -typedef gboolean (*GstAmrwbSeekHandler) (GstAmrwbParse * amrwbparse, GstPad * pad, - GstEvent * event); - -struct _GstAmrwbParse { - GstElement element; - - /* pads */ - GstPad *sinkpad, *srcpad; - - GstAdapter *adapter; - - gboolean seekable; - gboolean need_header; - gint64 offset; - gint block; - - GstAmrwbSeekHandler seek_handler; - - guint64 ts; - - /* for seeking etc */ - GstSegment segment; -}; - -struct _GstAmrwbParseClass { - GstElementClass parent_class; -}; - -GType gst_amrwbparse_get_type (void); - -G_END_DECLS - -#endif /* __GST_AMRWBPARSE_H__ */ diff --git a/ext/amrwbenc/GstAmrwbEnc.prs b/ext/amrwbenc/GstAmrwbEnc.prs new file mode 100644 index 00000000..57658494 --- /dev/null +++ b/ext/amrwbenc/GstAmrwbEnc.prs @@ -0,0 +1,11 @@ +[_presets_] +version=0.10 +element-name=GstAmrwbEnc + +[enhance-size] +_meta/comment=Maximize compression, lowest bitrate +band-mode=0 + +[enhance-quality] +_meta/comment=Maximize quality, highest bitrate +band-mode=8 diff --git a/ext/amrwbenc/Makefile.am b/ext/amrwbenc/Makefile.am new file mode 100644 index 00000000..4c4ea3b3 --- /dev/null +++ b/ext/amrwbenc/Makefile.am @@ -0,0 +1,19 @@ +plugin_LTLIBRARIES = libgstamrwbenc.la + +libgstamrwbenc_la_SOURCES = \ + gstamrwb.c \ + gstamrwbenc.c + +libgstamrwbenc_la_CFLAGS = $(GST_CFLAGS) $(AMRWB_CFLAGS) +libgstamrwbenc_la_LIBADD = $(GST_BASE_LIBS) $(AMRWB_LIBS) +libgstamrwbenc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstamrwbenc_la_LIBTOOLFLAGS = --tag=disable-static + +noinst_HEADERS = \ + gstamrwbenc.h + +presetdir = $(datadir)/gstreamer-$(GST_MAJORMINOR)/presets +preset_DATA = GstAmrwbEnc.prs + +EXTRA_DIST = $(preset_DATA) + diff --git a/ext/amrwb/README b/ext/amrwbenc/README index 82d33715..9d158728 100644 --- a/ext/amrwb/README +++ b/ext/amrwbenc/README @@ -1,7 +1,7 @@ -Compiling AMRWB codec: -====================== +Compiling AMRWB encoder: +======================== -To compile the amrwb codec, you need to download the source code from +To compile the amrwb encoder, you need to download the source code from "http://www.3gpp.org/ftp/Specs/html-info/26204.htm" and uncompress the files inside an amrwb-code directory; diff --git a/ext/amrwb/gstamrwb.c b/ext/amrwbenc/gstamrwb.c index fd47b371..6cd734b1 100644 --- a/ext/amrwb/gstamrwb.c +++ b/ext/amrwbenc/gstamrwb.c @@ -1,4 +1,4 @@ -/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) plugin +/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) Encoder plugin * Copyright (C) 2006 Edgard Lima <edgard.lima@indt.org.br> * * This library is free software; you can redistribute it and/or @@ -21,25 +21,19 @@ #include "config.h" #endif -#include "gstamrwbdec.h" #include "gstamrwbenc.h" -#include "gstamrwbparse.h" static gboolean plugin_init (GstPlugin * plugin) { - return gst_element_register (plugin, "amrwbdec", - GST_RANK_PRIMARY, GST_TYPE_AMRWBDEC) && - gst_element_register (plugin, "amrwbparse", - GST_RANK_PRIMARY, GST_TYPE_AMRWBPARSE) && - gst_element_register (plugin, "amrwbenc", + return gst_element_register (plugin, "amrwbenc", GST_RANK_NONE, GST_TYPE_AMRWBENC); } GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, - "amrwb", - "Adaptive Multi-Rate Wide-Band", + "amrwbenc", + "Adaptive Multi-Rate Wide-Band Encoder", plugin_init, VERSION, GST_LICENSE_UNKNOWN, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); diff --git a/ext/amrwb/gstamrwbenc.c b/ext/amrwbenc/gstamrwbenc.c index caf2adb7..3b46d3e9 100644 --- a/ext/amrwb/gstamrwbenc.c +++ b/ext/amrwbenc/gstamrwbenc.c @@ -116,8 +116,21 @@ static gboolean gst_amrwbenc_setcaps (GstPad * pad, GstCaps * caps); static GstStateChangeReturn gst_amrwbenc_state_change (GstElement * element, GstStateChange transition); -#define _do_init(bla) \ - GST_DEBUG_CATEGORY_INIT (gst_amrwbenc_debug, "amrwbenc", 0, "AMR-WB audio encoder"); +static void +_do_init (GType object_type) +{ + const GInterfaceInfo preset_interface_info = { + NULL, /* interface init */ + NULL, /* interface finalize */ + NULL /* interface_data */ + }; + + g_type_add_interface_static (object_type, GST_TYPE_PRESET, + &preset_interface_info); + + GST_DEBUG_CATEGORY_INIT (gst_amrwbenc_debug, "amrwbenc", 0, + "AMR-WB audio encoder"); +} GST_BOILERPLATE_FULL (GstAmrwbEnc, gst_amrwbenc, GstElement, GST_TYPE_ELEMENT, _do_init); diff --git a/ext/amrwb/gstamrwbenc.h b/ext/amrwbenc/gstamrwbenc.h index 034a5bed..034a5bed 100644 --- a/ext/amrwb/gstamrwbenc.h +++ b/ext/amrwbenc/gstamrwbenc.h diff --git a/ext/gme/Makefile.am b/ext/gme/Makefile.am new file mode 100644 index 00000000..f9a7b139 --- /dev/null +++ b/ext/gme/Makefile.am @@ -0,0 +1,10 @@ +plugin_LTLIBRARIES = libgstgme.la + +libgstgme_la_SOURCES = gstgme.c + +libgstgme_la_CFLAGS = $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(GME_CFLAGS) +libgstgme_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) $(GME_LIBS) +libgstgme_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstgme_la_LIBTOOLFLAGS = --tag=disable-static + +noinst_HEADERS = gstgme.h diff --git a/ext/gme/gstgme.c b/ext/gme/gstgme.c new file mode 100644 index 00000000..e3f145c5 --- /dev/null +++ b/ext/gme/gstgme.c @@ -0,0 +1,545 @@ +/* Copyright (C) 2004-2005,2009 Michael Pyne <mpyne at kde org> + * Copyright (C) 2004-2006 Chris Lee <clee at kde org> + * Copyright (C) 2007 Brian Koropoff <bkoropoff at gmail 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 "gstgme.h" + +#include <string.h> +#include <glib/gprintf.h> +#include <glib.h> + +static GstStaticPadTemplate sink_factory = + GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-ay; " + "audio/x-gbs; " + "audio/x-gym; " + "audio/x-hes; " + "audio/x-kss; " + "audio/x-nsf; " "audio/x-sap; " "audio/x-spc; " "audio/x-vgm")); + +static GstStaticPadTemplate src_factory = +GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-int, " + "endianness = (int) BYTE_ORDER, " + "signed = (boolean) TRUE, " + "width = (int) 16, " + "depth = (int) 16, " "rate = (int) 32000, " "channels = (int) 2")); + +GST_BOILERPLATE (GstGmeDec, gst_gme_dec, GstElement, GST_TYPE_ELEMENT); + +static GstFlowReturn gst_gme_dec_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_gme_dec_sink_event (GstPad * pad, GstEvent * event); +static gboolean gst_gme_dec_src_event (GstPad * pad, GstEvent * event); +static gboolean gst_gme_dec_src_query (GstPad * pad, GstQuery * query); +static GstStateChangeReturn gst_gme_dec_change_state (GstElement * element, + GstStateChange transition); +static void gst_gme_play (GstPad * pad); +static void gst_gme_dec_dispose (GObject * object); +static gboolean gme_setup (GstGmeDec * gme); + +static gboolean +gme_negotiate (GstGmeDec * gme) +{ + GstCaps *allowed, *caps; + GstStructure *structure; + gint width = 16, depth = 16; + gboolean sign; + int rate = 32000; + int channels = 2; + + allowed = gst_pad_get_allowed_caps (gme->srcpad); + if (!allowed) { + GST_DEBUG_OBJECT (gme, "couldn't get allowed caps"); + return FALSE; + } + + GST_DEBUG_OBJECT (gme, "allowed caps: %" GST_PTR_FORMAT, allowed); + + structure = gst_caps_get_structure (allowed, 0); + gst_structure_get_int (structure, "width", &width); + gst_structure_get_int (structure, "depth", &depth); + + if (width && depth && width != depth) { + GST_DEBUG_OBJECT (gme, "width %d and depth %d are different", width, depth); + gst_caps_unref (allowed); + return FALSE; + } + + gst_structure_get_boolean (structure, "signed", &sign); + gst_structure_get_int (structure, "rate", &rate); + gst_structure_get_int (structure, "channels", &channels); + + caps = gst_caps_new_simple ("audio/x-raw-int", + "endianness", G_TYPE_INT, G_BYTE_ORDER, + "signed", G_TYPE_BOOLEAN, TRUE, + "width", G_TYPE_INT, width, + "depth", G_TYPE_INT, depth, + "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, channels, NULL); + gst_pad_set_caps (gme->srcpad, caps); + + gst_caps_unref (caps); + gst_caps_unref (allowed); + + return TRUE; +} + +static void +gst_gme_dec_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_set_details_simple (element_class, + "Gaming console music file decoder", "Codec/Audio/Decoder", + "Uses libgme to emulate a gaming console sound processors", + "Chris Lee <clee@kde.org>, Brian Koropoff <bkoropoff@gmail.com>, " + "Michael Pyne <mpyne@kde.org>, Sebastian Dröge <sebastian.droege@collabora.co.uk>"); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_factory)); +} + +static void +gst_gme_dec_class_init (GstGmeDecClass * klass) +{ + GstElementClass *element_class = (GstElementClass *) klass; + GObjectClass *gobject_class = (GObjectClass *) klass; + + element_class->change_state = GST_DEBUG_FUNCPTR (gst_gme_dec_change_state); + gobject_class->dispose = gst_gme_dec_dispose; +} + +static const GstQueryType * +gst_gme_dec_src_query_type (GstPad * pad) +{ + static const GstQueryType query_types[] = { + GST_QUERY_DURATION, + GST_QUERY_POSITION, + (GstQueryType) 0 + }; + + return query_types; +} + + +static void +gst_gme_dec_init (GstGmeDec * gme, GstGmeDecClass * klass) +{ + gme->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); + /* gst_pad_set_query_function (gme->sinkpad, NULL); */ + gst_pad_set_event_function (gme->sinkpad, gst_gme_dec_sink_event); + gst_pad_set_chain_function (gme->sinkpad, gst_gme_dec_chain); + gst_element_add_pad (GST_ELEMENT (gme), gme->sinkpad); + + gme->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); + gst_pad_set_event_function (gme->srcpad, gst_gme_dec_src_event); + gst_pad_set_query_function (gme->srcpad, gst_gme_dec_src_query); + gst_pad_set_query_type_function (gme->srcpad, gst_gme_dec_src_query_type); + gst_pad_use_fixed_caps (gme->srcpad); + gst_element_add_pad (GST_ELEMENT (gme), gme->srcpad); + + gme->adapter = gst_adapter_new (); + gme->player = NULL; + gme->total_duration = GST_CLOCK_TIME_NONE; + gme->initialized = FALSE; +} + +static void +gst_gme_dec_dispose (GObject * object) +{ + GstGmeDec *gme = GST_GME_DEC (object); + + if (gme->adapter) { + gst_object_unref (gme->adapter); + gme->adapter = NULL; + } +} + +static GstFlowReturn +gst_gme_dec_chain (GstPad * pad, GstBuffer * buffer) +{ + GstGmeDec *gme = GST_GME_DEC (gst_pad_get_parent (pad)); + + /* Accumulate GME data until end-of-stream, then commence playback. */ + gst_adapter_push (gme->adapter, buffer); + + gst_object_unref (gme); + + return GST_FLOW_OK; +} + +static gboolean +gst_gme_dec_sink_event (GstPad * pad, GstEvent * event) +{ + GstGmeDec *gme = GST_GME_DEC (gst_pad_get_parent (pad)); + gboolean result = TRUE; + gboolean forward = FALSE; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + /* we get EOS when we loaded the complete file, now try to initialize the + * decoding */ + if (!(result = gme_setup (gme))) { + /* can't start, post an ERROR and push EOS downstream */ + GST_ELEMENT_ERROR (gme, STREAM, DEMUX, (NULL), + ("can't start playback")); + forward = TRUE; + } + break; + default: + break; + } + if (forward) + result = gst_pad_push_event (gme->srcpad, event); + else + gst_event_unref (event); + + gst_object_unref (gme); + + return result; +} + +static gboolean +gst_gme_dec_src_event (GstPad * pad, GstEvent * event) +{ + GstGmeDec *gme = GST_GME_DEC (gst_pad_get_parent (pad)); + gboolean result = FALSE; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + { + gdouble rate; + GstFormat format; + GstSeekFlags flags; + GstSeekType start_type, stop_type; + gint64 start, stop; + gboolean flush; + + gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start, + &stop_type, &stop); + + gst_event_unref (event); + + if (format != GST_FORMAT_TIME) { + GST_DEBUG_OBJECT (gme, "seeking is only supported in TIME format"); + break; + } + + if (start_type != GST_SEEK_TYPE_SET || stop_type != GST_SEEK_TYPE_NONE) { + GST_DEBUG_OBJECT (gme, "unsupported seek type"); + break; + } + + if (stop_type == GST_SEEK_TYPE_NONE) + stop = GST_CLOCK_TIME_NONE; + + if (start_type == GST_SEEK_TYPE_SET) { + guint64 cur = gme_tell (gme->player) * GST_MSECOND; + guint64 dest = (guint64) start; + + if (gme->total_duration != GST_CLOCK_TIME_NONE) + dest = CLAMP (dest, 0, gme->total_duration); + else + dest = MAX (0, dest); + + if (dest == cur) + break; + + flush = (flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH; + + if (flush) { + gst_pad_push_event (gme->srcpad, gst_event_new_flush_start ()); + } else { + gst_pad_stop_task (gme->srcpad); + } + + GST_PAD_STREAM_LOCK (gme->srcpad); + + if (flags & GST_SEEK_FLAG_SEGMENT) { + gst_element_post_message (GST_ELEMENT (gme), + gst_message_new_segment_start (GST_OBJECT (gme), format, cur)); + } + + if (flush) { + gst_pad_push_event (gme->srcpad, gst_event_new_flush_stop ()); + } + + if (stop == GST_CLOCK_TIME_NONE + && gme->total_duration != GST_CLOCK_TIME_NONE) + stop = gme->total_duration; + + gst_pad_push_event (gme->srcpad, gst_event_new_new_segment (FALSE, rate, + GST_FORMAT_TIME, dest, stop, dest)); + + gme->seekpoint = dest / GST_MSECOND; /* nsecs to msecs */ + gme->seeking = TRUE; + + gst_pad_start_task (gme->srcpad, (GstTaskFunction) gst_gme_play, + gme->srcpad); + + GST_PAD_STREAM_UNLOCK (gme->srcpad); + result = TRUE; + } + break; + } + default: + result = gst_pad_push_event (gme->sinkpad, event); + break; + } + + gst_object_unref (gme); + + return result; +} + +static gboolean +gst_gme_dec_src_query (GstPad * pad, GstQuery * query) +{ + GstGmeDec *gme = GST_GME_DEC (gst_pad_get_parent (pad)); + gboolean result = TRUE; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_DURATION: + { + GstFormat format; + + gst_query_parse_duration (query, &format, NULL); + if (!gme->initialized || format != GST_FORMAT_TIME + || gme->total_duration == GST_CLOCK_TIME_NONE) { + result = FALSE; + break; + } + gst_query_set_duration (query, GST_FORMAT_TIME, gme->total_duration); + break; + } + case GST_QUERY_POSITION: + { + GstFormat format; + + gst_query_parse_position (query, &format, NULL); + if (!gme->initialized || format != GST_FORMAT_TIME) { + result = FALSE; + break; + } + gst_query_set_position (query, GST_FORMAT_TIME, + (gint64) gme_tell (gme->player) * GST_MSECOND); + break; + } + default: + result = gst_pad_query_default (pad, query); + break; + } + + gst_object_unref (gme); + + return result; +} + +static void +gst_gme_play (GstPad * pad) +{ + GstGmeDec *gme = GST_GME_DEC (gst_pad_get_parent (pad)); + GstFlowReturn flow_return; + GstBuffer *out; + gboolean seeking = gme->seeking; + gme_err_t gme_err = NULL; + const int NUM_SAMPLES = 1600; /* 4 bytes (stereo 16-bit) per sample */ + + if (!seeking) { + out = gst_buffer_new_and_alloc (NUM_SAMPLES * 4); + gst_buffer_set_caps (out, GST_PAD_CAPS (pad)); + GST_BUFFER_TIMESTAMP (out) = gme_tell (gme->player) * GST_MSECOND; + + gme_err = + gme_play (gme->player, NUM_SAMPLES * 2, + (short *) GST_BUFFER_DATA (out)); + if (gme_err) { + GST_ELEMENT_ERROR (gme, STREAM, DEMUX, (NULL), (gme_err)); + gst_pad_pause_task (pad); + gst_pad_push_event (pad, gst_event_new_eos ()); + gst_object_unref (gme); + return; + } + } else { + gme_seek (gme->player, gme->seekpoint); + gme->seeking = FALSE; + + out = gst_buffer_new (); + gst_buffer_set_caps (out, GST_PAD_CAPS (pad)); + } + + if ((flow_return = gst_pad_push (gme->srcpad, out)) != GST_FLOW_OK) { + GST_DEBUG_OBJECT (gme, "pausing task, reason %s", + gst_flow_get_name (flow_return)); + + gst_pad_pause_task (pad); + + if (GST_FLOW_IS_FATAL (flow_return) || flow_return == GST_FLOW_NOT_LINKED) { + gst_pad_push_event (pad, gst_event_new_eos ()); + } + } + + if (gme_track_ended (gme->player)) { + gst_pad_pause_task (pad); + gst_pad_push_event (pad, gst_event_new_eos ()); + } + + gst_object_unref (gme); + + return; +} + +static gboolean +gme_setup (GstGmeDec * gme) +{ + gme_info_t *info; + gme_err_t gme_err = NULL; + GstTagList *taglist; + guint64 total_duration; + GstBuffer *buffer; + + if (!gst_adapter_available (gme->adapter) || !gme_negotiate (gme)) { + return FALSE; + } + + buffer = + gst_adapter_take_buffer (gme->adapter, + gst_adapter_available (gme->adapter)); + + gme_err = + gme_open_data (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), + &gme->player, 32000); + gst_buffer_unref (buffer); + + if (gme_err || !gme->player) { + if (gme->player) { + gme_delete (gme->player); + gme->player = NULL; + } + + GST_ELEMENT_ERROR (gme, STREAM, DEMUX, (NULL), (gme_err)); + + return FALSE; + } + + gme_err = gme_track_info (gme->player, &info, 0); + + taglist = gst_tag_list_new (); + + if (info->song && *info->song) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE, + info->song, NULL); + if (info->author && *info->author) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ARTIST, + info->author, NULL); + /* Prefer the name of the official soundtrack over the name of the game (since this is + * how track numbers are derived) + */ + if (info->game && *info->game) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ALBUM, info->game, + NULL); + + if (info->comment && *info->comment) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_COMMENT, + info->comment, NULL); + if (info->dumper && *info->dumper) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_CONTACT, + info->dumper, NULL); + if (info->copyright && *info->copyright) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_COPYRIGHT, + info->copyright, NULL); + if (info->system && *info->system) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, + info->system, NULL); + + gme->total_duration = total_duration = + gst_util_uint64_scale_int (info->play_length, GST_MSECOND, 1); + + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, + GST_TAG_DURATION, total_duration, NULL); + + gst_element_found_tags_for_pad (GST_ELEMENT (gme), gme->srcpad, taglist); + + g_free (info); + +#ifdef HAVE_LIBGME_ACCURACY + /* TODO: Is it worth it to make this optional? */ + gme_enable_accuracy (gme->player, 1); +#endif + gme_start_track (gme->player, 0); + + gst_pad_push_event (gme->srcpad, gst_event_new_new_segment (FALSE, 1.0, + GST_FORMAT_TIME, 0, -1, 0)); + + gst_pad_start_task (gme->srcpad, (GstTaskFunction) gst_gme_play, gme->srcpad); + + gme->initialized = TRUE; + gme->seeking = FALSE; + gme->seekpoint = 0; + return gme->initialized; +} + +static GstStateChangeReturn +gst_gme_dec_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn result; + GstGmeDec *dec; + + dec = GST_GME_DEC (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + dec->total_duration = GST_CLOCK_TIME_NONE; + break; + default: + break; + } + + result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (result == GST_STATE_CHANGE_FAILURE) + return result; + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_adapter_clear (dec->adapter); + break; + default: + break; + } + + return result; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "gmedec", GST_RANK_PRIMARY, + GST_TYPE_GME_DEC); +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "gmedec", + "GME Audio Decoder", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); diff --git a/ext/gme/gstgme.h b/ext/gme/gstgme.h new file mode 100644 index 00000000..5fe969d3 --- /dev/null +++ b/ext/gme/gstgme.h @@ -0,0 +1,68 @@ +/* Copyright (C) 2004-2005, 2009 Michael Pyne <michael dot pyne at kdemail net> + * Copyright (C) 2004-2006 Chris Lee <clee at kde org> + * Copyright (C) 2007 Brian Koropoff <bkoropoff at gmail 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_GME_DEC_H__ +#define __GST_GME_DEC_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> + +#include <gme/gme.h> + +G_BEGIN_DECLS + +#define GST_TYPE_GME_DEC \ + (gst_gme_dec_get_type()) +#define GST_GME_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GME_DEC,GstGmeDec)) +#define GST_GME_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GME_DEC,GstGmeDecClass)) +#define GST_IS_GME_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GME_DEC)) +#define GST_IS_GME_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GME_DEC)) + +typedef struct _GstGmeDec GstGmeDec; +typedef struct _GstGmeDecClass GstGmeDecClass; + +struct _GstGmeDec +{ + GstElement element; + + GstPad *sinkpad; + GstPad *srcpad; + + GstAdapter *adapter; + Music_Emu *player; + gboolean initialized; + gboolean seeking; + int seekpoint; + + GstClockTime total_duration; +}; + +struct _GstGmeDecClass +{ + GstElementClass parent_class; +}; + +G_END_DECLS + +#endif /* __GST_GME_DEC_H__ */ diff --git a/ext/kate/gstkatedec.c b/ext/kate/gstkatedec.c index fe5ac51c..eb4bb2ba 100644 --- a/ext/kate/gstkatedec.c +++ b/ext/kate/gstkatedec.c @@ -74,8 +74,6 @@ * </refsect2> */ -/* FIXME: post appropriate GST_ELEMENT_ERROR when returning FLOW_ERROR */ - #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -298,7 +296,8 @@ gst_kate_dec_chain (GstPad * pad, GstBuffer * buf) gst_flow_get_name (rflow)); } } else { - GST_WARNING_OBJECT (kd, "failed to create buffer"); + GST_ELEMENT_ERROR (kd, STREAM, DECODE, (NULL), + ("Failed to create buffer")); rflow = GST_FLOW_ERROR; } } else { @@ -307,7 +306,8 @@ gst_kate_dec_chain (GstPad * pad, GstBuffer * buf) } g_free (escaped); } else { - GST_WARNING_OBJECT (kd, "failed to allocate string"); + GST_ELEMENT_ERROR (kd, STREAM, DECODE, (NULL), + ("Failed to allocate string")); rflow = GST_FLOW_ERROR; } @@ -329,7 +329,8 @@ gst_kate_dec_chain (GstPad * pad, GstBuffer * buf) gst_flow_get_name (rflow)); } } else { - GST_WARNING_OBJECT (kd, "failed to create SPU from paletted bitmap"); + GST_ELEMENT_ERROR (kd, STREAM, DECODE, (NULL), + ("failed to create SPU from paletted bitmap")); rflow = GST_FLOW_ERROR; } } diff --git a/ext/kate/gstkateenc.c b/ext/kate/gstkateenc.c index 80709eb3..42333509 100644 --- a/ext/kate/gstkateenc.c +++ b/ext/kate/gstkateenc.c @@ -68,7 +68,6 @@ * </refsect2> */ -/* FIXME: post appropriate GST_ELEMENT_ERROR when returning FLOW_ERROR */ /* FIXME: should we automatically pick up the language code from the * upstream event tags if none was set via the property? */ @@ -398,7 +397,7 @@ gst_kate_enc_create_buffer (GstKateEnc * ke, kate_packet * kp, { GstBuffer *buffer; - buffer = gst_buffer_new_and_alloc (kp->nbytes); + buffer = gst_buffer_try_new_and_alloc (kp->nbytes); if (G_UNLIKELY (!buffer)) { GST_WARNING_OBJECT (ke, "Failed to allocate buffer for %u bytes", kp->nbytes); @@ -426,7 +425,7 @@ gst_kate_enc_create_buffer (GstKateEnc * ke, kate_packet * kp, static GstFlowReturn gst_kate_enc_push_buffer (GstKateEnc * ke, GstBuffer * buffer) { - GstFlowReturn rflow; + GstFlowReturn flow; ke->last_timestamp = GST_BUFFER_TIMESTAMP (buffer); if (GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer) > @@ -438,12 +437,12 @@ gst_kate_enc_push_buffer (GstKateEnc * ke, GstBuffer * buffer) /* Hack to flush each packet on its own page - taken off the CMML encoder element */ GST_BUFFER_DURATION (buffer) = G_MAXINT64; - rflow = gst_pad_push (ke->srcpad, buffer); - if (G_UNLIKELY (rflow != GST_FLOW_OK)) { - GST_ERROR_OBJECT (ke, "Failed to push buffer: %d", rflow); + flow = gst_pad_push (ke->srcpad, buffer); + if (G_UNLIKELY (flow != GST_FLOW_OK)) { + GST_WARNING_OBJECT (ke->srcpad, "push flow: %s", gst_flow_get_name (flow)); } - return rflow; + return flow; } static GstFlowReturn @@ -457,7 +456,8 @@ gst_kate_enc_push_and_free_kate_packet (GstKateEnc * ke, kate_packet * kp, buffer = gst_kate_enc_create_buffer (ke, kp, granpos, timestamp, duration, header); if (G_UNLIKELY (!buffer)) { - GST_WARNING_OBJECT (ke, "Failed to create buffer, %u bytes", kp->nbytes); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("Failed to create buffer, %u bytes", kp->nbytes)); kate_packet_clear (kp); return GST_FLOW_ERROR; } @@ -548,9 +548,12 @@ gst_kate_enc_send_headers (GstKateEnc * ke) kate_packet kp; int ret = kate_encode_headers (&ke->k, &ke->kc, &kp); if (ret == 0) { - GstBuffer *buffer = - gst_kate_enc_create_buffer (ke, &kp, 0ll, 0ll, 0ll, TRUE); + GstBuffer *buffer; + + buffer = gst_kate_enc_create_buffer (ke, &kp, 0, 0, 0, TRUE); if (!buffer) { + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("Failed to create buffer, %u bytes", kp.nbytes)); rflow = GST_FLOW_ERROR; break; } @@ -561,7 +564,8 @@ gst_kate_enc_send_headers (GstKateEnc * ke) GST_LOG_OBJECT (ke, "Last header encoded"); break; } else { - GST_LOG_OBJECT (ke, "Error encoding header: %d", ret); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("kate_encode_headers: %d", ret)); rflow = GST_FLOW_ERROR; break; } @@ -634,7 +638,8 @@ gst_kate_enc_chain_push_packet (GstKateEnc * ke, kate_packet * kp, granpos = kate_encode_get_granule (&ke->k); if (G_UNLIKELY (granpos < 0)) { - GST_WARNING_OBJECT (ke, "Negative granpos for packet"); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("Negative granpos for packet")); kate_packet_clear (kp); return GST_FLOW_ERROR; } @@ -685,6 +690,8 @@ gst_kate_enc_flush_waiting (GstKateEnc * ke, GstClockTime now) ret = kate_encode_text (&ke->k, t0, t1, "", 0, &kp); if (G_UNLIKELY (ret < 0)) { + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("kate_encode_text: %d", ret)); rflow = GST_FLOW_ERROR; } else { rflow = @@ -747,7 +754,7 @@ gst_kate_enc_chain_spu (GstKateEnc * ke, GstBuffer * buf) g_free (kbitmap); if (kpalette) g_free (kpalette); - GST_ERROR_OBJECT (ke, "Out of memory"); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), ("Out of memory")); return GST_FLOW_ERROR; } @@ -802,17 +809,20 @@ gst_kate_enc_chain_spu (GstKateEnc * ke, GstBuffer * buf) kbitmap->width, kbitmap->height, GST_BUFFER_SIZE (buf), t0, t1); ret = kate_encode_set_region (&ke->k, kregion); if (G_UNLIKELY (ret < 0)) { - GST_WARNING_OBJECT (ke, "Failed to set event region (%d)", ret); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("kate_encode_set_region: %d", ret)); rflow = GST_FLOW_ERROR; } else { ret = kate_encode_set_palette (&ke->k, kpalette); if (G_UNLIKELY (ret < 0)) { - GST_WARNING_OBJECT (ke, "Failed to set event palette (%d)", ret); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("kate_encode_set_palette: %d", ret)); rflow = GST_FLOW_ERROR; } else { ret = kate_encode_set_bitmap (&ke->k, kbitmap); if (G_UNLIKELY (ret < 0)) { - GST_WARNING_OBJECT (ke, "Failed to set event bitmap (%d)", ret); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("kate_encode_set_bitmap: %d", ret)); rflow = GST_FLOW_ERROR; } else { /* Some SPUs have no hide time - so I'm going to delay the encoding of the packet @@ -833,8 +843,8 @@ gst_kate_enc_chain_spu (GstKateEnc * ke, GstBuffer * buf) } else { ret = kate_encode_text (&ke->k, t0, t1, "", 0, &kp); if (G_UNLIKELY (ret < 0)) { - GST_WARNING_OBJECT (ke, - "Failed to encode empty text for SPU buffer (%d)", ret); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("Failed to encode empty text for SPU buffer: %d", ret)); rflow = GST_FLOW_ERROR; } else { rflow = @@ -875,7 +885,8 @@ gst_kate_enc_chain_text (GstKateEnc * ke, GstBuffer * buf, } if (G_UNLIKELY (ret < 0)) { - GST_WARNING_OBJECT (ke, "Failed to set markup type (%d)", ret); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("kate_encode_set_markup_type: %d", ret)); rflow = GST_FLOW_ERROR; } else { const char *text = (const char *) GST_BUFFER_DATA (buf); @@ -888,13 +899,17 @@ gst_kate_enc_chain_text (GstKateEnc * ke, GstBuffer * buf, GST_BUFFER_SIZE (buf), t0, t1); ret = kate_encode_text (&ke->k, t0, t1, text, text_len, &kp); if (G_UNLIKELY (ret < 0)) { + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("Failed to encode text: %d", ret)); rflow = GST_FLOW_ERROR; } else { rflow = gst_kate_enc_chain_push_packet (ke, &kp, start, stop - start + 1); } } else { - GST_WARNING_OBJECT (ke, "No text in text packet"); + /* FIXME: this should not be an error, we should ignore it and move on */ + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("no text in text packet")); rflow = GST_FLOW_ERROR; } } @@ -918,8 +933,8 @@ gst_kate_enc_chain (GstPad * pad, GstBuffer * buf) /* get the type of the data we're being sent */ caps = GST_PAD_CAPS (pad); if (G_UNLIKELY (caps == NULL)) { - GST_ERROR_OBJECT (ke, ": Could not get caps of pad"); - rflow = GST_FLOW_ERROR; + GST_WARNING_OBJECT (ke, "No input caps set"); + rflow = GST_FLOW_NOT_NEGOTIATED; } else { const GstStructure *structure = gst_caps_get_structure (caps, 0); if (structure) diff --git a/ext/kate/gstkateparse.c b/ext/kate/gstkateparse.c index 89d17514..e4a83477 100644 --- a/ext/kate/gstkateparse.c +++ b/ext/kate/gstkateparse.c @@ -58,8 +58,6 @@ * */ -/* FIXME: post appropriate GST_ELEMENT_ERROR when returning FLOW_ERROR */ - #ifdef HAVE_CONFIG_H # include "config.h" #endif @@ -174,7 +172,8 @@ gst_kate_parse_push_headers (GstKateParse * parse) gst_pad_get_negotiated_caps (parse->sinkpad), parse->streamheader); if (G_UNLIKELY (!caps)) { - GST_ERROR_OBJECT (parse, "Failed to set headers on caps"); + GST_ELEMENT_ERROR (parse, STREAM, DECODE, (NULL), + ("Failed to set headers on caps")); return GST_FLOW_ERROR; } @@ -182,8 +181,8 @@ gst_kate_parse_push_headers (GstKateParse * parse) res = gst_pad_set_caps (parse->srcpad, caps); gst_caps_unref (caps); if (G_UNLIKELY (!res)) { - GST_WARNING_OBJECT (parse, "Failed to set pad caps"); - return GST_FLOW_ERROR; + GST_WARNING_OBJECT (parse->srcpad, "Failed to set caps on source pad"); + return GST_FLOW_NOT_NEGOTIATED; } headers = parse->streamheader; @@ -243,7 +242,7 @@ static GstFlowReturn gst_kate_parse_push_buffer (GstKateParse * parse, GstBuffer * buf, gint64 granulepos) { - GST_LOG_OBJECT (parse, "granulepos %16llx", granulepos); + GST_LOG_OBJECT (parse, "granulepos %16" G_GINT64_MODIFIER "x", granulepos); if (granulepos < 0) { /* packets coming not from Ogg won't have a granpos in the offset end, so we have to synthesize one here - only problem is we don't know @@ -331,7 +330,7 @@ gst_kate_parse_queue_buffer (GstKateParse * parse, GstBuffer * buf) /* oggdemux stores the granule pos in the offset end */ granpos = GST_BUFFER_OFFSET_END (buf); - GST_LOG_OBJECT (parse, "granpos %16llx", granpos); + GST_LOG_OBJECT (parse, "granpos %16" G_GINT64_MODIFIER "x", granpos); g_queue_push_tail (parse->buffer_queue, buf); #if 1 @@ -343,7 +342,8 @@ gst_kate_parse_queue_buffer (GstKateParse * parse, GstBuffer * buf) if (granpos >= 0) { ret = gst_kate_parse_drain_queue (parse, granpos); } else { - GST_WARNING_OBJECT (parse, "granulepos < 0 (%lld)", granpos); + GST_ELEMENT_ERROR (parse, STREAM, DECODE, (NULL), + ("Bad granulepos %" G_GINT64_FORMAT, granpos)); ret = GST_FLOW_ERROR; } #endif diff --git a/ext/kate/gstkatespu.c b/ext/kate/gstkatespu.c index f05ae421..cc54cc4f 100644 --- a/ext/kate/gstkatespu.c +++ b/ext/kate/gstkatespu.c @@ -253,9 +253,21 @@ gst_kate_spu_crop_bitmap (GstKateEnc * ke, kate_bitmap * kb, guint16 * dx, kb->height = h; } -#define CHECK(x) do { guint16 _ = (x); if (G_UNLIKELY((_) > sz)) { GST_WARNING_OBJECT (ke, "SPU overflow"); return GST_FLOW_ERROR; } } while (0) -#define ADVANCE(x) do { guint16 _ = (x); ptr += (_); sz -= (_); } while (0) -#define IGNORE(x) do { guint16 __ = (x); CHECK (__); ADVANCE (__); } while (0) +#define CHECK(x) G_STMT_START { \ + guint16 _ = (x); \ + if (G_UNLIKELY((_) > sz)) { \ + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), ("Read outside buffer")); \ + return GST_FLOW_ERROR; \ + } \ + } G_STMT_END +#define ADVANCE(x) G_STMT_START { \ + guint16 _ = (x); ptr += (_); sz -= (_); \ + } G_STMT_END +#define IGNORE(x) G_STMT_START { \ + guint16 __ = (x); \ + CHECK (__); \ + ADVANCE (__); \ + } G_STMT_END static GstFlowReturn gst_kate_spu_decode_command_sequence (GstKateEnc * ke, GstBuffer * buf, @@ -267,8 +279,9 @@ gst_kate_spu_decode_command_sequence (GstKateEnc * ke, GstBuffer * buf, guint16 sz; if (command_sequence_offset >= GST_BUFFER_SIZE (buf)) { - GST_WARNING_OBJECT (ke, "Command sequence offset %u is out of range %u", - command_sequence_offset, GST_BUFFER_SIZE (buf)); + GST_ELEMENT_ERROR (ke, STREAM, DECODE, (NULL), + ("Command sequence offset %u is out of range %u", + command_sequence_offset, GST_BUFFER_SIZE (buf))); return GST_FLOW_ERROR; } @@ -348,10 +361,12 @@ gst_kate_spu_decode_command_sequence (GstKateEnc * ke, GstBuffer * buf, } break; default: - GST_WARNING_OBJECT (ke, "invalid SPU command: %u", cmd); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("Invalid SPU command: %u", cmd)); return GST_FLOW_ERROR; } } + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), ("Error parsing SPU")); return GST_FLOW_ERROR; } @@ -395,9 +410,10 @@ gst_kate_spu_create_spu_palette (GstKateEnc * ke, kate_palette * kp) kate_palette_init (kp); kp->ncolors = 4; kp->colors = (kate_color *) g_malloc (kp->ncolors * sizeof (kate_color)); - if (G_UNLIKELY (!kp->colors)) + if (G_UNLIKELY (!kp->colors)) { + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), ("Out of memory")); return GST_FLOW_ERROR; - + } #if 1 for (n = 0; n < kp->ncolors; ++n) { int idx = ke->spu_colormap[n]; @@ -478,7 +494,8 @@ gst_kate_spu_decode_spu (GstKateEnc * ke, GstBuffer * buf, kate_region * kr, if (G_UNLIKELY (ke->spu_right - ke->spu_left < 0 || ke->spu_bottom - ke->spu_top < 0 || ke->spu_pix_data[0] == 0 || ke->spu_pix_data[1] == 0)) { - GST_WARNING_OBJECT (ke, "SPU area is empty, nothing to encode"); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("SPU area is empty, nothing to encode")); return GST_FLOW_ERROR; } @@ -495,7 +512,8 @@ gst_kate_spu_decode_spu (GstKateEnc * ke, GstBuffer * buf, kate_region * kr, kb->type = kate_bitmap_type_paletted; kb->pixels = (unsigned char *) g_malloc (kb->width * kb->height); if (G_UNLIKELY (!kb->pixels)) { - GST_WARNING_OBJECT (ke, "Failed to allocate memory for pixel data"); + GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), + ("Failed to allocate memory for pixel data")); return GST_FLOW_ERROR; } diff --git a/ext/kate/gstkatetag.c b/ext/kate/gstkatetag.c index f5734c12..ac4e925f 100644 --- a/ext/kate/gstkatetag.c +++ b/ext/kate/gstkatetag.c @@ -63,8 +63,6 @@ * </refsect2> */ -/* FIXME: post appropriate GST_ELEMENT_ERROR when returning FLOW_ERROR */ - #ifdef HAVE_CONFIG_H # include "config.h" #endif diff --git a/ext/kate/gstkatetiger.c b/ext/kate/gstkatetiger.c index 3e72285a..433a3106 100644 --- a/ext/kate/gstkatetiger.c +++ b/ext/kate/gstkatetiger.c @@ -73,8 +73,6 @@ * </refsect2> */ -/* FIXME: post appropriate GST_ELEMENT_ERROR when returning FLOW_ERROR */ - #ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/ext/kate/gstkateutil.c b/ext/kate/gstkateutil.c index ca245f99..f99247be 100644 --- a/ext/kate/gstkateutil.c +++ b/ext/kate/gstkateutil.c @@ -17,7 +17,6 @@ * Boston, MA 02111-1307, USA. */ -/* FIXME: post appropriate GST_ELEMENT_ERROR when returning FLOW_ERROR */ /* FIXME: shouldn't all this GstKateDecoderBase stuff really be a base class? */ #ifdef HAVE_CONFIG_H @@ -165,7 +164,8 @@ gst_kate_util_decoder_base_chain_kate_packet (GstKateDecoderBase * decoder, kate_packet_wrap (&kp, GST_BUFFER_SIZE (buf), GST_BUFFER_DATA (buf)); ret = kate_high_decode_packetin (&decoder->k, &kp, ev); if (G_UNLIKELY (ret < 0)) { - GST_WARNING_OBJECT (element, "kate_high_decode_packetin failed (%d)", ret); + GST_ELEMENT_ERROR (element, STREAM, DECODE, (NULL), + ("Failed to decode Kate packet: %d", ret)); return GST_FLOW_ERROR; } else if (G_UNLIKELY (ret > 0)) { GST_DEBUG_OBJECT (element, diff --git a/ext/mimic/gstmimdec.c b/ext/mimic/gstmimdec.c index 6a0abce5..a0878a13 100644 --- a/ext/mimic/gstmimdec.c +++ b/ext/mimic/gstmimdec.c @@ -68,6 +68,8 @@ static GstFlowReturn gst_mimdec_chain (GstPad * pad, GstBuffer * in); static GstStateChangeReturn gst_mimdec_change_state (GstElement * element, GstStateChange transition); +static gboolean gst_mimdec_sink_event (GstPad * pad, GstEvent * event); + GST_BOILERPLATE (GstMimDec, gst_mimdec, GstElement, GST_TYPE_ELEMENT); @@ -111,15 +113,12 @@ gst_mimdec_class_init (GstMimDecClass * klass) static void gst_mimdec_init (GstMimDec * mimdec, GstMimDecClass * klass) { - mimdec->sinkpad = - gst_pad_new_from_template (gst_static_pad_template_get (&sink_factory), - "sink"); + mimdec->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); gst_element_add_pad (GST_ELEMENT (mimdec), mimdec->sinkpad); gst_pad_set_chain_function (mimdec->sinkpad, gst_mimdec_chain); + gst_pad_set_event_function (mimdec->sinkpad, gst_mimdec_sink_event); - mimdec->srcpad = - gst_pad_new_from_template (gst_static_pad_template_get (&src_factory), - "src"); + mimdec->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); gst_element_add_pad (GST_ELEMENT (mimdec), mimdec->srcpad); mimdec->adapter = gst_adapter_new (); @@ -138,6 +137,8 @@ gst_mimdec_finalize (GObject * object) gst_adapter_clear (mimdec->adapter); g_object_unref (mimdec->adapter); + + GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); } static GstFlowReturn @@ -213,6 +214,9 @@ gst_mimdec_chain (GstPad * pad, GstBuffer * in) (guchar *) gst_adapter_peek (mimdec->adapter, mimdec->payload_size); if (mimdec->dec == NULL) { + GstEvent *event = NULL; + gboolean result = TRUE; + /* Check if its a keyframe, otherwise skip it */ if (GUINT32_FROM_LE (*((guint32 *) (frame_body + 12))) != 0) { gst_adapter_flush (mimdec->adapter, mimdec->payload_size); @@ -254,6 +258,20 @@ gst_mimdec_chain (GstPad * pad, GstBuffer * in) res = GST_FLOW_ERROR; goto out; } + + if (mimdec->need_newsegment) + event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, + mimdec->current_ts * GST_MSECOND, -1, 0); + mimdec->need_newsegment = FALSE; + GST_OBJECT_UNLOCK (mimdec); + if (event) + result = gst_pad_push_event (mimdec->srcpad, event); + GST_OBJECT_LOCK (mimdec); + if (!result) { + GST_WARNING_OBJECT (mimdec, "gst_pad_push_event failed"); + res = GST_FLOW_ERROR; + goto out; + } } out_buf = gst_buffer_new_and_alloc (mimdec->buffer_size); @@ -270,7 +288,10 @@ gst_mimdec_chain (GstPad * pad, GstBuffer * in) goto out; } - GST_BUFFER_TIMESTAMP (out_buf) = in_time; + if (GST_CLOCK_TIME_IS_VALID (in_time)) + GST_BUFFER_TIMESTAMP (out_buf) = in_time; + else + GST_BUFFER_TIMESTAMP (out_buf) = mimdec->current_ts * GST_MSECOND; mimic_get_property (mimdec->dec, "width", &width); mimic_get_property (mimdec->dec, "height", &height); @@ -312,6 +333,7 @@ gst_mimdec_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_READY_TO_NULL: + GST_OBJECT_LOCK (element); if (mimdec->dec != NULL) { mimic_close (mimdec->dec); mimdec->dec = NULL; @@ -319,8 +341,13 @@ gst_mimdec_change_state (GstElement * element, GstStateChange transition) mimdec->have_header = FALSE; mimdec->payload_size = -1; mimdec->current_ts = -1; - GST_OBJECT_UNLOCK (element); } + GST_OBJECT_UNLOCK (element); + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + GST_OBJECT_LOCK (element); + mimdec->need_newsegment = TRUE; + GST_OBJECT_UNLOCK (element); break; default: break; @@ -328,3 +355,72 @@ gst_mimdec_change_state (GstElement * element, GstStateChange transition) return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); } + +static gboolean +gst_mimdec_sink_event (GstPad * pad, GstEvent * event) +{ + gboolean res = TRUE; + GstMimDec *mimdec = GST_MIMDEC (gst_pad_get_parent (pad)); + + /* + * Ignore upstream newsegment event, its EVIL, we should implement + * proper seeking instead + */ + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_NEWSEGMENT: + { + gboolean update; + GstFormat format; + gdouble rate, arate; + gint64 start, stop, time; + + gst_event_parse_new_segment_full (event, &update, &rate, &arate, + &format, &start, &stop, &time); + + /* we need TIME and a positive rate */ + if (format != GST_FORMAT_TIME) + goto newseg_wrong_format; + + if (rate <= 0.0) + goto newseg_wrong_rate; + + GST_OBJECT_LOCK (mimdec); + mimdec->need_newsegment = FALSE; + GST_OBJECT_UNLOCK (mimdec); + + res = gst_pad_push_event (mimdec->srcpad, event); + } + break; + case GST_EVENT_FLUSH_STOP: + GST_OBJECT_LOCK (mimdec); + mimdec->need_newsegment = TRUE; + GST_OBJECT_UNLOCK (mimdec); + + res = gst_pad_push_event (mimdec->srcpad, event); + break; + default: + res = gst_pad_push_event (mimdec->srcpad, event); + break; + } + +done: + + gst_object_unref (mimdec); + + return res; + +newseg_wrong_format: + { + GST_DEBUG_OBJECT (mimdec, "received non TIME newsegment"); + gst_event_unref (event); + goto done; + } +newseg_wrong_rate: + { + GST_DEBUG_OBJECT (mimdec, "negative rates not supported yet"); + gst_event_unref (event); + goto done; + } + + +} diff --git a/ext/mimic/gstmimdec.h b/ext/mimic/gstmimdec.h index 7db6b1b8..e1db38d1 100644 --- a/ext/mimic/gstmimdec.h +++ b/ext/mimic/gstmimdec.h @@ -57,6 +57,8 @@ struct _GstMimDec gboolean have_header; guint32 payload_size; guint32 current_ts; + + gboolean need_newsegment; }; struct _GstMimDecClass diff --git a/ext/mimic/gstmimenc.c b/ext/mimic/gstmimenc.c index 2418e7c8..4a65b9fd 100644 --- a/ext/mimic/gstmimenc.c +++ b/ext/mimic/gstmimenc.c @@ -142,17 +142,13 @@ gst_mimenc_class_init (GstMimEncClass * klass) static void gst_mimenc_init (GstMimEnc * mimenc, GstMimEncClass * klass) { - mimenc->sinkpad = - gst_pad_new_from_template (gst_static_pad_template_get (&sink_factory), - "sink"); + mimenc->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); gst_element_add_pad (GST_ELEMENT (mimenc), mimenc->sinkpad); gst_pad_set_setcaps_function (mimenc->sinkpad, gst_mimenc_setcaps); gst_pad_set_chain_function (mimenc->sinkpad, gst_mimenc_chain); gst_pad_set_event_function (mimenc->sinkpad, gst_mimenc_event); - mimenc->srcpad = - gst_pad_new_from_template (gst_static_pad_template_get (&src_factory), - "src"); + mimenc->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); gst_element_add_pad (GST_ELEMENT (mimenc), mimenc->srcpad); mimenc->enc = NULL; @@ -538,6 +534,8 @@ paused_mode_task (gpointer data) GST_OBJECT_LOCK (mimenc); mimenc->clock_id = NULL; GST_OBJECT_UNLOCK (mimenc); + + gst_clock_id_unref (id); } return; diff --git a/ext/musepack/gstmusepackdec.c b/ext/musepack/gstmusepackdec.c index fea4bc05..30a34bff 100644 --- a/ext/musepack/gstmusepackdec.c +++ b/ext/musepack/gstmusepackdec.c @@ -300,6 +300,7 @@ gst_musepackdec_get_src_query_types (GstPad * pad) static const GstQueryType query_types[] = { GST_QUERY_POSITION, GST_QUERY_DURATION, + GST_QUERY_SEEKING, (GstQueryType) 0 }; @@ -358,6 +359,18 @@ gst_musepackdec_src_query (GstPad * pad, GstQuery * query) } break; } + case GST_QUERY_SEEKING:{ + GstFormat fmt; + + res = TRUE; + gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL); + if (fmt == GST_FORMAT_TIME || fmt == GST_FORMAT_DEFAULT) + gst_query_set_seeking (query, fmt, TRUE, 0, -1); + else + gst_query_set_seeking (query, fmt, FALSE, -1, -1); + + break; + } default: res = gst_pad_query_default (pad, query); break; diff --git a/ext/spc/Makefile.am b/ext/spc/Makefile.am index 89f0f721..067ea669 100644 --- a/ext/spc/Makefile.am +++ b/ext/spc/Makefile.am @@ -8,3 +8,4 @@ libgstspc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstspc_la_LIBTOOLFLAGS = --tag=disable-static noinst_HEADERS = gstspc.h tag.h + diff --git a/ext/spc/gstspc.c b/ext/spc/gstspc.c index c8ece379..7032065a 100644 --- a/ext/spc/gstspc.c +++ b/ext/spc/gstspc.c @@ -584,7 +584,7 @@ gst_spc_dec_change_state (GstElement * element, GstStateChange transition) static gboolean plugin_init (GstPlugin * plugin) { - return gst_element_register (plugin, "spcdec", GST_RANK_PRIMARY, + return gst_element_register (plugin, "spcdec", GST_RANK_SECONDARY, GST_TYPE_SPC_DEC); } diff --git a/gst/aacparse/gstbaseparse.c b/gst/aacparse/gstbaseparse.c index b59319a5..cdd8b182 100644 --- a/gst/aacparse/gstbaseparse.c +++ b/gst/aacparse/gstbaseparse.c @@ -872,6 +872,8 @@ gst_base_parse_drain (GstBaseParse * parse) { guint avail; + GST_DEBUG_OBJECT (parse, "draining"); + for (;;) { avail = gst_adapter_available (parse->adapter); if (!avail) @@ -881,6 +883,12 @@ gst_base_parse_drain (GstBaseParse * parse) if (gst_base_parse_chain (parse->sinkpad, NULL) != GST_FLOW_OK) { break; } + + /* nothing changed, maybe due to truncated frame; break infinite loop */ + if (avail == gst_adapter_available (parse->adapter)) { + GST_DEBUG_OBJECT (parse, "no change during draining; flushing"); + gst_adapter_clear (parse->adapter); + } } } @@ -950,6 +958,7 @@ gst_base_parse_chain (GstPad * pad, GstBuffer * buffer) GST_DEBUG_OBJECT (parse, "found valid frame but not enough data available (only %d bytes)", gst_adapter_available (parse->adapter)); + gst_buffer_unref (tmpbuf); goto done; } break; diff --git a/gst/amrparse/gstbaseparse.c b/gst/amrparse/gstbaseparse.c index 870492a8..68a8aad6 100644 --- a/gst/amrparse/gstbaseparse.c +++ b/gst/amrparse/gstbaseparse.c @@ -872,6 +872,8 @@ gst_base_parse_drain (GstBaseParse * parse) { guint avail; + GST_DEBUG_OBJECT (parse, "draining"); + for (;;) { avail = gst_adapter_available (parse->adapter); if (!avail) @@ -881,6 +883,12 @@ gst_base_parse_drain (GstBaseParse * parse) if (gst_base_parse_chain (parse->sinkpad, NULL) != GST_FLOW_OK) { break; } + + /* nothing changed, maybe due to truncated frame; break infinite loop */ + if (avail == gst_adapter_available (parse->adapter)) { + GST_DEBUG_OBJECT (parse, "no change during draining; flushing"); + gst_adapter_clear (parse->adapter); + } } } @@ -950,6 +958,7 @@ gst_base_parse_chain (GstPad * pad, GstBuffer * buffer) GST_DEBUG_OBJECT (parse, "found valid frame but not enough data available (only %d bytes)", gst_adapter_available (parse->adapter)); + gst_buffer_unref (tmpbuf); goto done; } break; diff --git a/gst/asfmux/Makefile.am b/gst/asfmux/Makefile.am new file mode 100644 index 00000000..0246102b --- /dev/null +++ b/gst/asfmux/Makefile.am @@ -0,0 +1,24 @@ +# plugindir is set in configure + +plugin_LTLIBRARIES = libgstasfmux.la + +# sources used to compile this plug-in +libgstasfmux_la_SOURCES = gstasfmux.c \ + gstasfobjects.c \ + gstasfparse.c \ + gstrtpasfpay.c \ + gstasf.c + +# flags used to compile this plugin +# add other _CFLAGS and _LIBS as needed +libgstasfmux_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) +libgstasfmux_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) \ + -lgstrtp-@GST_MAJORMINOR@ +libgstasfmux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +#libgstasfmux_la_LIBTOOLFLAGS = --tag=disable-static + +# headers we need but don't want installed +noinst_HEADERS = gstasfmux.h \ + gstasfobjects.h \ + gstasfparse.h \ + gstrtpasfpay.h diff --git a/gst/asfmux/gstasf.c b/gst/asfmux/gstasf.c new file mode 100644 index 00000000..fb935172 --- /dev/null +++ b/gst/asfmux/gstasf.c @@ -0,0 +1,51 @@ +/* GStreamer + * Copyright (C) 2009 Thiago Santos <thiagoss@embeddeed.ufcg.edu.br> + * + * gstasf.c: plugin registering + * + * 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 <gst/gst.h> + +#include "gstasfmux.h" +#include "gstrtpasfpay.h" +#include "gstasfparse.h" + +static gboolean +plugin_init (GstPlugin * plugin) +{ + if (!gst_asf_mux_plugin_init (plugin)) { + return FALSE; + } + if (!gst_rtp_asf_pay_plugin_init (plugin)) { + return FALSE; + } + if (!gst_asf_parse_plugin_init (plugin)) { + return FALSE; + } + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "asfmux", + "ASF Muxer Plugin", + plugin_init, VERSION, "LGPL", "gsoc2009 package", "embedded.ufcg.edu.br") diff --git a/gst/asfmux/gstasfmux.c b/gst/asfmux/gstasfmux.c new file mode 100644 index 00000000..83f52701 --- /dev/null +++ b/gst/asfmux/gstasfmux.c @@ -0,0 +1,2280 @@ +/* ASF muxer plugin for GStreamer + * Copyright (C) 2009 Thiago Santos <thiagoss@embedded.ufcg.edu.br> + * + * 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: + * - avimux (by Ronald Bultje and Mark Nauwelaerts) + * - qtmux (by Thiago Santos and Mark Nauwelaerts) + */ + +/** + * SECTION:element-asfmux + * + * Muxes media into an ASF file/stream. + * + * Pad names are either video_xx or audio_xx, where 'xx' is the + * stream number of the stream that goes through that pad. Stream numbers + * are assigned sequentially, starting from 1. + * + * <refsect2> + * <title>Example launch lines</title> + * <para>(write everything in one line, without the backslash characters)</para> + * |[ + * gst-launch videotestsrc num-buffers=250 \ + * ! "video/x-raw-yuv,format=(fourcc)I420,framerate=(fraction)25/1" ! ffenc_wmv2 \ + * ! asfmux name=mux ! filesink location=test.asf \ + * audiotestsrc num-buffers=440 ! audioconvert \ + * ! "audio/x-raw-int,rate=44100" ! ffenc_wmav2 ! mux. + * ]| This creates an ASF file containing an WMV video stream + * with a test picture and WMA audio stream of a test sound. + * + * <title>Live streaming</title> + * asfmux and rtpasfpay are capable of generating a live asf stream. + * asfmux has to set its 'is-live' property to true, because in this + * mode it won't try to seek back to the start of the file to replace + * some fields that couldn't be known at the file start. In this mode, + * it won't also send indexes at the end of the data packets (the actual + * media content) + * the following pipelines are an example of this usage. + * <para>(write everything in one line, without the backslash characters)</para> + * Server (sender) + * |[ + * gst-launch -ve videotestsrc ! ffenc_wmv2 ! asfmux name=mux is-live=true \ + * ! rtpasfpay ! udpsink host=127.0.0.1 port=3333 \ + * audiotestsrc ! ffenc_wmav2 ! mux. + * ]| + * Client (receiver) + * |[ + * gst-launch udpsrc port=3333 ! "caps_from_rtpasfpay_at_sender" \ + * ! rtpasfdepay ! decodebin2 name=d ! queue \ + * ! ffmpegcolorspace ! autovideosink \ + * d. ! queue ! audioconvert ! autoaudiosink + * ]| + * </refsect2> + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include "gstasfmux.h" + +#define DEFAULT_SIMPLE_INDEX_TIME_INTERVAL G_GUINT64_CONSTANT (10000000) +#define MAX_PAYLOADS_IN_A_PACKET 63 + +GST_DEBUG_CATEGORY_STATIC (asfmux_debug); +#define GST_CAT_DEFAULT asfmux_debug + +enum +{ + PROP_0, + PROP_PACKET_SIZE, + PROP_PREROLL, + PROP_MERGE_STREAM_TAGS, + PROP_PADDING, + PROP_IS_LIVE +}; + +/* Stores a tag list for the available/known tags + * in an ASF file + * Also stores the sizes those entries would use in a + * content description object and extended content + * description object + */ +typedef struct +{ + GstTagList *tags; + guint64 cont_desc_size; + guint64 ext_cont_desc_size; +} GstAsfTags; + +/* Helper struct to be used as user data + * in gst_tag_foreach function for writing + * each tag for the metadata objects + * + * stream_num is used only for stream dependent tags + */ +typedef struct +{ + GstAsfMux *asfmux; + guint8 *buf; + guint16 count; + guint64 size; + guint16 stream_num; +} GstAsfExtContDescData; + +typedef GstAsfExtContDescData GstAsfMetadataObjData; + +#define DEFAULT_PACKET_SIZE 4800 +#define DEFAULT_PREROLL 5000 +#define DEFAULT_MERGE_STREAM_TAGS TRUE +#define DEFAULT_PADDING 0 +#define DEFAULT_IS_LIVE FALSE + +static const GstElementDetails gst_asf_mux_details = +GST_ELEMENT_DETAILS ("ASF muxer", + "Codec/Muxer", + "Muxes audio and video into an ASF stream", + "Thiago Santos <thiagoss@embedded.ufcg.edu.br>"); + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-ms-asf, " "parsed = (boolean) true") + ); + +static GstStaticPadTemplate video_sink_factory = +GST_STATIC_PAD_TEMPLATE ("video_%d", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS ("video/x-wmv, wmvversion = (int) [1,3]")); + +static GstStaticPadTemplate audio_sink_factory = + GST_STATIC_PAD_TEMPLATE ("audio_%d", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS ("audio/x-wma, wmaversion = (int) [1,3]; " + "audio/mpeg, layer = (int) 3, mpegversion = (int) 1, " + "channels = (int) [1,2], rate = (int) [8000,96000]")); + +static void gst_asf_mux_base_init (gpointer g_class); +static void gst_asf_mux_class_init (GstAsfMuxClass * klass); +static void gst_asf_mux_init (GstAsfMux * asfmux); + +static GstPad *gst_asf_mux_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * name); +static void gst_asf_mux_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_asf_mux_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); +static GstStateChangeReturn gst_asf_mux_change_state (GstElement * element, + GstStateChange transition); + +static gboolean gst_asf_mux_sink_event (GstPad * pad, GstEvent * event); + +static void gst_asf_mux_pad_reset (GstAsfPad * data); +static GstFlowReturn gst_asf_mux_collected (GstCollectPads * collect, + gpointer data); + +static GstElementClass *parent_class = NULL; + +GType +gst_asf_mux_get_type (void) +{ + static GType asfmux_type = 0; + + if (!asfmux_type) { + static const GTypeInfo asfmux_info = { + sizeof (GstAsfMuxClass), + gst_asf_mux_base_init, + NULL, + (GClassInitFunc) gst_asf_mux_class_init, + NULL, + NULL, + sizeof (GstAsfMux), + 0, + (GInstanceInitFunc) gst_asf_mux_init, + }; + static const GInterfaceInfo tag_setter_info = { + NULL, + NULL, + NULL + }; + + asfmux_type = + g_type_register_static (GST_TYPE_ELEMENT, "GstAsfMux", &asfmux_info, 0); + g_type_add_interface_static (asfmux_type, GST_TYPE_TAG_SETTER, + &tag_setter_info); + } + return asfmux_type; +} + +static void +gst_asf_mux_reset (GstAsfMux * asfmux) +{ + asfmux->state = GST_ASF_MUX_STATE_NONE; + asfmux->stream_number = 0; + asfmux->data_object_size = 0; + asfmux->data_object_position = 0; + asfmux->file_properties_object_position = 0; + asfmux->total_data_packets = 0; + asfmux->file_size = 0; + asfmux->packet_size = 0; + + if (asfmux->payloads) { + GSList *walk; + for (walk = asfmux->payloads; walk; walk = g_slist_next (walk)) { + gst_asf_payload_free ((AsfPayload *) walk->data); + walk->data = NULL; + } + g_slist_free (asfmux->payloads); + } + asfmux->payloads = NULL; + asfmux->payload_data_size = 0; + + asfmux->file_id.v1 = 0; + asfmux->file_id.v2 = 0; + asfmux->file_id.v3 = 0; + asfmux->file_id.v4 = 0; + + gst_tag_setter_reset_tags (GST_TAG_SETTER (asfmux)); +} + +static void +gst_asf_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 (&src_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&audio_sink_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&video_sink_factory)); + + gst_element_class_set_details (element_class, &gst_asf_mux_details); + + GST_DEBUG_CATEGORY_INIT (asfmux_debug, "asfmux", 0, "Muxer for ASF streams"); +} + +static void +gst_asf_mux_finalize (GObject * object) +{ + GstAsfMux *asfmux; + + asfmux = GST_ASF_MUX (object); + + gst_asf_mux_reset (asfmux); + gst_object_unref (asfmux->collect); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_asf_mux_class_init (GstAsfMuxClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->get_property = gst_asf_mux_get_property; + gobject_class->set_property = gst_asf_mux_set_property; + gobject_class->finalize = gst_asf_mux_finalize; + + g_object_class_install_property (gobject_class, PROP_PACKET_SIZE, + g_param_spec_uint ("packet-size", "Packet size", + "The ASF packets size (bytes)", + ASF_MULTIPLE_PAYLOAD_HEADER_SIZE + 1, G_MAXUINT32, + DEFAULT_PACKET_SIZE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (gobject_class, PROP_PREROLL, + g_param_spec_uint64 ("preroll", "Preroll", + "The preroll time (milisecs)", + 0, G_MAXUINT64, + DEFAULT_PREROLL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (gobject_class, PROP_MERGE_STREAM_TAGS, + g_param_spec_boolean ("merge-stream-tags", "Merge Stream Tags", + "If the stream metadata (received as events in the sink) should be " + "merged to the main file metadata.", + DEFAULT_MERGE_STREAM_TAGS, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (gobject_class, PROP_PADDING, + g_param_spec_uint64 ("padding", "Padding", + "Size of the padding object to be added to the end of the header. " + "If this less than 24 (the smaller size of an ASF object), " + "no padding is added.", + 0, G_MAXUINT64, + DEFAULT_PADDING, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (gobject_class, PROP_IS_LIVE, + g_param_spec_boolean ("is-live", "Is Live", + "If this stream should be threated as a live, meaning that it " + "doesn't need indexes nor late update of headers.", + DEFAULT_IS_LIVE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + gstelement_class->request_new_pad = + GST_DEBUG_FUNCPTR (gst_asf_mux_request_new_pad); + gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_asf_mux_change_state); +} + +static void +gst_asf_mux_init (GstAsfMux * asfmux) +{ + GstCaps *caps; + + asfmux->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); + caps = gst_caps_copy (gst_pad_get_pad_template_caps (asfmux->srcpad)); + gst_pad_set_caps (asfmux->srcpad, caps); + gst_caps_unref (caps); + gst_pad_use_fixed_caps (asfmux->srcpad); + gst_element_add_pad (GST_ELEMENT (asfmux), asfmux->srcpad); + + asfmux->collect = gst_collect_pads_new (); + gst_collect_pads_set_function (asfmux->collect, + (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_asf_mux_collected), + asfmux); + + asfmux->payloads = NULL; + asfmux->prop_packet_size = DEFAULT_PACKET_SIZE; + asfmux->prop_preroll = DEFAULT_PREROLL; + asfmux->prop_merge_stream_tags = DEFAULT_MERGE_STREAM_TAGS; + asfmux->prop_padding = DEFAULT_PADDING; + asfmux->prop_is_live = DEFAULT_IS_LIVE; + gst_asf_mux_reset (asfmux); +} + +static gboolean +gst_asf_mux_sink_event (GstPad * pad, GstEvent * event) +{ + gboolean ret; + GstAsfMux *asfmux; + GstAsfPad *asfpad = (GstAsfPad *) gst_pad_get_element_private (pad); + + asfmux = GST_ASF_MUX_CAST (gst_pad_get_parent (pad)); + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_TAG:{ + GST_DEBUG_OBJECT (asfmux, "received tag event"); + /* we discard tag events that come after we started + * writing the headers, because tags are to be in + * the headers + */ + if (asfmux->state == GST_ASF_MUX_STATE_NONE) { + GstTagList *list = NULL; + gst_event_parse_tag (event, &list); + if (asfmux->merge_stream_tags) { + GstTagSetter *setter = GST_TAG_SETTER (asfmux); + const GstTagMergeMode mode = + gst_tag_setter_get_tag_merge_mode (setter); + gst_tag_setter_merge_tags (setter, list, mode); + } else { + if (asfpad->taglist == NULL) { + asfpad->taglist = gst_tag_list_new (); + } + gst_tag_list_insert (asfpad->taglist, list, GST_TAG_MERGE_REPLACE); + } + } + break; + } + default: + break; + } + + ret = asfmux->collect_event (pad, event); + gst_object_unref (asfmux); + return ret; +} + +/** + * gst_asf_mux_push_buffer: + * @asfmux: #GstAsfMux that should push the buffer + * @buf: #GstBuffer to be pushed + * + * Pushes a buffer downstream and adds its size to the total file size + * + * Returns: the result of #gst_pad_push on the buffer + */ +static GstFlowReturn +gst_asf_mux_push_buffer (GstAsfMux * asfmux, GstBuffer * buf) +{ + GstFlowReturn ret; + gst_buffer_set_caps (buf, GST_PAD_CAPS (asfmux->srcpad)); + ret = gst_pad_push (asfmux->srcpad, buf); + if (ret == GST_FLOW_OK) + asfmux->file_size += GST_BUFFER_SIZE (buf); + return ret; +} + +/** + * content_description_calc_size_for_tag: + * @taglist: the #GstTagList that contains the tag + * @tag: the tag's name + * @user_data: a #GstAsfTags struct for putting the results + * + * Function that has the #GstTagForEach signature and + * is used to calculate the size in bytes for each tag + * that can be contained in asf's content description object + * and extended content description object. This size is added + * to the total size for each of that objects in the #GstAsfTags + * struct passed in the user_data pointer. + */ +static void +content_description_calc_size_for_tag (const GstTagList * taglist, + const gchar * tag, gpointer user_data) +{ + const gchar *asftag = gst_asf_get_asf_tag (tag); + GValue value = { 0 }; + guint type; + GstAsfTags *asftags = (GstAsfTags *) user_data; + guint content_size; + + if (asftag == NULL) + return; + + if (!gst_tag_list_copy_value (&value, taglist, tag)) { + return; + } + type = gst_asf_get_tag_field_type (&value); + switch (type) { + case ASF_TAG_TYPE_UNICODE_STR: + { + const gchar *text; + + text = g_value_get_string (&value); + /* +1 -> because of the \0 at the end + * 2* -> because we have uft8, and asf demands utf16 + */ + content_size = 2 * (1 + g_utf8_strlen (text, -1)); + + if (gst_asf_tag_present_in_content_description (tag)) { + asftags->cont_desc_size += content_size; + } + } + break; + case ASF_TAG_TYPE_DWORD: + content_size = 4; + break; + default: + GST_WARNING ("Unhandled asf tag field type %u for tag %s", type, tag); + g_value_reset (&value); + return; + } + if (asftag) { + /* size of the tag content in utf16 + + * size of the tag name + + * 3 uint16 (size of the tag name string, + * size of the tag content string and + * type of content + */ + asftags->ext_cont_desc_size += content_size + + (g_utf8_strlen (asftag, -1) + 1) * 2 + 6; + } + gst_tag_list_add_value (asftags->tags, GST_TAG_MERGE_REPLACE, tag, &value); + g_value_reset (&value); +} + +/* FIXME + * it is awful to keep track of the size here + * and get the same tags in the writing function */ +/** + * gst_asf_mux_get_content_description_tags: + * @asfmux: #GstAsfMux to have its tags proccessed + * @asftags: #GstAsfTags to hold the results + * + * Inspects the tags received by the GstTagSetter interface + * or possibly by sink tag events and calculates the total + * size needed for the default and extended content description objects. + * This results and a copy of the #GstTagList + * are stored in the #GstAsfTags. We store a copy so that + * the sizes estimated here mantain the same until they are + * written to the asf file. + */ +static void +gst_asf_mux_get_content_description_tags (GstAsfMux * asfmux, + GstAsfTags * asftags) +{ + const GstTagList *tags; + GstTagList *taglist = NULL; + + tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (asfmux)); + if (tags && !gst_tag_list_is_empty (tags)) { + if (asftags->tags != NULL) { + gst_tag_list_free (asftags->tags); + } + asftags->tags = gst_tag_list_new (); + asftags->cont_desc_size = 0; + asftags->ext_cont_desc_size = 0; + taglist = asftags->tags; + + GST_DEBUG_OBJECT (asfmux, "Processing tags"); + gst_tag_list_foreach (tags, content_description_calc_size_for_tag, asftags); + } else { + GST_DEBUG_OBJECT (asfmux, "No tags received"); + } + + if (asftags->cont_desc_size > 0) { + asftags->cont_desc_size += ASF_CONTENT_DESCRIPTION_OBJECT_SIZE; + } + if (asftags->ext_cont_desc_size > 0) { + asftags->ext_cont_desc_size += ASF_EXT_CONTENT_DESCRIPTION_OBJECT_SIZE; + } +} + +/** + * add_metadata_tag_size: + * @taglist: #GstTagList + * @tag: tag name + * @user_data: pointer to a guint to store the result + * + * GstTagForeachFunc implementation that accounts the size of + * each tag in the taglist and adds them to the guint pointed + * by the user_data + */ +static void +add_metadata_tag_size (const GstTagList * taglist, const gchar * tag, + gpointer user_data) +{ + const gchar *asftag = gst_asf_get_asf_tag (tag); + GValue value = { 0 }; + guint type; + guint content_size; + guint *total_size = (guint *) user_data; + + if (asftag == NULL) + return; + + if (!gst_tag_list_copy_value (&value, taglist, tag)) { + return; + } + type = gst_asf_get_tag_field_type (&value); + switch (type) { + case ASF_TAG_TYPE_UNICODE_STR: + { + const gchar *text; + + text = g_value_get_string (&value); + /* +1 -> because of the \0 at the end + * 2* -> because we have uft8, and asf demands utf16 + */ + content_size = 2 * (1 + g_utf8_strlen (text, -1)); + } + break; + case ASF_TAG_TYPE_DWORD: + content_size = 4; + break; + default: + GST_WARNING ("Unhandled asf tag field type %u for tag %s", type, tag); + g_value_reset (&value); + return; + } + /* size of reserved (2) + + * size of stream number (2) + + * size of the tag content in utf16 + + * size of the tag name + + * 2 uint16 (size of the tag name string and type of content) + + * 1 uint32 (size of the data) + */ + *total_size += + 4 + content_size + (g_utf8_strlen (asftag, -1) + 1) * 2 + 4 + 4; + g_value_reset (&value); +} + +/** + * gst_asf_mux_get_metadata_object_size: + * @asfmux: #GstAsfMux + * @asfpad: pad for which the metadata object size should be calculated + * + * Calculates the size of the metadata object for the tags of the stream + * handled by the asfpad in the parameter + * + * Returns: The size calculated + */ +static guint +gst_asf_mux_get_metadata_object_size (GstAsfMux * asfmux, GstAsfPad * asfpad) +{ + guint size = ASF_METADATA_OBJECT_SIZE; + if (asfpad->taglist == NULL || gst_tag_list_is_empty (asfpad->taglist)) + return 0; + + gst_tag_list_foreach (asfpad->taglist, add_metadata_tag_size, &size); + return size; +} + +/** + * gst_asf_mux_get_headers_size: + * @asfmux: #GstAsfMux + * + * Calculates the size of the headers of the asf stream + * to be generated by this #GstAsfMux. + * Its used for determining the size of the buffer to allocate + * to exactly fit the headers in. + * Padding and metadata objects sizes are not included. + * + * Returns: the calculated size + */ +static guint +gst_asf_mux_get_headers_size (GstAsfMux * asfmux) +{ + GSList *walk; + gint stream_num = 0; + guint size = ASF_HEADER_OBJECT_SIZE + + ASF_FILE_PROPERTIES_OBJECT_SIZE + ASF_HEADER_EXTENSION_OBJECT_SIZE; + + /* per stream data */ + for (walk = asfmux->collect->data; walk; walk = g_slist_next (walk)) { + GstAsfPad *asfpad = (GstAsfPad *) walk->data; + + if (asfpad->is_audio) + size += ASF_AUDIO_SPECIFIC_DATA_SIZE; + else + size += ASF_VIDEO_SPECIFIC_DATA_SIZE; + + if (asfpad->codec_data) + size += GST_BUFFER_SIZE (asfpad->codec_data); + + stream_num++; + } + size += stream_num * (ASF_STREAM_PROPERTIES_OBJECT_SIZE + + ASF_EXTENDED_STREAM_PROPERTIES_OBJECT_SIZE); + + return size; +} + +/** + * gst_asf_mux_write_header_object: + * @asfmux: + * @buf: pointer to the data pointer + * @size: size of the header object + * @child_objects: number of children objects inside the main header object + * + * Writes the main asf header object start. The buffer pointer + * is incremented to the next writing position. + */ +static void +gst_asf_mux_write_header_object (GstAsfMux * asfmux, guint8 ** buf, + guint64 size, guint32 child_objects) +{ + gst_asf_put_guid (*buf, guids[ASF_HEADER_OBJECT_INDEX]); + GST_WRITE_UINT64_LE (*buf + 16, size); /* object size */ + GST_WRITE_UINT32_LE (*buf + 24, child_objects); /* # of child objects */ + GST_WRITE_UINT8 (*buf + 28, 0x01); /* reserved */ + GST_WRITE_UINT8 (*buf + 29, 0x02); /* reserved */ + *buf += ASF_HEADER_OBJECT_SIZE; +} + +/** + * gst_asf_mux_write_file_properties: + * @asfmux: + * @buf: pointer to the data pointer + * + * Writes the file properties object to the buffer. The buffer pointer + * is incremented to the next writing position. + */ +static void +gst_asf_mux_write_file_properties (GstAsfMux * asfmux, guint8 ** buf) +{ + gst_asf_put_guid (*buf, guids[ASF_FILE_PROPERTIES_OBJECT_INDEX]); + GST_WRITE_UINT64_LE (*buf + 16, ASF_FILE_PROPERTIES_OBJECT_SIZE); /* object size */ + gst_asf_put_guid (*buf + 24, asfmux->file_id); + GST_WRITE_UINT64_LE (*buf + 40, 0); /* file size - needs update */ + gst_asf_put_time (*buf + 48, gst_asf_get_current_time ()); /* creation time */ + GST_WRITE_UINT64_LE (*buf + 56, 0); /* data packets - needs update */ + GST_WRITE_UINT64_LE (*buf + 64, 0); /* play duration - needs update */ + GST_WRITE_UINT64_LE (*buf + 72, 0); /* send duration - needs update */ + GST_WRITE_UINT64_LE (*buf + 80, 0); /* preroll */ + GST_WRITE_UINT32_LE (*buf + 88, 0x1); /* flags - broadcast on */ + GST_WRITE_UINT32_LE (*buf + 92, asfmux->packet_size); /* minimum data packet size */ + GST_WRITE_UINT32_LE (*buf + 96, asfmux->packet_size); /* maximum data packet size */ + GST_WRITE_UINT32_LE (*buf + 100, 0); /* maximum bitrate TODO */ + + *buf += ASF_FILE_PROPERTIES_OBJECT_SIZE; +} + +/** + * gst_asf_mux_write_stream_properties: + * @param asfmux: + * @param buf: pointer to the data pointer + * @param asfpad: Pad that handles the stream + * + * Writes the stream properties object in the buffer + * for the stream handled by the #GstAsfPad passed. + * The pointer is incremented to the next writing position + */ +static void +gst_asf_mux_write_stream_properties (GstAsfMux * asfmux, guint8 ** buf, + GstAsfPad * asfpad) +{ + guint32 codec_data_length = 0; + guint32 media_specific_data_length = 0; + guint16 flags = 0; + + /* codec specific data length */ + if (asfpad->codec_data) + codec_data_length = GST_BUFFER_SIZE (asfpad->codec_data); + if (asfpad->is_audio) + media_specific_data_length = ASF_AUDIO_SPECIFIC_DATA_SIZE; + else + media_specific_data_length = ASF_VIDEO_SPECIFIC_DATA_SIZE; + + GST_DEBUG_OBJECT (asfmux, "Stream %" G_GUINT16_FORMAT " codec data length: %" + G_GUINT32_FORMAT ", media specific data length: %" G_GUINT32_FORMAT, + (guint16) asfpad->stream_number, codec_data_length, + media_specific_data_length); + + gst_asf_put_guid (*buf, guids[ASF_STREAM_PROPERTIES_OBJECT_INDEX]); + GST_WRITE_UINT64_LE (*buf + 16, ASF_STREAM_PROPERTIES_OBJECT_SIZE + codec_data_length + media_specific_data_length); /* object size */ + + /* stream type */ + if (asfpad->is_audio) + gst_asf_put_guid (*buf + 24, guids[ASF_AUDIO_MEDIA_INDEX]); + else + gst_asf_put_guid (*buf + 24, guids[ASF_VIDEO_MEDIA_INDEX]); + /* error correction */ + if (asfpad->is_audio) { + gst_asf_put_guid (*buf + 40, guids[ASF_NO_ERROR_CORRECTION_INDEX]); + } else { + gst_asf_put_guid (*buf + 40, guids[ASF_NO_ERROR_CORRECTION_INDEX]); + } + GST_WRITE_UINT64_LE (*buf + 56, 0); /* time offset */ + + GST_WRITE_UINT32_LE (*buf + 64, codec_data_length + media_specific_data_length); /* type specific data length */ + GST_WRITE_UINT32_LE (*buf + 68, 0); /* error correction data length */ + + flags = (asfpad->stream_number & 0x7F); + GST_WRITE_UINT16_LE (*buf + 72, flags); + GST_WRITE_UINT32_LE (*buf + 74, 0); /* reserved */ + + *buf += ASF_STREAM_PROPERTIES_OBJECT_SIZE; + /* audio specific data */ + if (asfpad->is_audio) { + GstAsfAudioPad *audiopad = (GstAsfAudioPad *) asfpad; + GST_WRITE_UINT16_LE (*buf, audiopad->audioinfo.format); + GST_WRITE_UINT16_LE (*buf + 2, audiopad->audioinfo.channels); + GST_WRITE_UINT32_LE (*buf + 4, audiopad->audioinfo.rate); + GST_WRITE_UINT32_LE (*buf + 8, audiopad->audioinfo.av_bps); + GST_WRITE_UINT16_LE (*buf + 12, audiopad->audioinfo.blockalign); + GST_WRITE_UINT16_LE (*buf + 14, audiopad->audioinfo.size); + GST_WRITE_UINT16_LE (*buf + 16, codec_data_length); + + GST_DEBUG_OBJECT (asfmux, + "wave formatex values: codec_id=%" G_GUINT16_FORMAT ", channels=%" + G_GUINT16_FORMAT ", rate=%" G_GUINT32_FORMAT ", bytes_per_sec=%" + G_GUINT32_FORMAT ", block_alignment=%" G_GUINT16_FORMAT + ", bits_per_sample=%" G_GUINT16_FORMAT ", codec_data_length=%" + G_GUINT16_FORMAT, audiopad->audioinfo.format, + audiopad->audioinfo.channels, audiopad->audioinfo.rate, + audiopad->audioinfo.av_bps, audiopad->audioinfo.blockalign, + audiopad->audioinfo.size, codec_data_length); + + + *buf += ASF_AUDIO_SPECIFIC_DATA_SIZE; + } else { + GstAsfVideoPad *videopad = (GstAsfVideoPad *) asfpad; + GST_WRITE_UINT32_LE (*buf, (guint32) videopad->vidinfo.width); + GST_WRITE_UINT32_LE (*buf + 4, (guint32) videopad->vidinfo.height); + GST_WRITE_UINT8 (*buf + 8, 2); + + /* the BITMAPINFOHEADER size + codec_data size */ + GST_WRITE_UINT16_LE (*buf + 9, + ASF_VIDEO_SPECIFIC_DATA_SIZE + codec_data_length - 11); + + /* BITMAPINFOHEADER */ + GST_WRITE_UINT32_LE (*buf + 11, + ASF_VIDEO_SPECIFIC_DATA_SIZE + codec_data_length - 11); + gst_asf_put_i32 (*buf + 15, videopad->vidinfo.width); + gst_asf_put_i32 (*buf + 19, videopad->vidinfo.height); + GST_WRITE_UINT16_LE (*buf + 23, 1); /* reserved */ + GST_WRITE_UINT16_LE (*buf + 25, videopad->vidinfo.bit_cnt); + GST_WRITE_UINT32_LE (*buf + 27, videopad->vidinfo.compression); + GST_WRITE_UINT32_LE (*buf + 31, videopad->vidinfo.width * + videopad->vidinfo.height * videopad->vidinfo.bit_cnt); + GST_WRITE_UINT32_LE (*buf + 35, videopad->vidinfo.xpels_meter); + GST_WRITE_UINT32_LE (*buf + 39, videopad->vidinfo.ypels_meter); + GST_WRITE_UINT32_LE (*buf + 43, videopad->vidinfo.num_colors); + GST_WRITE_UINT32_LE (*buf + 47, videopad->vidinfo.imp_colors); + + *buf += ASF_VIDEO_SPECIFIC_DATA_SIZE; + } + + if (codec_data_length > 0) + memcpy (*buf, GST_BUFFER_DATA (asfpad->codec_data), codec_data_length); + + *buf += codec_data_length; +} + +/** + * gst_asf_mux_write_header_extension: + * @asfmux: + * @buf: pointer to the buffer pointer + * @extension_size: size of the extensions + * + * Writes the header of the header extension object. The buffer pointer + * is incremented to the next writing position (the header extension object + * childs should be writen from that point) + */ +static void +gst_asf_mux_write_header_extension (GstAsfMux * asfmux, guint8 ** buf, + guint64 extension_size) +{ + gst_asf_put_guid (*buf, guids[ASF_HEADER_EXTENSION_OBJECT_INDEX]); + GST_WRITE_UINT64_LE (*buf + 16, ASF_HEADER_EXTENSION_OBJECT_SIZE + extension_size); /* object size */ + gst_asf_put_guid (*buf + 24, guids[ASF_RESERVED_1_INDEX]); /* reserved */ + GST_WRITE_UINT16_LE (*buf + 40, 6); /* reserved */ + GST_WRITE_UINT32_LE (*buf + 42, extension_size); /* header extension data size */ + *buf += ASF_HEADER_EXTENSION_OBJECT_SIZE; +} + +/** + * gst_asf_mux_write_extended_stream_properties: + * @asfmux: + * @buf: pointer to the buffer pointer + * @asfpad: Pad that handles the stream of the properties to be writen + * + * Writes the extended stream properties object (that is part of the + * header extension objects) for the stream handled by asfpad + */ +static void +gst_asf_mux_write_extended_stream_properties (GstAsfMux * asfmux, guint8 ** buf, + GstAsfPad * asfpad) +{ + gst_asf_put_guid (*buf, guids[ASF_EXTENDED_STREAM_PROPERTIES_OBJECT_INDEX]); + GST_WRITE_UINT64_LE (*buf + 16, ASF_EXTENDED_STREAM_PROPERTIES_OBJECT_SIZE); + GST_WRITE_UINT64_LE (*buf + 24, 0); /* start time */ + GST_WRITE_UINT64_LE (*buf + 32, 0); /* end time */ + GST_WRITE_UINT32_LE (*buf + 40, asfpad->bitrate); /* bitrate */ + GST_WRITE_UINT32_LE (*buf + 44, 0); /* buffer size */ + GST_WRITE_UINT32_LE (*buf + 48, 0); /* initial buffer fullness */ + GST_WRITE_UINT32_LE (*buf + 52, asfpad->bitrate); /* alternate data bitrate */ + GST_WRITE_UINT32_LE (*buf + 56, 0); /* alternate buffer size */ + GST_WRITE_UINT32_LE (*buf + 60, 0); /* alternate initial buffer fullness */ + GST_WRITE_UINT32_LE (*buf + 64, 0); /* maximum object size */ + + /* flags */ + if (asfpad->is_audio) { + /* TODO check if audio is seekable */ + GST_WRITE_UINT32_LE (*buf + 68, 0x0); + } else { + /* video has indexes, so it is seekable */ + GST_WRITE_UINT32_LE (*buf + 68, 0x2); + } + + GST_WRITE_UINT16_LE (*buf + 72, asfpad->stream_number); + GST_WRITE_UINT16_LE (*buf + 74, 0); /* language index */ + GST_WRITE_UINT64_LE (*buf + 76, 0); /* avg time per frame */ + GST_WRITE_UINT16_LE (*buf + 84, 0); /* stream name count */ + GST_WRITE_UINT16_LE (*buf + 86, 0); /* payload extension count */ + + *buf += ASF_EXTENDED_STREAM_PROPERTIES_OBJECT_SIZE; +} + +/** + * gst_asf_mux_write_string_with_size: + * @asfmux: + * @size_buf: pointer to the memory position to write the size of the string + * @str_buf: pointer to the memory position to write the string + * @str: the string to be writen (in UTF-8) + * @use32: if the string size should be writen with 32 bits (if true) + * or with 16 (if false) + * + * Writes a string with its size as it is needed in many asf objects. + * The size is writen to size_buf as a WORD field if use32 is false, and + * as a DWORD if use32 is true. The string is writen to str_buf in UTF16-LE. + * The string should be passed in UTF-8. + * + * The string size in UTF16-LE is returned. + */ +static guint64 +gst_asf_mux_write_string_with_size (GstAsfMux * asfmux, + guint8 * size_buf, guint8 * str_buf, const gchar * str, gboolean use32) +{ + GError *error = NULL; + gsize str_size = 0; + gchar *str_utf16 = NULL; + + GST_LOG_OBJECT (asfmux, "Writing extended content description string: " + "%s", str); + + /* + * Covert the string to utf16 + * Also force the last bytes to null terminated, + * tags were with extra weird characters without it. + */ + str_utf16 = g_convert (str, -1, "UTF-16LE", "UTF-8", NULL, &str_size, &error); + str_utf16[str_size + 1] = '\0'; + str_utf16[str_size + 2] = '\0'; + + /* sum up the null terminating char */ + str_size += 2; + + if (use32) + GST_WRITE_UINT32_LE (size_buf, str_size); + else + GST_WRITE_UINT16_LE (size_buf, str_size); + if (error) { + GST_WARNING_OBJECT (asfmux, "Error converting string " + "to UTF-16: %s - %s", str, error->message); + g_free (error); + memset (str_buf, 0, str_size); + } else { + memcpy (str_buf, str_utf16, str_size); + } + g_free (str_utf16); + return str_size; +} + +/** + * gst_asf_mux_write_content_description_entry: + * @asfmux: + * @tags: + * @tagname: + * @size_buf: + * @data_buf: + * + * Checks if a string tag with tagname exists in the taglist. If it + * exists it is writen as an UTF-16LE to data_buf and its size in bytes + * is writen to size_buf. It is used for writing content description + * object fields. + * + * Returns: the size of the string + */ +static guint16 +gst_asf_mux_write_content_description_entry (GstAsfMux * asfmux, + const GstTagList * tags, const gchar * tagname, + guint8 * size_buf, guint8 * data_buf) +{ + gchar *text = NULL; + guint16 text_size = 0; + if (gst_tag_list_get_string (tags, tagname, &text)) { + text_size = gst_asf_mux_write_string_with_size (asfmux, size_buf, + data_buf, text, FALSE); + g_free (text); + } else { + GST_WRITE_UINT16_LE (size_buf, 0); + } + return text_size; +} + +static guint64 +gst_asf_mux_write_ext_content_description_dword_entry (GstAsfMux * asfmux, + guint8 * buf, const gchar * asf_tag, const guint32 value) +{ + guint64 tag_size; + GST_DEBUG_OBJECT (asfmux, "Writing extended content description tag: " + "%s (%u)", asf_tag, value); + + tag_size = gst_asf_mux_write_string_with_size (asfmux, buf, buf + 2, + asf_tag, FALSE); + buf += tag_size + 2; + GST_WRITE_UINT16_LE (buf, ASF_TAG_TYPE_DWORD); + GST_WRITE_UINT16_LE (buf + 2, 4); + GST_WRITE_UINT32_LE (buf + 4, value); + + /* tagsize -> string size + * 2 -> string size field size + * 4 -> dword entry + * 4 -> type of entry + entry size + */ + return tag_size + 2 + 4 + 4; +} + +static guint64 +gst_asf_mux_write_ext_content_description_string_entry (GstAsfMux * asfmux, + guint8 * buf, const gchar * asf_tag, const gchar * text) +{ + guint64 tag_size = 0; + guint64 text_size = 0; + + GST_DEBUG_OBJECT (asfmux, "Writing extended content description tag: " + "%s (%s)", asf_tag, text); + + tag_size = gst_asf_mux_write_string_with_size (asfmux, + buf, buf + 2, asf_tag, FALSE); + GST_WRITE_UINT16_LE (buf + tag_size + 2, ASF_TAG_TYPE_UNICODE_STR); + buf += tag_size + 2 + 2; + text_size = gst_asf_mux_write_string_with_size (asfmux, + buf, buf + 2, text, FALSE); + + /* the size of the strings in utf16-le plus the 3 WORD fields */ + return tag_size + text_size + 6; +} + +static void +gst_asf_mux_write_content_description (GstAsfMux * asfmux, guint8 ** buf, + const GstTagList * tags) +{ + guint8 *values = (*buf) + ASF_CONTENT_DESCRIPTION_OBJECT_SIZE; + guint64 size = 0; + + GST_DEBUG_OBJECT (asfmux, "Writing content description object"); + + gst_asf_put_guid (*buf, guids[ASF_CONTENT_DESCRIPTION_INDEX]); + + values += gst_asf_mux_write_content_description_entry (asfmux, tags, + GST_TAG_TITLE, *buf + 24, values); + values += gst_asf_mux_write_content_description_entry (asfmux, tags, + GST_TAG_ARTIST, *buf + 26, values); + values += gst_asf_mux_write_content_description_entry (asfmux, tags, + GST_TAG_COPYRIGHT, *buf + 28, values); + values += gst_asf_mux_write_content_description_entry (asfmux, tags, + GST_TAG_DESCRIPTION, *buf + 30, values); + + /* rating is currently not present in gstreamer tags, so we put 0 */ + GST_WRITE_UINT16_LE (*buf + 32, 0); + + size += values - *buf; + GST_WRITE_UINT64_LE (*buf + 16, size); + *buf += size; +} + +static void +write_ext_content_description_tag (const GstTagList * taglist, + const gchar * tag, gpointer user_data) +{ + const gchar *asftag = gst_asf_get_asf_tag (tag); + GValue value = { 0 }; + guint type; + GstAsfExtContDescData *data = (GstAsfExtContDescData *) user_data; + + if (asftag == NULL) + return; + + if (!gst_tag_list_copy_value (&value, taglist, tag)) { + return; + } + + type = gst_asf_get_tag_field_type (&value); + switch (type) { + case ASF_TAG_TYPE_UNICODE_STR: + { + const gchar *text; + text = g_value_get_string (&value); + data->size += + gst_asf_mux_write_ext_content_description_string_entry (data->asfmux, + data->buf + data->size, asftag, text); + } + break; + case ASF_TAG_TYPE_DWORD: + { + guint num = g_value_get_uint (&value); + data->size += + gst_asf_mux_write_ext_content_description_dword_entry (data->asfmux, + data->buf + data->size, asftag, num); + } + break; + default: + GST_WARNING_OBJECT (data->asfmux, + "Unhandled asf tag field type %u for tag %s", type, tag); + g_value_reset (&value); + return; + } + data->count++; + g_value_reset (&value); +} + +static void +gst_asf_mux_write_ext_content_description (GstAsfMux * asfmux, guint8 ** buf, + GstTagList * tags) +{ + GstAsfExtContDescData extContDesc; + extContDesc.asfmux = asfmux; + extContDesc.buf = *buf; + extContDesc.count = 0; + extContDesc.size = ASF_EXT_CONTENT_DESCRIPTION_OBJECT_SIZE; + + GST_DEBUG_OBJECT (asfmux, "Writing extended content description object"); + gst_asf_put_guid (*buf, guids[ASF_EXT_CONTENT_DESCRIPTION_INDEX]); + + gst_tag_list_foreach (tags, write_ext_content_description_tag, &extContDesc); + + GST_WRITE_UINT64_LE (*buf + 16, extContDesc.size); + GST_WRITE_UINT16_LE (*buf + 24, extContDesc.count); + + *buf += extContDesc.size; +} + +static void +write_metadata_tag (const GstTagList * taglist, const gchar * tag, + gpointer user_data) +{ + const gchar *asftag = gst_asf_get_asf_tag (tag); + GValue value = { 0 }; + guint type; + GstAsfMetadataObjData *data = (GstAsfMetadataObjData *) user_data; + guint16 tag_size; + guint32 content_size; + + if (asftag == NULL) + return; + + if (!gst_tag_list_copy_value (&value, taglist, tag)) { + return; + } + + type = gst_asf_get_tag_field_type (&value); + switch (type) { + case ASF_TAG_TYPE_UNICODE_STR: + { + const gchar *text; + text = g_value_get_string (&value); + GST_WRITE_UINT16_LE (data->buf + data->size, 0); + GST_WRITE_UINT16_LE (data->buf + data->size + 2, data->stream_num); + data->size += 4; + + tag_size = gst_asf_mux_write_string_with_size (data->asfmux, + data->buf + data->size, data->buf + data->size + 8, asftag, FALSE); + data->size += 2; + + GST_WRITE_UINT16_LE (data->buf + data->size, type); + data->size += 2; + + content_size = gst_asf_mux_write_string_with_size (data->asfmux, + data->buf + data->size, data->buf + data->size + tag_size + 4, text, + TRUE); + data->size += tag_size + content_size + 4; + } + break; + case ASF_TAG_TYPE_DWORD: + { + guint num = g_value_get_uint (&value); + GST_WRITE_UINT16_LE (data->buf + data->size, 0); + GST_WRITE_UINT16_LE (data->buf + data->size + 2, data->stream_num); + data->size += 4; + + tag_size = gst_asf_mux_write_string_with_size (data->asfmux, + data->buf + data->size, data->buf + data->size + 8, asftag, FALSE); + data->size += 2; + + GST_WRITE_UINT16_LE (data->buf + data->size, type); + data->size += 2; + /* dword length */ + GST_WRITE_UINT32_LE (data->buf + data->size, 4); + data->size += 4 + tag_size; + + GST_WRITE_UINT32_LE (data->buf + data->size, num); + data->size += 4; + } + break; + default: + GST_WARNING_OBJECT (data->asfmux, + "Unhandled asf tag field type %u for tag %s", type, tag); + g_value_reset (&value); + return; + } + + data->count++; + g_value_reset (&value); +} + +static void +gst_asf_mux_write_metadata_object (GstAsfMux * asfmux, guint8 ** buf, + GstAsfPad * asfpad) +{ + GstAsfMetadataObjData metaObjData; + metaObjData.asfmux = asfmux; + metaObjData.buf = *buf; + metaObjData.count = 0; + metaObjData.size = ASF_METADATA_OBJECT_SIZE; + metaObjData.stream_num = asfpad->stream_number; + + if (asfpad->taglist == NULL || gst_tag_list_is_empty (asfpad->taglist)) + return; + + GST_DEBUG_OBJECT (asfmux, "Writing metadata object"); + gst_asf_put_guid (*buf, guids[ASF_METADATA_OBJECT_INDEX]); + + gst_tag_list_foreach (asfpad->taglist, write_metadata_tag, &metaObjData); + + GST_WRITE_UINT64_LE (*buf + 16, metaObjData.size); + GST_WRITE_UINT16_LE (*buf + 24, metaObjData.count); + + *buf += metaObjData.size; +} + +static void +gst_asf_mux_write_padding_object (GstAsfMux * asfmux, guint8 ** buf, + guint64 padding) +{ + if (padding < ASF_PADDING_OBJECT_SIZE) { + return; + } + + GST_DEBUG_OBJECT (asfmux, "Writing padding object of size %" G_GUINT64_FORMAT, + padding); + gst_asf_put_guid (*buf, guids[ASF_PADDING_OBJECT_INDEX]); + GST_WRITE_UINT64_LE (*buf + 16, padding); + memset (*buf + 24, 0, padding - ASF_PADDING_OBJECT_SIZE); + *buf += padding; +} + +static void +gst_asf_mux_write_data_object (GstAsfMux * asfmux, guint8 ** buf) +{ + gst_asf_put_guid (*buf, guids[ASF_DATA_OBJECT_INDEX]); + GST_WRITE_UINT64_LE (*buf + 16, 0); /* object size - needs updating */ + gst_asf_put_guid (*buf + 24, asfmux->file_id); + GST_WRITE_UINT64_LE (*buf + 40, 0); /* total data packets */ + GST_WRITE_UINT16_LE (*buf + 48, 0x0101); /* reserved */ + *buf += ASF_DATA_OBJECT_SIZE; +} + +/** + * gst_asf_mux_start_file: + * @asfmux: #GstAsfMux + * + * Starts the asf file/stream by creating and pushing + * the headers downstream. + */ +static GstFlowReturn +gst_asf_mux_start_file (GstAsfMux * asfmux) +{ + GstBuffer *buf = NULL; + guint8 *bufdata = NULL; + GSList *walk; + guint stream_num = g_slist_length (asfmux->collect->data); + guint metadata_obj_size = 0; + GstAsfTags *asftags; + guint64 padding = asfmux->prop_padding; + if (padding < ASF_PADDING_OBJECT_SIZE) + padding = 0; + + /* from this point we started writing the headers */ + GST_INFO_OBJECT (asfmux, "Writing headers"); + asfmux->state = GST_ASF_MUX_STATE_HEADERS; + + /* let downstream know we think in BYTES and expect to do seeking later */ + gst_pad_push_event (asfmux->srcpad, + gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0)); + + asfmux->file_id = gst_asf_generate_file_id (); + + /* Get the metadata for content description object. + * We store our own taglist because it might get changed from now + * to the time we actually add its contents to the file, changing + * the size of the data we already calculated here. + */ + asftags = g_new0 (GstAsfTags, 1); + gst_asf_mux_get_content_description_tags (asfmux, asftags); + + /* get the total metadata objects size */ + for (walk = asfmux->collect->data; walk; walk = g_slist_next (walk)) { + metadata_obj_size += gst_asf_mux_get_metadata_object_size (asfmux, + (GstAsfPad *) walk->data); + } + + /* alloc a buffer for all header objects */ + buf = gst_buffer_new_and_alloc (gst_asf_mux_get_headers_size (asfmux) + + asftags->cont_desc_size + + asftags->ext_cont_desc_size + + metadata_obj_size + padding + ASF_DATA_OBJECT_SIZE); + bufdata = GST_BUFFER_DATA (buf); + + gst_asf_mux_write_header_object (asfmux, &bufdata, GST_BUFFER_SIZE (buf) - + ASF_DATA_OBJECT_SIZE, 2 + stream_num); + + /* get the position of the file properties object for + * updating it in gst_asf_mux_stop_file */ + asfmux->file_properties_object_position = bufdata - GST_BUFFER_DATA (buf); + gst_asf_mux_write_file_properties (asfmux, &bufdata); + + for (walk = asfmux->collect->data; walk; walk = g_slist_next (walk)) { + gst_asf_mux_write_stream_properties (asfmux, &bufdata, + (GstAsfPad *) walk->data); + } + + if (asftags->cont_desc_size) { + gst_asf_mux_write_content_description (asfmux, &bufdata, asftags->tags); + } + if (asftags->ext_cont_desc_size) { + gst_asf_mux_write_ext_content_description (asfmux, &bufdata, asftags->tags); + } + + if (asftags) { + if (asftags->tags) + gst_tag_list_free (asftags->tags); + g_free (asftags); + } + + /* writing header extension objects */ + gst_asf_mux_write_header_extension (asfmux, &bufdata, stream_num * + ASF_EXTENDED_STREAM_PROPERTIES_OBJECT_SIZE + metadata_obj_size); + for (walk = asfmux->collect->data; walk; walk = g_slist_next (walk)) { + gst_asf_mux_write_extended_stream_properties (asfmux, &bufdata, + (GstAsfPad *) walk->data); + } + for (walk = asfmux->collect->data; walk; walk = g_slist_next (walk)) { + gst_asf_mux_write_metadata_object (asfmux, &bufdata, + (GstAsfPad *) walk->data); + } + + gst_asf_mux_write_padding_object (asfmux, &bufdata, padding); + + /* store data object position for later updating some fields */ + asfmux->data_object_position = bufdata - GST_BUFFER_DATA (buf); + gst_asf_mux_write_data_object (asfmux, &bufdata); + + g_assert (bufdata - GST_BUFFER_DATA (buf) == GST_BUFFER_SIZE (buf)); + return gst_asf_mux_push_buffer (asfmux, buf); +} + +/** + * gst_asf_mux_add_simple_index_entry: + * @asfmux: + * @videopad: + * + * Adds a new entry to the simple index of the stream handler by videopad. + * This functions doesn't check if the time ellapsed + * is larger than the established time interval between entries. The caller + * is responsible for verifying this. + */ +static void +gst_asf_mux_add_simple_index_entry (GstAsfMux * asfmux, + GstAsfVideoPad * videopad) +{ + SimpleIndexEntry *entry = NULL; + GST_DEBUG_OBJECT (asfmux, "Adding new simple index entry " + "packet number:%" G_GUINT32_FORMAT ", " + "packet count:%" G_GUINT16_FORMAT, + videopad->last_keyframe_packet, videopad->last_keyframe_packet_count); + entry = g_malloc0 (sizeof (SimpleIndexEntry)); + entry->packet_number = videopad->last_keyframe_packet; + entry->packet_count = videopad->last_keyframe_packet_count; + if (entry->packet_count > videopad->max_keyframe_packet_count) + videopad->max_keyframe_packet_count = entry->packet_count; + videopad->simple_index = g_slist_append (videopad->simple_index, entry); +} + +/** + * gst_asf_mux_send_packet: + * @asfmux: + * @buf: The asf data packet + * + * Pushes an asf data packet downstream. The total number + * of packets and bytes of the stream are incremented. + * + * Returns: the result of pushing the buffer downstream + */ +static GstFlowReturn +gst_asf_mux_send_packet (GstAsfMux * asfmux, GstBuffer * buf) +{ + g_assert (GST_BUFFER_SIZE (buf) == asfmux->packet_size); + asfmux->total_data_packets++; + GST_LOG_OBJECT (asfmux, + "Pushing a packet of size %u and timestamp %" G_GUINT64_FORMAT, + GST_BUFFER_SIZE (buf), GST_BUFFER_TIMESTAMP (buf)); + return gst_asf_mux_push_buffer (asfmux, buf); +} + +/** + * gst_asf_mux_flush_payloads: + * @asfmux: #GstAsfMux to flush the payloads from + * + * Fills an asf packet with asfmux queued payloads and + * pushes it downstream. + * + * Returns: The result of pushing the packet + */ +static GstFlowReturn +gst_asf_mux_flush_payloads (GstAsfMux * asfmux) +{ + GstBuffer *buf; + guint8 payloads_count = 0; /* we only use 6 bits, max is 63 */ + guint i; + GstClockTime send_ts = GST_CLOCK_TIME_NONE; + guint64 size_left; + guint8 *data; + GSList *walk; + GstAsfPad *pad; + gboolean has_keyframe; + AsfPayload *payload; + guint32 payload_size; + + if (asfmux->payloads == NULL) + return GST_FLOW_OK; /* nothing to send is ok */ + + GST_DEBUG_OBJECT (asfmux, "Flushing payloads"); + + buf = gst_buffer_new_and_alloc (asfmux->packet_size); + memset (GST_BUFFER_DATA (buf), 0, asfmux->packet_size); + + /* 1 for the multiple payload flags */ + data = GST_BUFFER_DATA (buf) + ASF_PAYLOAD_PARSING_INFO_SIZE + 1; + size_left = asfmux->packet_size - ASF_PAYLOAD_PARSING_INFO_SIZE - 1; + + has_keyframe = FALSE; + walk = asfmux->payloads; + while (walk && payloads_count < MAX_PAYLOADS_IN_A_PACKET) { + payload = (AsfPayload *) walk->data; + pad = (GstAsfPad *) payload->pad; + payload_size = gst_asf_payload_get_size (payload); + if (size_left < payload_size) { + break; /* next payload doesn't fit fully */ + } + + if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (send_ts))) { + send_ts = GST_BUFFER_TIMESTAMP (payload->data); + } + + /* adding new simple index entry (if needed) */ + if (!pad->is_audio + && GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (payload->data))) { + GstAsfVideoPad *videopad = (GstAsfVideoPad *) pad; + if (videopad->has_keyframe) { + for (; videopad->next_index_time <= + ASF_MILI_TO_100NANO (payload->presentation_time); + videopad->next_index_time += videopad->time_interval) { + gst_asf_mux_add_simple_index_entry (asfmux, videopad); + } + } + } + + /* serialize our payload */ + GST_DEBUG_OBJECT (asfmux, "Serializing a payload into the packet. " + "Stream number:%" G_GUINT16_FORMAT + ", media object number:%" G_GUINT16_FORMAT + ", offset into media object:%" G_GUINT32_FORMAT + ", replicated data length:%" G_GUINT16_FORMAT + ", media object size:%" G_GUINT32_FORMAT + ", presentation time:%" G_GUINT32_FORMAT + ", payload size:%" G_GUINT16_FORMAT, + payload->stream_number & 0x7F, + (guint16) payload->media_obj_num, payload->offset_in_media_obj, + (guint16) payload->replicated_data_length, + payload->media_object_size, + payload->presentation_time, (guint16) GST_BUFFER_SIZE (payload->data)); + gst_asf_put_payload (data, payload); + if (!payload->has_packet_info) { + payload->has_packet_info = TRUE; + payload->packet_number = asfmux->total_data_packets; + } + + if (ASF_PAYLOAD_IS_KEYFRAME (payload)) { + has_keyframe = TRUE; + if (!pad->is_audio) { + GstAsfVideoPad *videopad = (GstAsfVideoPad *) pad; + videopad->last_keyframe_packet = payload->packet_number; + videopad->last_keyframe_packet_count = payload->packet_count; + videopad->has_keyframe = TRUE; + } + } + + /* update our variables */ + data += payload_size; + size_left -= payload_size; + payloads_count++; + walk = g_slist_next (walk); + } + + /* remove flushed payloads */ + GST_LOG_OBJECT (asfmux, "Freeing already used payloads"); + for (i = 0; i < payloads_count; i++) { + GSList *aux = g_slist_nth (asfmux->payloads, 0); + AsfPayload *payload; + g_assert (aux); + payload = (AsfPayload *) aux->data; + asfmux->payloads = g_slist_remove (asfmux->payloads, payload); + asfmux->payload_data_size -= + (GST_BUFFER_SIZE (payload->data) + ASF_MULTIPLE_PAYLOAD_HEADER_SIZE); + gst_asf_payload_free (payload); + } + + /* check if we can add part of the next payload */ + if (asfmux->payloads && size_left > ASF_MULTIPLE_PAYLOAD_HEADER_SIZE) { + AsfPayload *payload = + (AsfPayload *) g_slist_nth (asfmux->payloads, 0)->data; + guint16 bytes_writen; + GST_DEBUG_OBJECT (asfmux, "Adding part of a payload to a packet"); + + if (ASF_PAYLOAD_IS_KEYFRAME (payload)) + has_keyframe = TRUE; + + if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (send_ts))) { + send_ts = GST_BUFFER_TIMESTAMP (payload->data); + } + + bytes_writen = gst_asf_put_subpayload (data, payload, size_left); + if (!payload->has_packet_info) { + payload->has_packet_info = TRUE; + payload->packet_number = asfmux->total_data_packets; + } + asfmux->payload_data_size -= bytes_writen; + size_left -= (bytes_writen + ASF_MULTIPLE_PAYLOAD_HEADER_SIZE); + payloads_count++; + } + + GST_LOG_OBJECT (asfmux, "Payload data size: %" G_GUINT32_FORMAT, + asfmux->payload_data_size); + + /* fill payload parsing info */ + data = GST_BUFFER_DATA (buf); + /* flags */ + GST_WRITE_UINT8 (data, (0x0 << 7) | /* no error correction */ + (ASF_FIELD_TYPE_DWORD << 5) | /* packet length type */ + (ASF_FIELD_TYPE_DWORD << 3) | /* padding length type */ + (ASF_FIELD_TYPE_NONE << 1) | /* sequence type type */ + 0x1); /* multiple payloads */ + + /* property flags - according to the spec, this should not change */ + GST_WRITE_UINT8 (data + 1, (ASF_FIELD_TYPE_BYTE << 6) | /* stream number length type */ + (ASF_FIELD_TYPE_BYTE << 4) | /* media obj number length type */ + (ASF_FIELD_TYPE_DWORD << 2) | /* offset info media object length type */ + (ASF_FIELD_TYPE_BYTE)); /* replicated data length type */ + + GST_WRITE_UINT32_LE (data + 2, asfmux->packet_size); + GST_WRITE_UINT32_LE (data + 6, size_left); /* padding size */ + + /* packet send time */ + if (GST_CLOCK_TIME_IS_VALID (send_ts)) { + GST_WRITE_UINT32_LE (data + 10, (send_ts / GST_MSECOND)); + GST_BUFFER_TIMESTAMP (buf) = send_ts; + } + + /* packet duration */ + GST_WRITE_UINT16_LE (data + 14, 0); /* FIXME send duration needs to be estimated */ + + /* multiple payloads flags */ + GST_WRITE_UINT8 (data + 16, 0x2 << 6 | payloads_count); + + if (payloads_count == 0) { + GST_WARNING_OBJECT (asfmux, "Sending packet without any payload"); + } + asfmux->data_object_size += GST_BUFFER_SIZE (buf); + if (!has_keyframe) + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); + return gst_asf_mux_send_packet (asfmux, buf); +} + +/** + * stream_number_compare: + * @a: a #GstAsfPad + * @b: another #GstAsfPad + * + * Utility function to compare #GstAsfPad by their stream numbers + * + * Returns: The difference between their stream numbers + */ +static gint +stream_number_compare (gconstpointer a, gconstpointer b) +{ + GstAsfPad *pad_a = (GstAsfPad *) a; + GstAsfPad *pad_b = (GstAsfPad *) b; + return pad_b->stream_number - pad_a->stream_number; +} + +static GstFlowReturn +gst_asf_mux_push_simple_index (GstAsfMux * asfmux, GstAsfVideoPad * pad) +{ + guint64 object_size = ASF_SIMPLE_INDEX_OBJECT_SIZE + + g_slist_length (pad->simple_index) * ASF_SIMPLE_INDEX_ENTRY_SIZE; + GstBuffer *buf = gst_buffer_new_and_alloc (object_size); + GSList *walk; + guint8 *data = GST_BUFFER_DATA (buf); + guint32 entries_count = g_slist_length (pad->simple_index); + + gst_asf_put_guid (data, guids[ASF_SIMPLE_INDEX_OBJECT_INDEX]); + GST_WRITE_UINT64_LE (data + 16, object_size); + gst_asf_put_guid (data + 24, asfmux->file_id); + GST_WRITE_UINT64_LE (data + 40, pad->time_interval); + GST_WRITE_UINT32_LE (data + 48, pad->max_keyframe_packet_count); + GST_WRITE_UINT32_LE (data + 52, entries_count); + data += ASF_SIMPLE_INDEX_OBJECT_SIZE; + + GST_DEBUG_OBJECT (asfmux, + "Simple index object values - size:%" G_GUINT64_FORMAT ", time interval:%" + G_GUINT64_FORMAT ", max packet count:%" G_GUINT32_FORMAT ", entries:%" + G_GUINT16_FORMAT, object_size, pad->time_interval, + pad->max_keyframe_packet_count, entries_count); + + for (walk = pad->simple_index; walk; walk = g_slist_next (walk)) { + SimpleIndexEntry *entry = (SimpleIndexEntry *) walk->data; + GST_DEBUG_OBJECT (asfmux, "Simple index entry: packet_number:%" + G_GUINT32_FORMAT " packet_count:%" G_GUINT16_FORMAT, + entry->packet_number, entry->packet_count); + GST_WRITE_UINT32_LE (data, entry->packet_number); + GST_WRITE_UINT16_LE (data + 4, entry->packet_count); + data += ASF_SIMPLE_INDEX_ENTRY_SIZE; + } + + GST_DEBUG_OBJECT (asfmux, "Pushing the simple index"); + g_assert (data - GST_BUFFER_DATA (buf) == object_size); + return gst_asf_mux_push_buffer (asfmux, buf); +} + +static GstFlowReturn +gst_asf_mux_write_indexes (GstAsfMux * asfmux) +{ + GSList *ordered_pads; + GSList *walker; + GstFlowReturn ret = GST_FLOW_OK; + + /* write simple indexes for video medias */ + ordered_pads = + g_slist_sort (asfmux->collect->data, + (GCompareFunc) stream_number_compare); + for (walker = ordered_pads; walker; walker = g_slist_next (walker)) { + GstAsfPad *pad = (GstAsfPad *) walker->data; + if (!pad->is_audio) { + ret = gst_asf_mux_push_simple_index (asfmux, (GstAsfVideoPad *) pad); + if (ret != GST_FLOW_OK) { + GST_ERROR_OBJECT (asfmux, "Failed to write simple index for stream %" + G_GUINT16_FORMAT, (guint16) pad->stream_number); + return ret; + } + } + } + return ret; +} + +/** + * gst_asf_mux_stop_file: + * @asfmux: #GstAsfMux + * + * Finalizes the asf stream by pushing the indexes after + * the data object. Also seeks back to the header positions + * to rewrite some fields such as the total number of bytes + * of the file, or any other that couldn't be predicted/known + * back on the header generation. + * + * Returns: GST_FLOW_OK on success + */ +static GstFlowReturn +gst_asf_mux_stop_file (GstAsfMux * asfmux) +{ + GstEvent *event; + GstBuffer *buf; + GstFlowReturn ret = GST_FLOW_OK; + GSList *walk; + GstClockTime play_duration = 0; + guint32 bitrate = 0; + + /* write indexes */ + ret = gst_asf_mux_write_indexes (asfmux); + if (ret != GST_FLOW_OK) { + GST_ERROR_OBJECT (asfmux, "Failed to write indexes"); + return ret; + } + + /* find max stream duration and bitrate */ + for (walk = asfmux->collect->data; walk; walk = g_slist_next (walk)) { + GstAsfPad *pad = (GstAsfPad *) walk->data; + bitrate += pad->bitrate; + if (pad->play_duration > play_duration) + play_duration = pad->play_duration; + } + + /* going back to file properties object to fill in + * values we didn't know back then */ + GST_DEBUG_OBJECT (asfmux, + "Sending new segment to file properties object position"); + event = + gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, + asfmux->file_properties_object_position + 40, GST_CLOCK_TIME_NONE, 0); + if (!gst_pad_push_event (asfmux->srcpad, event)) { + GST_ERROR_OBJECT (asfmux, "Failed to update file properties object"); + return GST_FLOW_ERROR; + } + /* All file properties fields except the first 40 bytes */ + buf = gst_buffer_new_and_alloc (ASF_FILE_PROPERTIES_OBJECT_SIZE - 40); + + GST_WRITE_UINT64_LE (GST_BUFFER_DATA (buf), asfmux->file_size); + gst_asf_put_time (GST_BUFFER_DATA (buf) + 8, gst_asf_get_current_time ()); + GST_WRITE_UINT64_LE (GST_BUFFER_DATA (buf) + 16, asfmux->total_data_packets); + GST_WRITE_UINT64_LE (GST_BUFFER_DATA (buf) + 24, (play_duration / 100) + + ASF_MILI_TO_100NANO (asfmux->preroll)); + GST_WRITE_UINT64_LE (GST_BUFFER_DATA (buf) + 32, (play_duration / 100)); /* TODO send duration */ + GST_WRITE_UINT64_LE (GST_BUFFER_DATA (buf) + 40, asfmux->preroll); + GST_WRITE_UINT32_LE (GST_BUFFER_DATA (buf) + 48, 0x2); /* flags - seekable */ + GST_WRITE_UINT32_LE (GST_BUFFER_DATA (buf) + 52, asfmux->packet_size); + GST_WRITE_UINT32_LE (GST_BUFFER_DATA (buf) + 56, asfmux->packet_size); + /* FIXME - we want the max instantaneous bitrate, for vbr streams, we can't + * get it this way, this would be the average, right? */ + GST_WRITE_UINT32_LE (GST_BUFFER_DATA (buf) + 60, bitrate); /* max bitrate */ + + /* we don't use gst_asf_mux_push_buffer because we are overwriting + * already sent data */ + ret = gst_pad_push (asfmux->srcpad, buf); + if (ret != GST_FLOW_OK) { + GST_ERROR_OBJECT (asfmux, "Failed to update file properties object"); + return ret; + } + + GST_DEBUG_OBJECT (asfmux, "Seeking back to data object"); + + /* seek back to the data object */ + event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, + asfmux->data_object_position + 16, GST_CLOCK_TIME_NONE, 0); + + if (!gst_pad_push_event (asfmux->srcpad, event)) { + GST_ERROR_OBJECT (asfmux, "Seek to update data object failed"); + return GST_FLOW_ERROR; + } + + buf = gst_buffer_new_and_alloc (32); /* qword+guid+qword */ + GST_WRITE_UINT64_LE (GST_BUFFER_DATA (buf), asfmux->data_object_size + + ASF_DATA_OBJECT_SIZE); + gst_asf_put_guid (GST_BUFFER_DATA (buf) + 8, asfmux->file_id); + GST_WRITE_UINT64_LE (GST_BUFFER_DATA (buf) + 24, asfmux->total_data_packets); + + return gst_pad_push (asfmux->srcpad, buf); +} + +/** + * gst_asf_mux_process_buffer: + * @asfmux: + * @pad: stream of the buffer + * @buf: The buffer to be processed + * + * Processes the buffer by parsing it and + * queueing it up as an asf payload for later + * being added and pushed inside an asf packet. + * + * Returns: a #GstFlowReturn + */ +static GstFlowReturn +gst_asf_mux_process_buffer (GstAsfMux * asfmux, GstAsfPad * pad, + GstBuffer * buf) +{ + guint8 keyframe; + AsfPayload *payload; + + payload = g_malloc0 (sizeof (AsfPayload)); + payload->pad = (GstCollectData *) pad; + payload->data = buf; + + GST_LOG_OBJECT (asfmux, + "Processing payload data for stream number %" G_GUINT16_FORMAT, + pad->stream_number); + + /* stream number */ + if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) { + keyframe = 0; + } else { + keyframe = 0x1 << 7; + } + payload->stream_number = keyframe | pad->stream_number; + + payload->media_obj_num = pad->media_object_number; + payload->offset_in_media_obj = 0; + payload->replicated_data_length = 8; + + /* replicated data - 1) media object size */ + payload->media_object_size = GST_BUFFER_SIZE (buf); + /* replicated data - 2) presentation time */ + if (!GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buf))) { + GST_ERROR_OBJECT (asfmux, "Received buffer without timestamp"); + gst_asf_payload_free (payload); + return GST_FLOW_ERROR; + } + payload->presentation_time = asfmux->preroll + + (GST_BUFFER_TIMESTAMP (buf) / GST_MSECOND); + + /* update counting values */ + pad->media_object_number = (pad->media_object_number + 1) % 256; + if (GST_BUFFER_DURATION (buf) != GST_CLOCK_TIME_NONE) { + pad->play_duration += GST_BUFFER_DURATION (buf); + } else { + GST_WARNING_OBJECT (asfmux, "Received buffer without duration, it will not " + "be accounted in the total file time"); + } + + asfmux->payloads = g_slist_append (asfmux->payloads, payload); + asfmux->payload_data_size += + GST_BUFFER_SIZE (buf) + ASF_MULTIPLE_PAYLOAD_HEADER_SIZE; + GST_LOG_OBJECT (asfmux, "Payload data size: %" G_GUINT32_FORMAT, + asfmux->payload_data_size); + + while (asfmux->payload_data_size + ASF_PAYLOAD_PARSING_INFO_SIZE >= + asfmux->packet_size) { + GstFlowReturn ret = gst_asf_mux_flush_payloads (asfmux); + if (ret != GST_FLOW_OK) + return ret; + } + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_asf_mux_collected (GstCollectPads * collect, gpointer data) +{ + GstAsfMux *asfmux = GST_ASF_MUX_CAST (data); + GstFlowReturn ret = GST_FLOW_OK; + GstAsfPad *best_pad = NULL; + GstClockTime best_time = GST_CLOCK_TIME_NONE; + GstBuffer *buf = NULL; + GSList *walk; + + if (G_UNLIKELY (asfmux->state == GST_ASF_MUX_STATE_NONE)) { + ret = gst_asf_mux_start_file (asfmux); + if (ret != GST_FLOW_OK) { + GST_WARNING_OBJECT (asfmux, "Failed to send headers"); + return ret; + } else { + asfmux->state = GST_ASF_MUX_STATE_DATA; + } + } + + if (G_UNLIKELY (asfmux->state == GST_ASF_MUX_STATE_EOS)) + return GST_FLOW_UNEXPECTED; + + /* select the earliest buffer */ + walk = asfmux->collect->data; + while (walk) { + GstAsfPad *pad; + GstCollectData *data; + GstClockTime time; + + data = (GstCollectData *) walk->data; + pad = (GstAsfPad *) data; + + walk = g_slist_next (walk); + + buf = gst_collect_pads_peek (collect, data); + if (buf == NULL) { + GST_LOG_OBJECT (asfmux, "Pad %s has no buffers", + GST_PAD_NAME (pad->collect.pad)); + continue; + } + time = GST_BUFFER_TIMESTAMP (buf); + gst_buffer_unref (buf); + + if (best_pad == NULL || !GST_CLOCK_TIME_IS_VALID (time) || + (GST_CLOCK_TIME_IS_VALID (best_time) && time < best_time)) { + best_pad = pad; + best_time = time; + } + } + + if (best_pad != NULL) { + /* we have data */ + GST_LOG_OBJECT (asfmux, "selected pad %s with time %" GST_TIME_FORMAT, + GST_PAD_NAME (best_pad->collect.pad), GST_TIME_ARGS (best_time)); + buf = gst_collect_pads_pop (collect, &best_pad->collect); + ret = gst_asf_mux_process_buffer (asfmux, best_pad, buf); + } else { + /* no data, let's finish it up */ + while (asfmux->payloads) { + ret = gst_asf_mux_flush_payloads (asfmux); + if (ret != GST_FLOW_OK) { + return ret; + } + } + g_assert (asfmux->payloads == NULL); + g_assert (asfmux->payload_data_size == 0); + /* in 'is-live' mode we don't need to push indexes + * or updating headers */ + if (!asfmux->prop_is_live) { + ret = gst_asf_mux_stop_file (asfmux); + } + if (ret == GST_FLOW_OK) { + gst_pad_push_event (asfmux->srcpad, gst_event_new_eos ()); + ret = GST_FLOW_UNEXPECTED; + } + asfmux->state = GST_ASF_MUX_STATE_EOS; + } + + return ret; +} + +static void +gst_asf_mux_pad_reset (GstAsfPad * pad) +{ + pad->stream_number = 0; + pad->media_object_number = 0; + pad->play_duration = (GstClockTime) 0; + pad->bitrate = 0; + if (pad->codec_data) + gst_buffer_unref (pad->codec_data); + pad->codec_data = NULL; + if (pad->taglist) + gst_tag_list_free (pad->taglist); + pad->taglist = NULL; + + if (pad->is_audio) { + GstAsfAudioPad *audiopad = (GstAsfAudioPad *) pad; + audiopad->audioinfo.rate = 0; + audiopad->audioinfo.channels = 0; + audiopad->audioinfo.format = 0; + audiopad->audioinfo.av_bps = 0; + audiopad->audioinfo.blockalign = 0; + audiopad->audioinfo.size = 0; + } else { + GstAsfVideoPad *videopad = (GstAsfVideoPad *) pad; + videopad->vidinfo.size = 0; + videopad->vidinfo.width = 0; + videopad->vidinfo.height = 0; + videopad->vidinfo.planes = 1; + videopad->vidinfo.bit_cnt = 0; + videopad->vidinfo.compression = 0; + videopad->vidinfo.image_size = 0; + videopad->vidinfo.xpels_meter = 0; + videopad->vidinfo.ypels_meter = 0; + videopad->vidinfo.num_colors = 0; + videopad->vidinfo.imp_colors = 0; + + videopad->last_keyframe_packet = 0; + videopad->has_keyframe = FALSE; + videopad->last_keyframe_packet_count = 0; + videopad->max_keyframe_packet_count = 0; + videopad->next_index_time = 0; + videopad->time_interval = DEFAULT_SIMPLE_INDEX_TIME_INTERVAL; + if (videopad->simple_index) { + GSList *walk; + for (walk = videopad->simple_index; walk; walk = g_slist_next (walk)) { + g_free (walk->data); + walk->data = NULL; + } + g_slist_free (videopad->simple_index); + } + videopad->simple_index = NULL; + } +} + +static gboolean +gst_asf_mux_audio_set_caps (GstPad * pad, GstCaps * caps) +{ + GstAsfMux *asfmux; + GstAsfAudioPad *audiopad; + GstStructure *structure; + const gchar *caps_name; + gint channels, rate; + gchar *aux; + const GValue *codec_data; + + asfmux = GST_ASF_MUX (gst_pad_get_parent (pad)); + + audiopad = (GstAsfAudioPad *) gst_pad_get_element_private (pad); + g_assert (audiopad); + + aux = gst_caps_to_string (caps); + GST_DEBUG_OBJECT (asfmux, "%s:%s, caps=%s", GST_DEBUG_PAD_NAME (pad), aux); + g_free (aux); + + structure = gst_caps_get_structure (caps, 0); + caps_name = gst_structure_get_name (structure); + + if (!gst_structure_get_int (structure, "channels", &channels) || + !gst_structure_get_int (structure, "rate", &rate)) + goto refuse_caps; + + audiopad->audioinfo.channels = (guint16) channels; + audiopad->audioinfo.rate = (guint32) rate; + + /* taken from avimux + * codec initialization data, if any + */ + codec_data = gst_structure_get_value (structure, "codec_data"); + if (codec_data) { + audiopad->pad.codec_data = gst_value_get_buffer (codec_data); + gst_buffer_ref (audiopad->pad.codec_data); + } + + if (strcmp (caps_name, "audio/x-wma") == 0) { + gint version; + gint block_align = 0; + gint bitrate = 0; + + if (!gst_structure_get_int (structure, "wmaversion", &version)) { + goto refuse_caps; + } + + if (gst_structure_get_int (structure, "block_align", &block_align)) { + audiopad->audioinfo.blockalign = (guint16) block_align; + } + if (gst_structure_get_int (structure, "bitrate", &bitrate)) { + audiopad->pad.bitrate = (guint32) bitrate; + audiopad->audioinfo.av_bps = bitrate / 8; + } + + if (version == 1) { + audiopad->audioinfo.format = GST_RIFF_WAVE_FORMAT_WMAV1; + } else if (version == 2) { + audiopad->audioinfo.format = GST_RIFF_WAVE_FORMAT_WMAV2; + } else if (version == 3) { + audiopad->audioinfo.format = GST_RIFF_WAVE_FORMAT_WMAV3; + } else { + goto refuse_caps; + } + } else if (strcmp (caps_name, "audio/mpeg") == 0) { + gint version; + gint layer; + + if (!gst_structure_get_int (structure, "mpegversion", &version) || + !gst_structure_get_int (structure, "layer", &layer)) { + goto refuse_caps; + } + if (version != 1 || layer != 3) { + goto refuse_caps; + } + + audiopad->audioinfo.format = GST_RIFF_WAVE_FORMAT_MPEGL3; + } else { + goto refuse_caps; + } + + gst_object_unref (asfmux); + return TRUE; + +refuse_caps: + GST_WARNING_OBJECT (asfmux, "pad %s refused caps %" GST_PTR_FORMAT, + GST_PAD_NAME (pad), caps); + gst_object_unref (asfmux); + return FALSE; +} + +/* TODO Read pixel aspect ratio */ +static gboolean +gst_asf_mux_video_set_caps (GstPad * pad, GstCaps * caps) +{ + GstAsfMux *asfmux; + GstAsfVideoPad *videopad; + GstStructure *structure; + const gchar *caps_name; + gint width, height; + gchar *aux; + const GValue *codec_data; + + asfmux = GST_ASF_MUX (gst_pad_get_parent (pad)); + + videopad = (GstAsfVideoPad *) gst_pad_get_element_private (pad); + g_assert (videopad); + + aux = gst_caps_to_string (caps); + GST_DEBUG_OBJECT (asfmux, "%s:%s, caps=%s", GST_DEBUG_PAD_NAME (pad), aux); + g_free (aux); + + structure = gst_caps_get_structure (caps, 0); + caps_name = gst_structure_get_name (structure); + + if (!gst_structure_get_int (structure, "width", &width) || + !gst_structure_get_int (structure, "height", &height)) + goto refuse_caps; + + videopad->vidinfo.width = (gint32) width; + videopad->vidinfo.height = (gint32) height; + + /* taken from avimux + * codec initialization data, if any + */ + codec_data = gst_structure_get_value (structure, "codec_data"); + if (codec_data) { + videopad->pad.codec_data = gst_value_get_buffer (codec_data); + gst_buffer_ref (videopad->pad.codec_data); + } + + if (strcmp (caps_name, "video/x-wmv") == 0) { + guint32 fourcc; + + videopad->vidinfo.bit_cnt = 24; + + /* in case we have a fourcc, we use it */ + if (gst_structure_get_fourcc (structure, "format", &fourcc)) { + videopad->vidinfo.compression = fourcc; + } else { + gint version; + if (!gst_structure_get_int (structure, "wmvversion", &version)) + goto refuse_caps; + if (version == 2) { + videopad->vidinfo.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '2'); + } else if (version == 1) { + videopad->vidinfo.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '1'); + } else if (version == 3) { + videopad->vidinfo.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '3'); + } else { + goto refuse_caps; + } + } + } else { + goto refuse_caps; + } + + gst_object_unref (asfmux); + return TRUE; + +refuse_caps: + GST_WARNING_OBJECT (asfmux, "pad %s refused caps %" GST_PTR_FORMAT, + GST_PAD_NAME (pad), caps); + gst_object_unref (asfmux); + return FALSE; +} + +static GstPad * +gst_asf_mux_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * req_name) +{ + GstElementClass *klass = GST_ELEMENT_GET_CLASS (element); + GstAsfMux *asfmux = GST_ASF_MUX_CAST (element); + GstPad *newpad; + GstAsfPad *collect_pad; + gboolean is_audio; + guint collect_size = 0; + gchar *name; + + GST_DEBUG_OBJECT (asfmux, "Requested pad: %s", GST_STR_NULL (req_name)); + + if (asfmux->state != GST_ASF_MUX_STATE_NONE) { + GST_WARNING_OBJECT (asfmux, "Not providing request pad after element is at " + "paused/playing state."); + return NULL; + } + + if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) { + name = g_strdup_printf ("audio_%02d", asfmux->stream_number + 1); + GST_DEBUG_OBJECT (asfmux, "Adding new pad %s", name); + newpad = gst_pad_new_from_template (templ, name); + g_free (name); + is_audio = TRUE; + gst_pad_set_setcaps_function (newpad, + GST_DEBUG_FUNCPTR (gst_asf_mux_audio_set_caps)); + } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) { + name = g_strdup_printf ("video_%02d", asfmux->stream_number + 1); + GST_DEBUG_OBJECT (asfmux, "Adding new pad %s", name); + newpad = gst_pad_new_from_template (templ, name); + g_free (name); + is_audio = FALSE; + gst_pad_set_setcaps_function (newpad, + GST_DEBUG_FUNCPTR (gst_asf_mux_video_set_caps)); + } else { + GST_WARNING_OBJECT (asfmux, "This is not our template!"); + return NULL; + } + + /* add pad to collections */ + if (is_audio) { + collect_size = sizeof (GstAsfAudioPad); + } else { + collect_size = sizeof (GstAsfVideoPad); + } + collect_pad = (GstAsfPad *) + gst_collect_pads_add_pad_full (asfmux->collect, newpad, collect_size, + (GstCollectDataDestroyNotify) (gst_asf_mux_pad_reset)); + + /* set up pad */ + collect_pad->is_audio = is_audio; + if (!is_audio) + ((GstAsfVideoPad *) collect_pad)->simple_index = NULL; + collect_pad->taglist = NULL; + gst_asf_mux_pad_reset (collect_pad); + + /* set pad stream number */ + asfmux->stream_number += 1; + collect_pad->stream_number = asfmux->stream_number; + + /* 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. + */ + asfmux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad); + gst_pad_set_event_function (newpad, + GST_DEBUG_FUNCPTR (gst_asf_mux_sink_event)); + + gst_pad_set_active (newpad, TRUE); + gst_element_add_pad (element, newpad); + + return newpad; +} + +static void +gst_asf_mux_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstAsfMux *asfmux; + + asfmux = GST_ASF_MUX (object); + switch (prop_id) { + case PROP_PACKET_SIZE: + g_value_set_uint (value, asfmux->prop_packet_size); + break; + case PROP_PREROLL: + g_value_set_uint64 (value, asfmux->prop_preroll); + break; + case PROP_MERGE_STREAM_TAGS: + g_value_set_boolean (value, asfmux->prop_merge_stream_tags); + break; + case PROP_PADDING: + g_value_set_uint64 (value, asfmux->prop_padding); + break; + case PROP_IS_LIVE: + g_value_set_boolean (value, asfmux->prop_is_live); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_asf_mux_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstAsfMux *asfmux; + + asfmux = GST_ASF_MUX (object); + switch (prop_id) { + case PROP_PACKET_SIZE: + asfmux->prop_packet_size = g_value_get_uint (value); + break; + case PROP_PREROLL: + asfmux->prop_preroll = g_value_get_uint64 (value); + break; + case PROP_MERGE_STREAM_TAGS: + asfmux->prop_merge_stream_tags = g_value_get_boolean (value); + break; + case PROP_PADDING: + asfmux->prop_padding = g_value_get_uint64 (value); + break; + case PROP_IS_LIVE: + asfmux->prop_is_live = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstStateChangeReturn +gst_asf_mux_change_state (GstElement * element, GstStateChange transition) +{ + GstAsfMux *asfmux; + GstStateChangeReturn ret; + + asfmux = GST_ASF_MUX (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + /* TODO - check if it is possible to mux 2 files without going + * through here */ + asfmux->packet_size = asfmux->prop_packet_size; + asfmux->preroll = asfmux->prop_preroll; + asfmux->merge_stream_tags = asfmux->prop_merge_stream_tags; + gst_collect_pads_start (asfmux->collect); + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_collect_pads_stop (asfmux->collect); + asfmux->state = GST_ASF_MUX_STATE_NONE; + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + goto done; + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + +done: + return ret; +} + +gboolean +gst_asf_mux_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "asfmux", + GST_RANK_NONE, GST_TYPE_ASF_MUX); +} diff --git a/gst/asfmux/gstasfmux.h b/gst/asfmux/gstasfmux.h new file mode 100644 index 00000000..2b71fa36 --- /dev/null +++ b/gst/asfmux/gstasfmux.h @@ -0,0 +1,158 @@ +/* ASF muxer plugin for GStreamer + * Copyright (C) 2009 Thiago Santos <thiagoss@embedded.ufcg.edu.br> + * + * 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_ASF_MUX_H__ +#define __GST_ASF_MUX_H__ + + +#include <gst/gst.h> +#include <gst/base/gstcollectpads.h> +#include <gst/riff/riff-media.h> + +#include "gstasfobjects.h" + +G_BEGIN_DECLS +#define GST_TYPE_ASF_MUX \ + (gst_asf_mux_get_type()) +#define GST_ASF_MUX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ASF_MUX,GstAsfMux)) +#define GST_ASF_MUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ASF_MUX,GstAsfMuxClass)) +#define GST_IS_ASF_MUX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ASF_MUX)) +#define GST_IS_ASF_MUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ASF_MUX)) +#define GST_ASF_MUX_CAST(obj) ((GstAsfMux*)(obj)) +typedef struct _GstAsfMux GstAsfMux; +typedef struct _GstAsfMuxClass GstAsfMuxClass; +typedef struct _GstAsfPad GstAsfPad; +typedef struct _GstAsfAudioPad GstAsfAudioPad; +typedef struct _GstAsfVideoPad GstAsfVideoPad; +typedef enum _GstAsfMuxState GstAsfMuxState; + +enum _GstAsfMuxState +{ + GST_ASF_MUX_STATE_NONE, + GST_ASF_MUX_STATE_HEADERS, + GST_ASF_MUX_STATE_DATA, + GST_ASF_MUX_STATE_EOS +}; + +struct _GstAsfPad +{ + GstCollectData collect; + + gboolean is_audio; + guint8 stream_number; + guint8 media_object_number; + guint32 bitrate; + + GstClockTime play_duration; + + GstBuffer *codec_data; + + /* stream only metadata */ + GstTagList *taglist; +}; + +struct _GstAsfAudioPad +{ + GstAsfPad pad; + + gst_riff_strf_auds audioinfo; +}; + +struct _GstAsfVideoPad +{ + GstAsfPad pad; + + gst_riff_strf_vids vidinfo; + + /* Simple Index Entries */ + GSList *simple_index; + gboolean has_keyframe; /* if we have received one at least */ + guint32 last_keyframe_packet; + guint16 last_keyframe_packet_count; + guint16 max_keyframe_packet_count; + GstClockTime next_index_time; + guint64 time_interval; +}; + +struct _GstAsfMux +{ + GstElement element; + + /* output stream state */ + GstAsfMuxState state; + + /* counter to assign stream numbers */ + guint8 stream_number; + + /* counting variables */ + guint64 file_size; + guint64 data_object_size; + guint64 total_data_packets; + + /* + * data object size field position + * needed for updating when finishing the file + */ + guint64 data_object_position; + guint64 file_properties_object_position; + + /* payloads still to be sent in a packet */ + guint32 payload_data_size; + GSList *payloads; + + Guid file_id; + + /* properties */ + guint32 prop_packet_size; + guint64 prop_preroll; + gboolean prop_merge_stream_tags; + guint64 prop_padding; + gboolean prop_is_live; + + /* same as properties, but those are stored here to be + * used without modification while muxing a single file */ + guint32 packet_size; + guint64 preroll; /* milisecs */ + gboolean merge_stream_tags; + + /* pads */ + GstPad *srcpad; + + /* sinkpads, video first */ + GSList *sinkpads; + + GstCollectPads *collect; + GstPadEventFunction collect_event; +}; + +struct _GstAsfMuxClass +{ + GstElementClass parent_class; +}; + +GType gst_asf_mux_get_type (void); +gboolean gst_asf_mux_plugin_init (GstPlugin * plugin); + +G_END_DECLS +#endif /* __GST_ASF_MUX_H__ */ diff --git a/gst/asfmux/gstasfobjects.c b/gst/asfmux/gstasfobjects.c new file mode 100644 index 00000000..c0b63ae0 --- /dev/null +++ b/gst/asfmux/gstasfobjects.c @@ -0,0 +1,813 @@ +/* ASF muxer plugin for GStreamer + * Copyright (C) 2009 Thiago Santos <thiagoss@embedded.ufcg.edu.br> + * + * 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 "gstasfobjects.h" +#include <string.h> + +/* Guids */ +const Guid guids[] = { + /* asf header object */ + {0x75B22630, 0x668E, 0x11CF, G_GUINT64_CONSTANT (0xA6D900AA0062CE6C)}, + /* asf file properties object */ + {0x8CABDCA1, 0xA947, 0x11CF, G_GUINT64_CONSTANT (0x8EE400C00C205365)}, + /* asf stream properties object */ + {0xB7DC0791, 0xA9B7, 0x11CF, G_GUINT64_CONSTANT (0x8EE600C00C205365)}, + /* asf audio media */ + {0xF8699E40, 0x5B4D, 0x11CF, G_GUINT64_CONSTANT (0xA8FD00805F5C442B)}, + /* asf no error correction */ + {0x20FB5700, 0x5B55, 0x11CF, G_GUINT64_CONSTANT (0xA8FD00805F5C442B)}, + /* asf audio spread */ + {0xBFC3CD50, 0x618F, 0x11CF, G_GUINT64_CONSTANT (0x8BB200AA00B4E220)}, + /* asf header extension object */ + {0x5FBF03B5, 0xA92E, 0x11CF, G_GUINT64_CONSTANT (0x8EE300C00C205365)}, + /* asf reserved 1 */ + {0xABD3D211, 0xA9BA, 0x11CF, G_GUINT64_CONSTANT (0x8EE600C00C205365)}, + /* asf data object */ + {0x75B22636, 0x668E, 0x11CF, G_GUINT64_CONSTANT (0xA6D900AA0062CE6C)}, + /* asf extended stream properties object */ + {0x14E6A5CB, 0xC672, 0x4332, G_GUINT64_CONSTANT (0x8399A96952065B5A)}, + /* asf video media */ + {0xBC19EFC0, 0x5B4D, 0x11CF, G_GUINT64_CONSTANT (0xA8FD00805F5C442B)}, + /* asf simple index object */ + {0x33000890, 0xE5B1, 0x11CF, G_GUINT64_CONSTANT (0x89F400A0C90349CB)}, + /* asf content description */ + {0x75B22633, 0x668E, 0x11CF, G_GUINT64_CONSTANT (0xA6D900AA0062CE6C)}, + /* asf extended content description */ + {0xD2D0A440, 0xE307, 0x11D2, G_GUINT64_CONSTANT (0x97F000A0C95EA850)}, + /* asf metadata object */ + {0xC5F8CBEA, 0x5BAF, 0x4877, G_GUINT64_CONSTANT (0x8467AA8C44FA4CCA)}, + /* asf padding object */ + {0x1806D474, 0xCADF, 0x4509, G_GUINT64_CONSTANT (0xA4BA9AABCB96AAE8)} +}; + +/** + * gst_asf_generate_file_id: + * + * Generates a random GUID + * + * Returns: The generated GUID + */ +Guid +gst_asf_generate_file_id () +{ + guint32 aux; + Guid guid; + guid.v1 = g_random_int (); + aux = g_random_int (); + guid.v2 = (guint16) (aux & 0x0000FFFF); + guid.v3 = (guint16) (aux >> 16); + guid.v4 = (((guint64) g_random_int ()) << 32) | (guint64) g_random_int (); + + return guid; +} + +/** + * gst_byte_reader_get_asf_var_size_field: + * @reader: A #GstByteReader + * @field_type: an asf field type + * @var: pointer to store the result + * + * Reads the proper data from the #GstByteReader according to the + * asf field type and stores it in var + * + * Returns: True on success, false otherwise + */ +gboolean +gst_byte_reader_get_asf_var_size_field (GstByteReader * reader, + guint8 field_type, guint32 * var) +{ + guint8 aux8 = 0; + guint16 aux16 = 0; + guint32 aux32 = 0; + gboolean ret; + + switch (field_type) { + case ASF_FIELD_TYPE_DWORD: + ret = gst_byte_reader_get_uint32_le (reader, &aux32); + *var = aux32; + break; + case ASF_FIELD_TYPE_WORD: + ret = gst_byte_reader_get_uint16_le (reader, &aux16); + *var = aux16; + break; + case ASF_FIELD_TYPE_BYTE: + ret = gst_byte_reader_get_uint8 (reader, &aux8); + *var = aux8; + break; + case ASF_FIELD_TYPE_NONE: + ret = TRUE; + *var = 0; + break; + default: + return FALSE; + } + return ret; +} + +/** + * gst_asf_read_var_size_field: + * @data: pointer to the data to be read + * @field_type: the asf field type pointed by data + * + * Reads and returns the value read from the data, according to the + * field type given + * + * Returns: The value read + */ +guint32 +gst_asf_read_var_size_field (guint8 * data, guint8 field_type) +{ + switch (field_type) { + case ASF_FIELD_TYPE_DWORD: + return GST_READ_UINT32_LE (data); + case ASF_FIELD_TYPE_WORD: + return GST_READ_UINT16_LE (data); + case ASF_FIELD_TYPE_BYTE: + return data[0]; + default: + return 0; + } +} + +/** + * gst_asf_get_var_size_field_len: + * @field_type: the asf field type + * + * Returns: the size in bytes of a variable of field_type type + */ +guint +gst_asf_get_var_size_field_len (guint8 field_type) +{ + switch (field_type) { + case ASF_FIELD_TYPE_DWORD: + return 4; + case ASF_FIELD_TYPE_WORD: + return 2; + case ASF_FIELD_TYPE_BYTE: + return 1; + default: + return 0; + } +} + +/** + * gst_asf_file_info_new: + * Creates a new #GstAsfFileInfo + * Returns: the created struct + */ +GstAsfFileInfo * +gst_asf_file_info_new () +{ + return g_new0 (GstAsfFileInfo, 1); +} + +/** + * gst_asf_file_info_reset: + * @info: the #GstAsfFileInfo to be reset + * resets the data of a #GstFileInfo + */ +void +gst_asf_file_info_reset (GstAsfFileInfo * info) +{ + info->packet_size = 0; + info->packets_count = 0; + info->broadcast = FALSE; +} + +/** + * gst_asf_file_info_free: + * @info: the #GstAsfFileInfo to be freed + * + * Releases memory associated with this #GstAsfFileInfo + */ +void +gst_asf_file_info_free (GstAsfFileInfo * info) +{ + g_free (info); +} + +/** + * gst_asf_payload_get_size: + * @payload: the payload to get the size from + * + * Returns: the size of an asf payload of the data represented by this + * #AsfPayload + */ +guint32 +gst_asf_payload_get_size (AsfPayload * payload) +{ + return ASF_MULTIPLE_PAYLOAD_HEADER_SIZE + GST_BUFFER_SIZE (payload->data); +} + +/** + * gst_asf_payload_free: + * @payload: the #AsfPayload to be freed + * + * Releases teh memory associated with this payload + */ +void +gst_asf_payload_free (AsfPayload * payload) +{ + gst_buffer_unref (payload->data); + g_free (payload); +} + +/** + * gst_asf_get_current_time: + * + * Gets system current time in ASF time unit + * (100-nanoseconds since Jan, 1st 1601) + * + * Returns: + */ +guint64 +gst_asf_get_current_time () +{ + GTimeVal timeval; + guint64 secs; + guint64 usecs; + + g_get_current_time (&timeval); + + secs = (guint64) timeval.tv_sec; + usecs = (guint64) timeval.tv_usec; + return secs * G_GUINT64_CONSTANT (10000000) + usecs * 10 + + G_GUINT64_CONSTANT (116444628000000000); +} + +/** + * gst_asf_match_guid: + * @data: pointer to the guid to be tested + * @guid: guid to match against data + * + * Checks if the guid pointed by data is the same + * as the guid parameter + * + * Returns: True if they are the same, false otherwise + */ +gboolean +gst_asf_match_guid (const guint8 * data, const Guid * guid) +{ + Guid g; + g.v1 = GST_READ_UINT32_LE (data); + g.v2 = GST_READ_UINT16_LE (data + 4); + g.v3 = GST_READ_UINT16_LE (data + 6); + g.v4 = GST_READ_UINT64_BE (data + 8); + + return g.v1 == guid->v1 && + g.v2 == guid->v2 && g.v3 == guid->v3 && g.v4 == guid->v4; +} + +/** + * gst_asf_put_i32: + * @buf: the memory to write data to + * @data: the value to be writen + * + * Writes a 32 bit signed integer to memory + */ +void +gst_asf_put_i32 (guint8 * buf, gint32 data) +{ + *(gint32 *) buf = data; +} + +/** + * gst_asf_put_time: + * @buf: pointer to the buffer to write the value to + * @time: value to be writen + * + * Writes an asf time value to the buffer + */ +void +gst_asf_put_time (guint8 * buf, guint64 time) +{ + GST_WRITE_UINT64_LE (buf, time); +} + +/** + * gst_asf_put_guid: + * @buf: the buffer to write the guid to + * @guid: the guid to be writen + * + * Writes a GUID to the buffer + */ +void +gst_asf_put_guid (guint8 * buf, Guid guid) +{ + guint32 *aux32 = (guint32 *) buf; + guint16 *aux16 = (guint16 *) & (buf[4]); + guint64 *aux64 = (guint64 *) & (buf[8]); + *aux32 = GUINT32_TO_LE (guid.v1); + *aux16 = GUINT16_TO_LE (guid.v2); + aux16 = (guint16 *) & (buf[6]); + *aux16 = GUINT16_TO_LE (guid.v3); + *aux64 = GUINT64_TO_BE (guid.v4); +} + +/** + * gst_asf_put_payload: + * @buf: memory to write the payload to + * @payload: #AsfPayload to be writen + * + * Writes the asf payload to the buffer. The #AsfPayload + * packet count is incremented. + */ +void +gst_asf_put_payload (guint8 * buf, AsfPayload * payload) +{ + GST_WRITE_UINT8 (buf, payload->stream_number); + GST_WRITE_UINT8 (buf + 1, payload->media_obj_num); + GST_WRITE_UINT32_LE (buf + 2, payload->offset_in_media_obj); + GST_WRITE_UINT8 (buf + 6, payload->replicated_data_length); + GST_WRITE_UINT32_LE (buf + 7, payload->media_object_size); + GST_WRITE_UINT32_LE (buf + 11, payload->presentation_time); + GST_WRITE_UINT16_LE (buf + 15, (guint16) GST_BUFFER_SIZE (payload->data)); + memcpy (buf + 17, GST_BUFFER_DATA (payload->data), + GST_BUFFER_SIZE (payload->data)); + + payload->packet_count++; +} + +/** + * gst_asf_put_subpayload: + * @buf: buffer to write the payload to + * @payload: the payload to be writen + * @size: maximum size in bytes to write + * + * Serializes part of a payload to a buffer. + * The maximum size is checked against the payload length, + * the minimum of this size and the payload length is writen + * to the buffer and the writen size is returned. + * + * It also updates the values of the payload to match the remaining + * data. + * In case there is not enough space to write the headers, nothing is done. + * + * Returns: The writen size in bytes. + */ +guint16 +gst_asf_put_subpayload (guint8 * buf, AsfPayload * payload, guint16 size) +{ + guint16 payload_size; + GstBuffer *newbuf; + if (size <= ASF_MULTIPLE_PAYLOAD_HEADER_SIZE) { + return 0; /* do nothing if there is not enough space */ + } + GST_WRITE_UINT8 (buf, payload->stream_number); + GST_WRITE_UINT8 (buf + 1, payload->media_obj_num); + GST_WRITE_UINT32_LE (buf + 2, payload->offset_in_media_obj); + GST_WRITE_UINT8 (buf + 6, payload->replicated_data_length); + GST_WRITE_UINT32_LE (buf + 7, payload->media_object_size); + GST_WRITE_UINT32_LE (buf + 11, payload->presentation_time); + size -= ASF_MULTIPLE_PAYLOAD_HEADER_SIZE; + payload_size = size < GST_BUFFER_SIZE (payload->data) ? + size : GST_BUFFER_SIZE (payload->data); + GST_WRITE_UINT16_LE (buf + 15, payload_size); + memcpy (buf + 17, GST_BUFFER_DATA (payload->data), payload_size); + + /* updates the payload to the remaining data */ + payload->offset_in_media_obj += payload_size; + newbuf = gst_buffer_create_sub (payload->data, payload_size, + GST_BUFFER_SIZE (payload->data) - payload_size); + gst_buffer_copy_metadata (payload->data, newbuf, GST_BUFFER_COPY_FLAGS | + GST_BUFFER_COPY_CAPS); + GST_BUFFER_TIMESTAMP (newbuf) = GST_BUFFER_TIMESTAMP (payload->data); + gst_buffer_unref (payload->data); + payload->data = newbuf; + + payload->packet_count++; + + return payload_size; +} + +/** + * gst_asf_match_and_peek_obj_size: + * @data: data to be peeked at + * @guid: pointer to a guid + * + * Compares the first bytes of data against the guid parameter and + * if they match gets the object size (that are right after the guid in + * asf objects). + * + * In case the guids don't match, 0 is returned. + * If the guid is NULL the match is assumed to be true. + * + * Returns: The size of the object in case the guid matches, 0 otherwise + */ +guint64 +gst_asf_match_and_peek_obj_size (const guint8 * data, const Guid * guid) +{ + g_assert (data); + if (guid && !gst_asf_match_guid (data, guid)) { + /* this is not the expected object */ + return 0; + } + /* return the object size */ + return GST_READ_UINT64_LE (data + ASF_GUID_SIZE); +} + +/** + * gst_asf_parse_mult_payload: + * @reader: a #GstByteReader ready to read the multiple payload data + * @has_keyframe: pointer to return the result + * + * Parses a multiple payload section of an asf data packet + * to see if any of the paylaods has a a keyframe + * + * Notice that the #GstByteReader might not be positioned after + * this section on this function return. Because this section + * is the last one in an asf packet and the remaining data + * is probably uninteresting to the application. + * + * Returns: true on success, false if some error occurrs + */ +static gboolean +gst_asf_parse_mult_payload (GstByteReader * reader, gboolean * has_keyframe) +{ + guint payloads; + guint8 payload_len_type; + guint8 rep_data_len; + guint32 payload_len; + guint8 stream_num; + guint8 aux; + guint i; + + if (!gst_byte_reader_get_uint8 (reader, &aux)) + return FALSE; + + payloads = (aux & 0x3F); + payload_len_type = (aux & 0xC0) >> 6; + + *has_keyframe = FALSE; + for (i = 0; i < payloads; i++) { + GST_LOG ("Parsing payload %u/%u", i + 1, payloads); + if (!gst_byte_reader_get_uint8 (reader, &stream_num)) + goto error; + if ((stream_num & 0x80) != 0) { + GST_LOG ("Keyframe found, stoping parse of payloads"); + *has_keyframe = TRUE; + return TRUE; + } + /* skip to replicated data length */ + if (!gst_byte_reader_skip (reader, 5)) + goto error; + if (!gst_byte_reader_get_uint8 (reader, &rep_data_len)) + goto error; + if (!gst_byte_reader_skip (reader, rep_data_len)) + goto error; + if (!gst_byte_reader_get_asf_var_size_field (reader, payload_len_type, + &payload_len)) + goto error; + if (!gst_byte_reader_skip (reader, payload_len)) + goto error; + } + + /* we do not skip the rest of the payload bytes as + this is the last data to be parsed on the buffer */ + return TRUE; +error: + GST_WARNING ("Error while parsing payloads"); + return FALSE; +} + +/** + * gst_asf_parse_single_payload: + * @reader: a #GstByteReader ready to read the multiple payload data + * @has_keyframe: pointer to return the result + * + * Parses a single payload section of an asf data packet + * to see if any of the paylaods has a a keyframe + * + * Notice that the #GstByteReader might not be positioned after + * this section on this function return. Because this section + * is the last one in an asf packet and the remaining data + * is probably uninteresting to the application. + * + * Returns: true on success, false if some error occurrs + */ +static gboolean +gst_asf_parse_single_payload (GstByteReader * reader, gboolean * has_keyframe) +{ + guint8 stream_num; + if (!gst_byte_reader_get_uint8 (reader, &stream_num)) + return GST_FLOW_ERROR; + *has_keyframe = (stream_num & 0x80) != 0; + + /* we do not skip the rest of the payload bytes as + this is the last data to be parsed on the buffer */ + return TRUE; +} + +gboolean +gst_asf_parse_packet (GstBuffer * buffer, GstAsfPacketInfo * packet, + gboolean trust_delta_flag) +{ + GstByteReader *reader; + gboolean ret = TRUE; + guint8 first; + guint8 err_length = 0; /* length of the error fields */ + guint8 aux; + guint8 packet_len_type; + guint8 padding_len_type; + guint8 seq_len_type; + guint8 rep_data_len_type; + guint8 mo_number_len_type; + guint8 mo_offset_type; + gboolean mult_payloads; + guint32 packet_len; + guint32 padd_len; + guint32 send_time; + guint16 duration; + gboolean has_keyframe; + + reader = gst_byte_reader_new_from_buffer (buffer); + + GST_LOG ("Starting packet parsing, size: %u", GST_BUFFER_SIZE (buffer)); + if (!gst_byte_reader_get_uint8 (reader, &first)) + goto error; + + if (first & 0x80) { /* error correction present */ + guint8 err_cor_len; + err_length += 1; + GST_DEBUG ("Packet contains error correction"); + if (first & 0x60) { + GST_ERROR ("Error correction data length should be " + "set to 0 and is reserved for future use."); + return FALSE; + } + err_cor_len = (first & 0x0F); + err_length += err_cor_len; + GST_DEBUG ("Error correction data length: %d", (gint) err_cor_len); + if (!gst_byte_reader_skip (reader, err_cor_len)) + goto error; + + /* put payload parsing info first byte in aux var */ + if (!gst_byte_reader_get_uint8 (reader, &aux)) + goto error; + } else { + aux = first; + } + mult_payloads = (aux & 0x1) != 0; + + packet_len_type = (aux >> 5) & 0x3; + padding_len_type = (aux >> 3) & 0x3; + seq_len_type = (aux >> 1) & 0x3; + GST_LOG ("Field sizes: packet length type: %u " + ", padding length type: %u, sequence length type: %u", + gst_asf_get_var_size_field_len (packet_len_type), + gst_asf_get_var_size_field_len (padding_len_type), + gst_asf_get_var_size_field_len (seq_len_type)); + + if (mult_payloads) { + GST_DEBUG ("Packet contains multiple payloads"); + } + + if (!gst_byte_reader_get_uint8 (reader, &aux)) + goto error; + rep_data_len_type = aux & 0x3; + mo_offset_type = (aux >> 2) & 0x3; + mo_number_len_type = (aux >> 4) & 0x3; + + /* gets the fields lengths */ + GST_LOG ("Getting packet and padding length"); + if (!gst_byte_reader_get_asf_var_size_field (reader, + packet_len_type, &packet_len)) + goto error; + if (!gst_byte_reader_skip (reader, + gst_asf_get_var_size_field_len (seq_len_type))) + goto error; + if (!gst_byte_reader_get_asf_var_size_field (reader, + padding_len_type, &padd_len)) + goto error; + + if (packet_len_type != ASF_FIELD_TYPE_NONE && + packet_len != GST_BUFFER_SIZE (buffer)) { + GST_WARNING ("ASF packets should be aligned with buffers"); + ret = FALSE; + goto end; + } + + GST_LOG ("Getting send time and duration"); + if (!gst_byte_reader_get_uint32_le (reader, &send_time)) + goto error; + if (!gst_byte_reader_get_uint16_le (reader, &duration)) + goto error; + + has_keyframe = FALSE; + GST_LOG ("Checking for keyframes"); + if (trust_delta_flag) { + has_keyframe = GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT); + } else { + if (mult_payloads) { + ret = gst_asf_parse_mult_payload (reader, &has_keyframe); + } else { + ret = gst_asf_parse_single_payload (reader, &has_keyframe); + } + } + + if (!ret) { + GST_WARNING ("Failed to parse payloads"); + goto end; + } + GST_DEBUG ("Received packet of length %" G_GUINT32_FORMAT + ", padding %" G_GUINT32_FORMAT ", send time %" G_GUINT32_FORMAT + ", duration %" G_GUINT16_FORMAT " and %s keyframe(s)", + packet_len, padd_len, send_time, duration, + (has_keyframe) ? "with" : "without"); + + packet->packet_size = packet_len; + packet->padding = padd_len; + packet->send_time = send_time; + packet->duration = duration; + packet->has_keyframe = has_keyframe; + packet->multiple_payloads = mult_payloads ? TRUE : FALSE; + packet->padd_field_type = padding_len_type; + packet->packet_field_type = packet_len_type; + packet->seq_field_type = seq_len_type; + packet->err_cor_len = err_length; + + gst_byte_reader_free (reader); + return ret; + +error: + ret = FALSE; + GST_WARNING ("Error while parsing data packet"); +end: + gst_byte_reader_free (reader); + return ret; +} + +static gboolean +gst_asf_parse_file_properties_obj (GstByteReader * reader, + GstAsfFileInfo * asfinfo) +{ + guint32 min_ps; + guint32 max_ps; + guint64 packets; + guint32 flags; + GST_DEBUG ("ASF: Parsing file properties object"); + + /* skip until data packets count */ + if (!gst_byte_reader_skip (reader, 32)) + return FALSE; + if (!gst_byte_reader_get_uint64_le (reader, &packets)) + return FALSE; + asfinfo->packets_count = packets; + GST_DEBUG ("ASF: packets count %" G_GUINT64_FORMAT, packets); + + /* skip until flags */ + if (!gst_byte_reader_skip (reader, 24)) + return FALSE; + + if (!gst_byte_reader_get_uint32_le (reader, &flags)) + return GST_FLOW_ERROR; + asfinfo->broadcast = (flags & 0x1) == 1; + GST_DEBUG ("ASF: broadcast flag: %s", asfinfo->broadcast ? "true" : "false"); + if (!gst_byte_reader_get_uint32_le (reader, &min_ps)) + return GST_FLOW_ERROR; + if (!gst_byte_reader_get_uint32_le (reader, &max_ps)) + return GST_FLOW_ERROR; + + if (min_ps != max_ps) { + GST_WARNING ("Mininum and maximum packet size differ " + "%" G_GUINT32_FORMAT " and %" G_GUINT32_FORMAT ", " + "ASF spec states they should be the same", min_ps, max_ps); + return FALSE; + } + + GST_DEBUG ("ASF: Packet size: %" G_GUINT32_FORMAT, min_ps); + asfinfo->packet_size = min_ps; + if (!gst_byte_reader_skip (reader, 4)) + return FALSE; + + return TRUE; +} + +gboolean +gst_asf_parse_headers (GstBuffer * buffer, GstAsfFileInfo * file_info) +{ + gboolean ret = TRUE; + guint32 header_objects; + guint32 i; + GstByteReader *reader; + guint64 object_size; + + object_size = gst_asf_match_and_peek_obj_size (GST_BUFFER_DATA (buffer), + &(guids[ASF_HEADER_OBJECT_INDEX])); + if (object_size == 0) { + GST_WARNING ("ASF: Cannot parse, header guid not found at the beginning " + " of data"); + return FALSE; + } + + reader = gst_byte_reader_new_from_buffer (buffer); + + if (!gst_byte_reader_skip (reader, ASF_GUID_OBJSIZE_SIZE)) + goto error; + if (!gst_byte_reader_get_uint32_le (reader, &header_objects)) + goto error; + GST_DEBUG ("ASF: Header has %" G_GUINT32_FORMAT " child" + " objects", header_objects); + /* skip reserved bytes */ + if (!gst_byte_reader_skip (reader, 2)) + goto error; + + /* iterate through childs of header object */ + for (i = 0; i < header_objects; i++) { + const guint8 *guid = NULL; + guint64 obj_size; + if (!gst_byte_reader_get_data (reader, ASF_GUID_SIZE, &guid)) + goto error; + if (!gst_byte_reader_get_uint64_le (reader, &obj_size)) + goto error; + + if (gst_asf_match_guid (guid, &guids[ASF_FILE_PROPERTIES_OBJECT_INDEX])) { + ret = gst_asf_parse_file_properties_obj (reader, file_info); + } else { + /* we don't know/care about this object */ + if (!gst_byte_reader_skip (reader, obj_size - ASF_GUID_OBJSIZE_SIZE)) + goto error; + } + + if (!ret) + goto end; + } + goto end; + +error: + ret = FALSE; + GST_WARNING ("ASF: Error while parsing headers"); +end: + gst_byte_reader_free (reader); + return ret; +} + +#define MAP_GST_TO_ASF_TAG(tag, gst, asf) \ + if (strcmp (tag, gst) == 0) \ + return asf + +/** + * gst_asf_get_asf_tag: + * @gsttag: a gstreamer tag + * + * Maps gstreamer tags to asf tags + * + * Returns: The tag corresponding name in asf files or NULL if it is not mapped + */ +const gchar * +gst_asf_get_asf_tag (const gchar * gsttag) +{ + g_return_val_if_fail (gsttag != NULL, NULL); + + MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_TITLE, ASF_TAG_TITLE); + MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_TITLE_SORTNAME, ASF_TAG_TITLE_SORTNAME); + MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_ARTIST, ASF_TAG_ARTIST); + MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_ARTIST_SORTNAME, ASF_TAG_ARTIST_SORTNAME); + MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_ALBUM, ASF_TAG_ALBUM_TITLE); + MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_ALBUM_SORTNAME, + ASF_TAG_ALBUM_TITLE_SORTNAME); + MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_GENRE, ASF_TAG_GENRE); + MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_COPYRIGHT, ASF_TAG_COPYRIGHT); + MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_COMPOSER, ASF_TAG_COMPOSER); + MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_COMMENT, ASF_TAG_COMMENT); + MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_TRACK_NUMBER, ASF_TAG_TRACK_NUMBER); + + return NULL; +} + +guint +gst_asf_get_tag_field_type (GValue * value) +{ + if (G_VALUE_HOLDS_STRING (value)) + return ASF_TAG_TYPE_UNICODE_STR; + if (G_VALUE_HOLDS_UINT (value)) + return ASF_TAG_TYPE_DWORD; + + return -1; +} + +gboolean +gst_asf_tag_present_in_content_description (const gchar * tag) +{ + return strcmp (tag, GST_TAG_TITLE) == 0 || + strcmp (tag, GST_TAG_ARTIST) == 0 || + strcmp (tag, GST_TAG_COPYRIGHT) == 0 || + strcmp (tag, GST_TAG_DESCRIPTION) == 0; + /* FIXME we have no tag for rating */ +} diff --git a/gst/asfmux/gstasfobjects.h b/gst/asfmux/gstasfobjects.h new file mode 100644 index 00000000..302c42c6 --- /dev/null +++ b/gst/asfmux/gstasfobjects.h @@ -0,0 +1,196 @@ +/* ASF muxer plugin for GStreamer + * Copyright (C) 2009 Thiago Santos <thiagoss@embedded.ufcg.edu.br> + * + * 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_ASF_OBJECTS_H__ +#define __GST_ASF_OBJECTS_H__ + +#include <glib.h> +#include <gst/gst.h> +#include <gst/base/gstbytereader.h> +#include <gst/base/gstcollectpads.h> + +#define ASF_PAYLOAD_IS_KEYFRAME(pay) ((pay->stream_number & 0x80) != 0) +#define ASF_MILI_TO_100NANO(v) (v * 10000) +#define ASF_GUID_SIZE 16 +#define ASF_GUID_OBJSIZE_SIZE 24 + +typedef struct _Guid +{ + guint32 v1; + guint16 v2; + guint16 v3; + guint64 v4; +} Guid; + +typedef struct _GstAsfFileInfo +{ + guint64 packets_count; + guint32 packet_size; + gboolean broadcast; +} GstAsfFileInfo; + +typedef struct _GstAsfPacketInfo +{ + guint8 err_cor_len; + gboolean multiple_payloads; + guint8 padd_field_type; + guint8 packet_field_type; + guint8 seq_field_type; + + guint32 packet_size; + guint32 padding; + guint32 send_time; + guint16 duration; + gboolean has_keyframe; +} GstAsfPacketInfo; + +typedef struct _SimpleIndexEntry +{ + guint32 packet_number; + guint16 packet_count; +} SimpleIndexEntry; + +typedef struct _AsfPayload +{ + guint8 stream_number; + guint8 media_obj_num; + guint32 offset_in_media_obj; + guint8 replicated_data_length; + guint32 media_object_size; + guint32 presentation_time; + GstBuffer *data; + + GstCollectData *pad; + + /* simple index info */ + gboolean has_packet_info; + guint32 packet_number; + guint16 packet_count; +} AsfPayload; + +Guid gst_asf_generate_file_id (); + +gboolean gst_byte_reader_get_asf_var_size_field (GstByteReader * reader, + guint8 field_type, guint32 * var); +guint32 gst_asf_read_var_size_field (guint8 * data, guint8 field_type); +guint gst_asf_get_var_size_field_len (guint8 field_type); + +GstAsfFileInfo *gst_asf_file_info_new (); +void gst_asf_file_info_reset (GstAsfFileInfo * info); +void gst_asf_file_info_free (GstAsfFileInfo * info); + +guint32 gst_asf_payload_get_size (AsfPayload * payload); +void gst_asf_payload_free (AsfPayload * payload); + +guint64 gst_asf_get_current_time (); + +gboolean gst_asf_match_guid (const guint8 * data, const Guid * g); + +void gst_asf_put_i32 (guint8 * buf, gint32 data); +void gst_asf_put_time (guint8 * buf, guint64 time); +void gst_asf_put_guid (guint8 * buf, Guid guid); +void gst_asf_put_payload (guint8 * buf, AsfPayload * payload); +guint16 gst_asf_put_subpayload (guint8 * buf, AsfPayload * payload, + guint16 size); + +gboolean gst_asf_parse_packet (GstBuffer * buffer, GstAsfPacketInfo * packet, + gboolean trust_delta_flag); +guint64 gst_asf_match_and_peek_obj_size (const guint8 * data, + const Guid * guid); +gboolean gst_asf_parse_headers (GstBuffer * buffer, GstAsfFileInfo * file_info); + +/* ASF tags + * found at http://msdn.microsoft.com/en-us/library/dd562330(VS.85).aspx + */ + +#define ASF_TAG_TITLE "Title\0" +#define ASF_TAG_TITLE_SORTNAME "TitleSortOrder\0" + +/* FIXME asf has no artist tag other than AlbumArtist, but it has Author + * What to use here? */ +#define ASF_TAG_ARTIST "WM/AlbumArtist\0" +#define ASF_TAG_ARTIST_SORTNAME "AlbumArtistSortOrder\0" + +#define ASF_TAG_ALBUM_TITLE "WM/AlbumTitle\0" +#define ASF_TAG_ALBUM_TITLE_SORTNAME "AlbumTitleSortOrder\0" + +#define ASF_TAG_GENRE "WM/Genre\0" +#define ASF_TAG_COMMENT "Comment\0" +#define ASF_TAG_TRACK_NUMBER "WM/TrackNumber\0" +#define ASF_TAG_COPYRIGHT "Copyright\0" +#define ASF_TAG_COMPOSER "WM/Composer\0" + +const gchar *gst_asf_get_asf_tag (const gchar * gsttag); +guint gst_asf_get_tag_field_type (GValue * value); +gboolean gst_asf_tag_present_in_content_description (const gchar * tag); + +/* ASF Objects Sizes */ +#define ASF_HEADER_OBJECT_SIZE 30 +#define ASF_FILE_PROPERTIES_OBJECT_SIZE 104 +#define ASF_STREAM_PROPERTIES_OBJECT_SIZE 78 +#define ASF_HEADER_EXTENSION_OBJECT_SIZE 46 +#define ASF_AUDIO_SPECIFIC_DATA_SIZE 18 +#define ASF_VIDEO_SPECIFIC_DATA_SIZE 51 +#define ASF_DATA_OBJECT_SIZE 50 +#define ASF_PAYLOAD_PARSING_INFO_SIZE 16 +#define ASF_SINGLE_PAYLOAD_HEADER_SIZE 15 +#define ASF_MULTIPLE_PAYLOAD_HEADER_SIZE 17 +#define ASF_EXTENDED_STREAM_PROPERTIES_OBJECT_SIZE 88 +#define ASF_CONTENT_DESCRIPTION_OBJECT_SIZE 34 +#define ASF_EXT_CONTENT_DESCRIPTION_OBJECT_SIZE 26 +#define ASF_SIMPLE_INDEX_OBJECT_SIZE 56 +#define ASF_SIMPLE_INDEX_ENTRY_SIZE 6 +#define ASF_METADATA_OBJECT_SIZE 26 +#define ASF_PADDING_OBJECT_SIZE 24 + +/* Field types for data object payload description */ +#define ASF_FIELD_TYPE_NONE 0 +#define ASF_FIELD_TYPE_BYTE 1 +#define ASF_FIELD_TYPE_WORD 2 +#define ASF_FIELD_TYPE_DWORD 3 + +/* tag types */ +#define ASF_TAG_TYPE_UNICODE_STR 0 +#define ASF_TAG_TYPE_BYTES 1 +#define ASF_TAG_TYPE_BOOL 2 +#define ASF_TAG_TYPE_DWORD 3 +#define ASF_TAG_TYPE_QWORD 4 +#define ASF_TAG_TYPE_WORD 5 + +/* GUID objects */ + +#define ASF_HEADER_OBJECT_INDEX 0 +#define ASF_FILE_PROPERTIES_OBJECT_INDEX 1 +#define ASF_STREAM_PROPERTIES_OBJECT_INDEX 2 +#define ASF_AUDIO_MEDIA_INDEX 3 +#define ASF_NO_ERROR_CORRECTION_INDEX 4 +#define ASF_AUDIO_SPREAD_INDEX 5 +#define ASF_HEADER_EXTENSION_OBJECT_INDEX 6 +#define ASF_RESERVED_1_INDEX 7 +#define ASF_DATA_OBJECT_INDEX 8 +#define ASF_EXTENDED_STREAM_PROPERTIES_OBJECT_INDEX 9 +#define ASF_VIDEO_MEDIA_INDEX 10 +#define ASF_SIMPLE_INDEX_OBJECT_INDEX 11 +#define ASF_CONTENT_DESCRIPTION_INDEX 12 +#define ASF_EXT_CONTENT_DESCRIPTION_INDEX 13 +#define ASF_METADATA_OBJECT_INDEX 14 +#define ASF_PADDING_OBJECT_INDEX 15 + +extern const Guid guids[]; + +#endif diff --git a/gst/asfmux/gstasfparse.c b/gst/asfmux/gstasfparse.c new file mode 100644 index 00000000..17db7293 --- /dev/null +++ b/gst/asfmux/gstasfparse.c @@ -0,0 +1,623 @@ +/* ASF parser plugin for GStreamer + * Copyright (C) 2009 Thiago Santos <thiagoss@embedded.ufcg.edu.br> + * + * 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 "gstasfparse.h" + +/* FIXME add this include + * #include <gst/gst-i18n-plugin.h> */ + +GST_DEBUG_CATEGORY_STATIC (asfparse_debug); +#define GST_CAT_DEFAULT asfparse_debug + +enum +{ + PROP_0, +}; + +static const GstElementDetails gst_asf_parse_details = +GST_ELEMENT_DETAILS ("ASF parser", + "Parser", + "Parses ASF", + "Thiago Santos <thiagoss@embedded.ufcg.edu.br>"); + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-ms-asf, parsed = (boolean) true") + ); + +static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-ms-asf, parsed = (boolean) false") + ); + +static void gst_asf_parse_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_asf_parse_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); +static GstStateChangeReturn gst_asf_parse_change_state (GstElement * element, + GstStateChange transition); +static void gst_asf_parse_loop (GstPad * pad); + +GST_BOILERPLATE (GstAsfParse, gst_asf_parse, GstElement, GST_TYPE_ELEMENT); + +static void +gst_asf_parse_reset (GstAsfParse * asfparse) +{ + gst_adapter_clear (asfparse->adapter); + gst_asf_file_info_reset (asfparse->asfinfo); + asfparse->parse_state = ASF_PARSING_HEADERS; + asfparse->headers_size = 0; + asfparse->data_size = 0; + asfparse->parsed_packets = 0; + asfparse->offset = 0; +} + +static gboolean +gst_asf_parse_sink_activate (GstPad * pad) +{ + if (gst_pad_check_pull_range (pad)) { + return gst_pad_activate_pull (pad, TRUE); + } else { + return gst_pad_activate_push (pad, TRUE); + } +} + +static gboolean +gst_asf_parse_sink_activate_pull (GstPad * pad, gboolean active) +{ + if (active) { + return gst_pad_start_task (pad, (GstTaskFunction) gst_asf_parse_loop, pad); + } else { + return gst_pad_stop_task (pad); + } +} + +static GstFlowReturn +gst_asf_parse_push (GstAsfParse * asfparse, GstBuffer * buf) +{ + gst_buffer_set_caps (buf, asfparse->outcaps); + return gst_pad_push (asfparse->srcpad, buf); +} + +static GstFlowReturn +gst_asf_parse_parse_data_object (GstAsfParse * asfparse, GstBuffer * buffer) +{ + GstByteReader *reader; + GstFlowReturn ret = GST_FLOW_OK; + guint64 packet_count; + + GST_DEBUG_OBJECT (asfparse, "Parsing data object"); + + reader = gst_byte_reader_new_from_buffer (buffer); + /* skip to packet count */ + if (!gst_byte_reader_skip (reader, 40)) + goto error; + if (!gst_byte_reader_get_uint64_le (reader, &packet_count)) + goto error; + + if (asfparse->asfinfo->packets_count != packet_count) { + GST_WARNING_OBJECT (asfparse, "File properties object and data object have " + "different packets count, %" G_GUINT64_FORMAT " %" G_GUINT64_FORMAT, + asfparse->asfinfo->packets_count, packet_count); + } else { + GST_DEBUG_OBJECT (asfparse, "Total packets: %" G_GUINT64_FORMAT, + packet_count); + } + + gst_byte_reader_free (reader); + return gst_asf_parse_push (asfparse, buffer); + +error: + ret = GST_FLOW_ERROR; + GST_ERROR_OBJECT (asfparse, "Error while parsing data object headers"); + gst_byte_reader_free (reader); + return ret; +} + +static GstFlowReturn +gst_asf_parse_parse_packet (GstAsfParse * asfparse, GstBuffer * buffer) +{ + GstAsfPacketInfo *packetinfo = asfparse->packetinfo; + + if (!gst_asf_parse_packet (buffer, packetinfo, FALSE)) + goto error; + + GST_DEBUG_OBJECT (asfparse, "Received packet of length %" G_GUINT32_FORMAT + ", padding %" G_GUINT32_FORMAT ", send time %" G_GUINT32_FORMAT + ", duration %" G_GUINT16_FORMAT " and %s keyframe(s)", + packetinfo->packet_size, packetinfo->padding, + packetinfo->send_time, packetinfo->duration, + (packetinfo->has_keyframe) ? "with" : "without"); + + /* set gstbuffer fields */ + if (!packetinfo->has_keyframe) { + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT); + } + GST_BUFFER_TIMESTAMP (buffer) = ((GstClockTime) packetinfo->send_time) + * GST_MSECOND; + GST_BUFFER_DURATION (buffer) = ((GstClockTime) packetinfo->duration) + * GST_MSECOND; + + return gst_asf_parse_push (asfparse, buffer); + +error: + GST_ERROR_OBJECT (asfparse, "Error while parsing data packet"); + return GST_FLOW_ERROR; +} + +static GstFlowReturn +gst_asf_parse_pull_headers (GstAsfParse * asfparse) +{ + GstBuffer *guid_and_size = NULL; + GstBuffer *headers = NULL; + guint64 size; + GstFlowReturn ret; + + if ((ret = gst_pad_pull_range (asfparse->sinkpad, asfparse->offset, + ASF_GUID_OBJSIZE_SIZE, &guid_and_size)) != GST_FLOW_OK) { + GST_ERROR_OBJECT (asfparse, "Failed to pull data from headers"); + goto leave; + } + asfparse->offset += ASF_GUID_OBJSIZE_SIZE; + size = gst_asf_match_and_peek_obj_size (GST_BUFFER_DATA (guid_and_size), + &(guids[ASF_HEADER_OBJECT_INDEX])); + + if (size == 0) { + GST_ERROR_OBJECT (asfparse, "ASF starting identifier missing"); + goto leave; + } + + if ((ret = gst_pad_pull_range (asfparse->sinkpad, asfparse->offset, + size - ASF_GUID_OBJSIZE_SIZE, &headers)) != GST_FLOW_OK) { + GST_ERROR_OBJECT (asfparse, "Failed to pull data from headers"); + goto leave; + } + headers = gst_buffer_join (guid_and_size, headers); + guid_and_size = NULL; + asfparse->offset += size - ASF_GUID_OBJSIZE_SIZE; + if (!gst_asf_parse_headers (headers, asfparse->asfinfo)) { + goto leave; + } + return gst_asf_parse_push (asfparse, headers); + +leave: + if (headers) + gst_buffer_unref (headers); + if (guid_and_size) + gst_buffer_unref (guid_and_size); + return ret; +} + +static GstFlowReturn +gst_asf_parse_pull_data_header (GstAsfParse * asfparse) +{ + GstBuffer *buf = NULL; + GstFlowReturn ret; + + if ((ret = gst_pad_pull_range (asfparse->sinkpad, asfparse->offset, + ASF_DATA_OBJECT_SIZE, &buf)) != GST_FLOW_OK) { + GST_ERROR_OBJECT (asfparse, "Failed to pull data header"); + return ret; + } + asfparse->offset += ASF_DATA_OBJECT_SIZE; + asfparse->data_size = gst_asf_match_and_peek_obj_size (GST_BUFFER_DATA (buf), + &(guids[ASF_DATA_OBJECT_INDEX])); + if (asfparse->data_size == 0) { + GST_ERROR_OBJECT (asfparse, "Unexpected object, was expecting data object"); + gst_buffer_unref (buf); + return GST_FLOW_ERROR; + } + + return gst_asf_parse_parse_data_object (asfparse, buf); +} + +static GstFlowReturn +gst_asf_parse_pull_packets (GstAsfParse * asfparse) +{ + GstFlowReturn ret; + while (asfparse->asfinfo->broadcast || + asfparse->parsed_packets < asfparse->asfinfo->packets_count) { + GstBuffer *packet = NULL; + + GST_DEBUG_OBJECT (asfparse, "Parsing packet %" G_GUINT64_FORMAT, + asfparse->parsed_packets); + + /* get the packet */ + ret = gst_pad_pull_range (asfparse->sinkpad, asfparse->offset, + asfparse->asfinfo->packet_size, &packet); + if (ret != GST_FLOW_OK) + return ret; + asfparse->parsed_packets++; + asfparse->offset += asfparse->asfinfo->packet_size; + + /* parse the packet */ + ret = gst_asf_parse_parse_packet (asfparse, packet); + if (ret != GST_FLOW_OK) + return ret; + } + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_asf_parse_pull_indexes (GstAsfParse * asfparse) +{ + GstBuffer *guid_and_size = NULL; + GstBuffer *buf = NULL; + guint64 obj_size; + GstFlowReturn ret = GST_FLOW_OK; + while (1) { + ret = gst_pad_pull_range (asfparse->sinkpad, asfparse->offset, + ASF_GUID_OBJSIZE_SIZE, &guid_and_size); + if (ret != GST_FLOW_OK) + break; + /* we can peek at the object size */ + obj_size = + gst_asf_match_and_peek_obj_size (GST_BUFFER_DATA (guid_and_size), NULL); + if (obj_size == 0) { + GST_ERROR_OBJECT (asfparse, "Incomplete object found"); + gst_buffer_unref (guid_and_size); + ret = GST_FLOW_ERROR; + break; + } + asfparse->offset += ASF_GUID_OBJSIZE_SIZE; + + /* pull the rest of the object */ + ret = gst_pad_pull_range (asfparse->sinkpad, asfparse->offset, obj_size, + &buf); + if (ret != GST_FLOW_OK) { + gst_buffer_unref (guid_and_size); + break; + } + asfparse->offset += obj_size - ASF_GUID_OBJSIZE_SIZE; + + buf = gst_buffer_join (guid_and_size, buf); + ret = gst_asf_parse_push (asfparse, buf); + if (ret != GST_FLOW_OK) + break; + } + return ret; +} + +static void +gst_asf_parse_loop (GstPad * pad) +{ + GstFlowReturn ret = GST_FLOW_OK; + GstAsfParse *asfparse = GST_ASF_PARSE_CAST (GST_OBJECT_PARENT (pad)); + + GST_LOG_OBJECT (asfparse, "Processing data in loop function"); + switch (asfparse->parse_state) { + case ASF_PARSING_HEADERS: + GST_INFO_OBJECT (asfparse, "Starting to parse headers"); + ret = gst_asf_parse_pull_headers (asfparse); + if (ret != GST_FLOW_OK) + goto pause; + asfparse->parse_state = ASF_PARSING_DATA; + + case ASF_PARSING_DATA: + GST_INFO_OBJECT (asfparse, "Parsing data object headers"); + ret = gst_asf_parse_pull_data_header (asfparse); + if (ret != GST_FLOW_OK) + goto pause; + asfparse->parse_state = ASF_PARSING_PACKETS; + + case ASF_PARSING_PACKETS: + GST_INFO_OBJECT (asfparse, "Starting packet parsing"); + GST_INFO_OBJECT (asfparse, "Broadcast mode %s", + asfparse->asfinfo->broadcast ? "on" : "off"); + ret = gst_asf_parse_pull_packets (asfparse); + if (ret != GST_FLOW_OK) + goto pause; + + /* test if all packets have been processed */ + if (!asfparse->asfinfo->broadcast && + asfparse->parsed_packets == asfparse->asfinfo->packets_count) { + GST_INFO_OBJECT (asfparse, + "All %" G_GUINT64_FORMAT " packets processed", + asfparse->parsed_packets); + asfparse->parse_state = ASF_PARSING_INDEXES; + } + + case ASF_PARSING_INDEXES: + /* we currently don't care about indexes, so just push them forward */ + GST_INFO_OBJECT (asfparse, "Starting indexes parsing"); + ret = gst_asf_parse_pull_indexes (asfparse); + if (ret != GST_FLOW_OK) + goto pause; + default: + break; + } + +pause: + { + const gchar *reason = gst_flow_get_name (ret); + + GST_INFO_OBJECT (asfparse, "Pausing sinkpad task"); + gst_pad_pause_task (pad); + + if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) { + if (ret == GST_FLOW_UNEXPECTED) { + } else { + GST_ELEMENT_ERROR (asfparse, STREAM, FAILED, + (NULL), ("streaming task paused, reason %s (%d)", reason, ret)); + } + gst_pad_push_event (asfparse->srcpad, gst_event_new_eos ()); + } + } +} + +static GstFlowReturn +gst_asf_parse_chain (GstPad * pad, GstBuffer * buffer) +{ + GstAsfParse *asfparse; + GstFlowReturn ret = GST_FLOW_OK; + + asfparse = GST_ASF_PARSE (GST_PAD_PARENT (pad)); + gst_adapter_push (asfparse->adapter, buffer); + + switch (asfparse->parse_state) { + case ASF_PARSING_HEADERS: + if (asfparse->headers_size == 0 && + gst_adapter_available (asfparse->adapter) >= ASF_GUID_OBJSIZE_SIZE) { + + /* we can peek at the object size */ + asfparse->headers_size = + gst_asf_match_and_peek_obj_size (gst_adapter_peek + (asfparse->adapter, ASF_GUID_OBJSIZE_SIZE), + &(guids[ASF_HEADER_OBJECT_INDEX])); + + if (asfparse->headers_size == 0) { + /* something is wrong, this probably ain't an ASF stream */ + GST_ERROR_OBJECT (asfparse, "ASF starting identifier missing"); + ret = GST_FLOW_ERROR; + goto end; + } + } + if (gst_adapter_available (asfparse->adapter) >= asfparse->headers_size) { + GstBuffer *headers = gst_adapter_take_buffer (asfparse->adapter, + asfparse->headers_size); + if (gst_asf_parse_headers (headers, asfparse->asfinfo)) { + ret = gst_asf_parse_push (asfparse, headers); + asfparse->parse_state = ASF_PARSING_DATA; + } else { + ret = GST_FLOW_ERROR; + GST_ERROR_OBJECT (asfparse, "Failed to parse headers"); + } + } + break; + case ASF_PARSING_DATA: + if (asfparse->data_size == 0 && + gst_adapter_available (asfparse->adapter) >= ASF_GUID_OBJSIZE_SIZE) { + + /* we can peek at the object size */ + asfparse->data_size = + gst_asf_match_and_peek_obj_size (gst_adapter_peek + (asfparse->adapter, ASF_GUID_OBJSIZE_SIZE), + &(guids[ASF_DATA_OBJECT_INDEX])); + + if (asfparse->data_size == 0) { + /* something is wrong */ + GST_ERROR_OBJECT (asfparse, "Unexpected object after headers, was " + "expecting a data object"); + ret = GST_FLOW_ERROR; + goto end; + } + } + /* if we have received the full data object headers */ + if (gst_adapter_available (asfparse->adapter) >= ASF_DATA_OBJECT_SIZE) { + ret = gst_asf_parse_parse_data_object (asfparse, + gst_adapter_take_buffer (asfparse->adapter, ASF_DATA_OBJECT_SIZE)); + if (ret != GST_FLOW_OK) { + goto end; + } + asfparse->parse_state = ASF_PARSING_PACKETS; + } + break; + case ASF_PARSING_PACKETS: + g_assert (asfparse->asfinfo->packet_size); + while ((asfparse->asfinfo->broadcast || + asfparse->parsed_packets < asfparse->asfinfo->packets_count) && + gst_adapter_available (asfparse->adapter) >= + asfparse->asfinfo->packet_size) { + GstBuffer *packet = gst_adapter_take_buffer (asfparse->adapter, + asfparse->asfinfo->packet_size); + asfparse->parsed_packets++; + ret = gst_asf_parse_parse_packet (asfparse, packet); + if (ret != GST_FLOW_OK) + goto end; + } + if (!asfparse->asfinfo->broadcast && + asfparse->parsed_packets >= asfparse->asfinfo->packets_count) { + GST_INFO_OBJECT (asfparse, "Finished parsing packets"); + asfparse->parse_state = ASF_PARSING_INDEXES; + } + break; + case ASF_PARSING_INDEXES: + /* we currently don't care about any of those objects */ + if (gst_adapter_available (asfparse->adapter) >= ASF_GUID_OBJSIZE_SIZE) { + guint64 obj_size; + /* we can peek at the object size */ + obj_size = gst_asf_match_and_peek_obj_size (gst_adapter_peek + (asfparse->adapter, ASF_GUID_OBJSIZE_SIZE), NULL); + if (gst_adapter_available (asfparse->adapter) >= obj_size) { + GST_DEBUG_OBJECT (asfparse, "Skiping object"); + ret = gst_asf_parse_push (asfparse, + gst_adapter_take_buffer (asfparse->adapter, obj_size)); + if (ret != GST_FLOW_OK) { + goto end; + } + } + } + break; + default: + break; + } + +end: + return ret; +} + +static void +gst_asf_parse_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 (element_class, &gst_asf_parse_details); + + GST_DEBUG_CATEGORY_INIT (asfparse_debug, "asfparse", 0, + "Parser for ASF streams"); +} + +static void +gst_asf_parse_finalize (GObject * object) +{ + GstAsfParse *asfparse = GST_ASF_PARSE (object); + gst_adapter_clear (asfparse->adapter); + g_object_unref (G_OBJECT (asfparse->adapter)); + gst_caps_unref (asfparse->outcaps); + gst_asf_file_info_free (asfparse->asfinfo); + g_free (asfparse->packetinfo); + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_asf_parse_class_init (GstAsfParseClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->get_property = gst_asf_parse_get_property; + gobject_class->set_property = gst_asf_parse_set_property; + gobject_class->finalize = gst_asf_parse_finalize; + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_asf_parse_change_state); +} + +static void +gst_asf_parse_init (GstAsfParse * asfparse, GstAsfParseClass * klass) +{ + asfparse->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); + gst_pad_set_chain_function (asfparse->sinkpad, gst_asf_parse_chain); + gst_pad_set_activate_function (asfparse->sinkpad, + gst_asf_parse_sink_activate); + gst_pad_set_activatepull_function (asfparse->sinkpad, + gst_asf_parse_sink_activate_pull); + gst_element_add_pad (GST_ELEMENT (asfparse), asfparse->sinkpad); + + asfparse->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); + gst_pad_use_fixed_caps (asfparse->srcpad); + gst_element_add_pad (GST_ELEMENT (asfparse), asfparse->srcpad); + + asfparse->adapter = gst_adapter_new (); + asfparse->outcaps = gst_caps_new_simple ("video/x-ms-asf", NULL); + asfparse->asfinfo = gst_asf_file_info_new (); + asfparse->packetinfo = g_new0 (GstAsfPacketInfo, 1); + gst_asf_parse_reset (asfparse); +} + +static void +gst_asf_parse_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstAsfParse *asfparse; + + asfparse = GST_ASF_PARSE (object); + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_asf_parse_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstAsfParse *asfparse; + + asfparse = GST_ASF_PARSE (object); + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstStateChangeReturn +gst_asf_parse_change_state (GstElement * element, GstStateChange transition) +{ + GstAsfParse *asfparse; + GstStateChangeReturn ret; + + asfparse = GST_ASF_PARSE (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_asf_parse_reset (asfparse); + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + goto done; + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + +done: + return ret; +} + +gboolean +gst_asf_parse_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "asfparse", + GST_RANK_NONE, GST_TYPE_ASF_PARSE); +} diff --git a/gst/asfmux/gstasfparse.h b/gst/asfmux/gstasfparse.h new file mode 100644 index 00000000..760e81f4 --- /dev/null +++ b/gst/asfmux/gstasfparse.h @@ -0,0 +1,88 @@ +/* ASF Parser plugin for GStreamer + * Copyright (C) 2009 Thiago Santos <thiagoss@embedded.ufcg.edu.br> + * + * 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_ASF_PARSE_H__ +#define __GST_ASF_PARSE_H__ + + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include <gst/base/gstbytereader.h> + +#include "gstasfobjects.h" + +G_BEGIN_DECLS + +#define GST_TYPE_ASF_PARSE \ + (gst_asf_parse_get_type()) +#define GST_ASF_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ASF_PARSE,GstAsfParse)) +#define GST_ASF_PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ASF_PARSE,GstAsfParseClass)) +#define GST_IS_ASF_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ASF_PARSE)) +#define GST_IS_ASF_PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ASF_PARSE)) + +#define GST_ASF_PARSE_CAST(obj) ((GstAsfParse*)(obj)) + +enum GstAsfParsingState { + ASF_PARSING_HEADERS, + ASF_PARSING_DATA, + ASF_PARSING_PACKETS, + ASF_PARSING_INDEXES +}; + +typedef struct _GstAsfParse GstAsfParse; +typedef struct _GstAsfParseClass GstAsfParseClass; + +struct _GstAsfParse { + GstElement element; + + enum GstAsfParsingState parse_state; + + GstAdapter *adapter; + + GstPad *srcpad; + GstPad *sinkpad; + GstCaps *outcaps; + + guint64 parsed_packets; + + guint64 offset; /* used in pull mode */ + + /* parsed info */ + GstAsfFileInfo *asfinfo; + GstAsfPacketInfo *packetinfo; /* we keep it here to avoid allocs */ + guint64 headers_size; + guint64 data_size; +}; + +struct _GstAsfParseClass { + GstElementClass parent_class; +}; + +GType gst_asf_parse_get_type(void); +gboolean gst_asf_parse_plugin_init (GstPlugin * plugin); + +G_END_DECLS + + +#endif /* __GST_ASF_PARSE_H__ */ diff --git a/gst/asfmux/gstrtpasfpay.c b/gst/asfmux/gstrtpasfpay.c new file mode 100644 index 00000000..ffcfd85a --- /dev/null +++ b/gst/asfmux/gstrtpasfpay.c @@ -0,0 +1,469 @@ +/* ASF RTP Payloader plugin for GStreamer + * Copyright (C) 2009 Thiago Santos <thiagoss@embedded.ufcg.edu.br> + * + * 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. + */ + +/* FIXME + * - this element doesn't follow (max/min) time properties, + * is it possible to do it with a container format? + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gst/rtp/gstrtpbuffer.h> +#include <string.h> + +#include "gstrtpasfpay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpasfpay_debug); +#define GST_CAT_DEFAULT (rtpasfpay_debug) + +/* elementfactory information */ +static const GstElementDetails gst_rtp_asf_pay_details = +GST_ELEMENT_DETAILS ("RTP ASF payloader", + "Codec/Payloader/Network", + "Payload-encodes ASF into RTP packets (MS_RTSP)", + "Thiago Santos <thiagoss@embedded.ufcg.edu.br>"); + +static GstStaticPadTemplate gst_rtp_asf_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-ms-asf, " "parsed = (boolean) true") + ); + +static GstStaticPadTemplate gst_rtp_asf_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) {\"audio\", \"video\", \"application\"}, " + "clock-rate = (int) 1000, " "encoding-name = (string) \"X-ASF-PF\"") + ); + +static GstFlowReturn +gst_rtp_asf_pay_handle_buffer (GstBaseRTPPayload * rtppay, GstBuffer * buffer); +static gboolean +gst_rtp_asf_pay_set_caps (GstBaseRTPPayload * rtppay, GstCaps * caps); + +GST_BOILERPLATE (GstRtpAsfPay, gst_rtp_asf_pay, GstBaseRTPPayload, + GST_TYPE_BASE_RTP_PAYLOAD); + +static void +gst_rtp_asf_pay_init (GstRtpAsfPay * rtpasfpay, GstRtpAsfPayClass * klass) +{ + rtpasfpay->first_ts = 0; + rtpasfpay->config = NULL; + rtpasfpay->packets_count = 0; + rtpasfpay->state = ASF_NOT_STARTED; + rtpasfpay->headers = NULL; + rtpasfpay->current = NULL; +} + +static void +gst_rtp_asf_pay_finalize (GObject * object) +{ + GstRtpAsfPay *rtpasfpay; + rtpasfpay = GST_RTP_ASF_PAY (object); + g_free (rtpasfpay->config); + if (rtpasfpay->headers) + gst_buffer_unref (rtpasfpay->headers); + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_rtp_asf_pay_base_init (gpointer klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rtp_asf_pay_sink_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rtp_asf_pay_src_template)); + gst_element_class_set_details (element_class, &gst_rtp_asf_pay_details); +} + +static void +gst_rtp_asf_pay_class_init (GstRtpAsfPayClass * klass) +{ + GObjectClass *gobject_class; + GstBaseRTPPayloadClass *gstbasertppayload_class; + + gobject_class = (GObjectClass *) klass; + gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass; + + gobject_class->finalize = gst_rtp_asf_pay_finalize; + + gstbasertppayload_class->handle_buffer = gst_rtp_asf_pay_handle_buffer; + gstbasertppayload_class->set_caps = gst_rtp_asf_pay_set_caps; + + GST_DEBUG_CATEGORY_INIT (rtpasfpay_debug, "rtpasfpay", 0, + "ASF RTP Payloader"); +} + +static gboolean +gst_rtp_asf_pay_set_caps (GstBaseRTPPayload * rtppay, GstCaps * caps) +{ + /* FIXME change application for the actual content */ + gst_basertppayload_set_options (rtppay, "application", TRUE, "X-ASF-PF", + 1000); + return TRUE; +} + +static GstFlowReturn +gst_rtp_asf_pay_handle_packet (GstRtpAsfPay * rtpasfpay, GstBuffer * buffer) +{ + GstBaseRTPPayload *rtppay; + GstAsfPacketInfo *packetinfo; + guint8 flags; + guint8 *data; + guint32 packet_util_size; + guint32 packet_offset; + guint32 size_left; + GstFlowReturn ret = GST_FLOW_OK; + + rtppay = GST_BASE_RTP_PAYLOAD (rtpasfpay); + packetinfo = &rtpasfpay->packetinfo; + + if (!gst_asf_parse_packet (buffer, packetinfo, TRUE)) { + GST_ERROR_OBJECT (rtpasfpay, "Error while parsing asf packet"); + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; + } + + if (packetinfo->packet_size == 0) + packetinfo->packet_size = rtpasfpay->asfinfo.packet_size; + + GST_LOG_OBJECT (rtpasfpay, "Packet size: %" G_GUINT32_FORMAT + ", padding: %" G_GUINT32_FORMAT, packetinfo->packet_size, + packetinfo->padding); + + /* update padding field to 0 */ + if (packetinfo->padding > 0) { + GstAsfPacketInfo info; + /* find padding field offset */ + guint offset = packetinfo->err_cor_len + 2 + + gst_asf_get_var_size_field_len (packetinfo->packet_field_type) + + gst_asf_get_var_size_field_len (packetinfo->seq_field_type); + buffer = gst_buffer_make_writable (buffer); + switch (packetinfo->padd_field_type) { + case ASF_FIELD_TYPE_DWORD: + GST_WRITE_UINT32_LE (&(GST_BUFFER_DATA (buffer)[offset]), 0); + break; + case ASF_FIELD_TYPE_WORD: + GST_WRITE_UINT16_LE (&(GST_BUFFER_DATA (buffer)[offset]), 0); + break; + case ASF_FIELD_TYPE_BYTE: + GST_BUFFER_DATA (buffer)[offset] = 0; + break; + case ASF_FIELD_TYPE_NONE: + default: + break; + } + gst_asf_parse_packet (buffer, &info, FALSE); + } + + packet_util_size = packetinfo->packet_size - packetinfo->padding; + packet_offset = 0; + while (packet_util_size > 0) { + /* Even if we don't fill completely an output buffer we + * push it when we add an fragment. Because it seems that + * it is not possible to determine where a asf packet + * fragment ends inside a rtp packet payload. + * This flag tells us to push the packet. + */ + gboolean force_push = FALSE; + + /* we have no output buffer pending, create one */ + if (rtpasfpay->current == NULL) { + GST_LOG_OBJECT (rtpasfpay, "Creating new output buffer"); + rtpasfpay->current = + gst_rtp_buffer_new_allocate_len (GST_BASE_RTP_PAYLOAD_MTU (rtpasfpay), + 0, 0); + rtpasfpay->cur_off = gst_rtp_buffer_get_header_len (rtpasfpay->current); + rtpasfpay->has_ts = FALSE; + rtpasfpay->marker = FALSE; + } + data = GST_BUFFER_DATA (rtpasfpay->current) + rtpasfpay->cur_off; + size_left = GST_BUFFER_SIZE (rtpasfpay->current) - rtpasfpay->cur_off; + + GST_DEBUG_OBJECT (rtpasfpay, "Input buffer bytes consumed: %" + G_GUINT32_FORMAT "/%" G_GUINT32_FORMAT, packet_offset, + GST_BUFFER_SIZE (buffer)); + + GST_DEBUG_OBJECT (rtpasfpay, "Output rtpbuffer status"); + GST_DEBUG_OBJECT (rtpasfpay, "Current offset: %" G_GUINT32_FORMAT, + rtpasfpay->cur_off); + GST_DEBUG_OBJECT (rtpasfpay, "Size left: %" G_GUINT32_FORMAT, size_left); + GST_DEBUG_OBJECT (rtpasfpay, "Has ts: %s", + rtpasfpay->has_ts ? "yes" : "no"); + if (rtpasfpay->has_ts) { + GST_DEBUG_OBJECT (rtpasfpay, "Ts: %" G_GUINT32_FORMAT, rtpasfpay->ts); + } + + flags = 0; + if (packetinfo->has_keyframe) { + flags = flags | 0x80; + } + flags = flags | 0x20; /* Relative timestamp is present */ + + if (!rtpasfpay->has_ts) { + /* this is the first asf packet, its send time is the + * rtp packet timestamp */ + rtpasfpay->has_ts = TRUE; + rtpasfpay->ts = packetinfo->send_time; + } + + if (GST_BUFFER_SIZE (rtpasfpay->current) - rtpasfpay->cur_off >= + packet_util_size + 8) { + /* enough space for the rest of the packet */ + if (packet_offset == 0) { + flags = flags | 0x40; + GST_WRITE_UINT24_BE (data + 1, packet_util_size); + } else { + GST_WRITE_UINT24_BE (data + 1, packet_offset); + force_push = TRUE; + } + data[0] = flags; + GST_WRITE_UINT32_BE (data + 4, + (gint32) (packetinfo->send_time) - (gint32) rtpasfpay->ts); + memcpy (data + 8, GST_BUFFER_DATA (buffer) + packet_offset, + packet_util_size); + + /* updating status variables */ + rtpasfpay->cur_off += 8 + packet_util_size; + size_left -= packet_util_size + 8; + packet_offset += packet_util_size; + packet_util_size = 0; + rtpasfpay->marker = TRUE; + } else { + /* fragment packet */ + data[0] = flags; + GST_WRITE_UINT24_BE (data + 1, packet_offset); + GST_WRITE_UINT32_BE (data + 4, + (gint32) (packetinfo->send_time) - (gint32) rtpasfpay->ts); + memcpy (data + 8, GST_BUFFER_DATA (buffer) + packet_offset, + size_left - 8); + + /* updating status variables */ + rtpasfpay->cur_off += size_left; + packet_offset += size_left - 8; + packet_util_size -= size_left - 8; + size_left = 0; + force_push = TRUE; + } + + /* there is not enough room for any more buffers */ + if (force_push || size_left <= 8) { + + if (size_left != 0) { + /* trim remaining bytes not used */ + GstBuffer *aux = gst_buffer_create_sub (rtpasfpay->current, 0, + GST_BUFFER_SIZE (rtpasfpay->current) - size_left); + gst_buffer_unref (rtpasfpay->current); + rtpasfpay->current = aux; + } + gst_rtp_buffer_set_ssrc (rtpasfpay->current, rtppay->current_ssrc); + gst_rtp_buffer_set_marker (rtpasfpay->current, rtpasfpay->marker); + gst_rtp_buffer_set_payload_type (rtpasfpay->current, + GST_BASE_RTP_PAYLOAD_PT (rtppay)); + gst_rtp_buffer_set_seq (rtpasfpay->current, rtppay->seqnum + 1); + gst_rtp_buffer_set_timestamp (rtpasfpay->current, packetinfo->send_time); + + GST_BUFFER_TIMESTAMP (rtpasfpay->current) = GST_BUFFER_TIMESTAMP (buffer); + + gst_buffer_set_caps (rtpasfpay->current, + GST_PAD_CAPS (GST_BASE_RTP_PAYLOAD_SRCPAD (rtppay))); + + rtppay->seqnum++; + rtppay->timestamp = packetinfo->send_time; + + GST_DEBUG_OBJECT (rtpasfpay, "Pushing rtp buffer"); + ret = + gst_pad_push (GST_BASE_RTP_PAYLOAD_SRCPAD (rtppay), + rtpasfpay->current); + rtpasfpay->current = NULL; + if (ret != GST_FLOW_OK) { + gst_buffer_unref (buffer); + return ret; + } + } + } + gst_buffer_unref (buffer); + return ret; +} + +static GstFlowReturn +gst_rtp_asf_pay_parse_headers (GstRtpAsfPay * rtpasfpay) +{ + GstFlowReturn ret = GST_FLOW_OK; + gchar *maxps; + g_return_val_if_fail (rtpasfpay->headers, GST_FLOW_ERROR); + + if (!gst_asf_parse_headers (rtpasfpay->headers, &rtpasfpay->asfinfo)) + goto error; + + GST_DEBUG_OBJECT (rtpasfpay, "Packets number: %" G_GUINT64_FORMAT, + rtpasfpay->asfinfo.packets_count); + GST_DEBUG_OBJECT (rtpasfpay, "Packets size: %" G_GUINT32_FORMAT, + rtpasfpay->asfinfo.packet_size); + GST_DEBUG_OBJECT (rtpasfpay, "Broadcast mode: %s", + rtpasfpay->asfinfo.broadcast ? "true" : "false"); + + /* get the config for caps */ + g_free (rtpasfpay->config); + rtpasfpay->config = g_base64_encode (GST_BUFFER_DATA (rtpasfpay->headers), + GST_BUFFER_SIZE (rtpasfpay->headers)); + GST_DEBUG_OBJECT (rtpasfpay, "Serialized headers to base64 string %s", + rtpasfpay->config); + + g_assert (rtpasfpay->config != NULL); + GST_DEBUG_OBJECT (rtpasfpay, "Setting optional caps values: maxps=%" + G_GUINT32_FORMAT " and config=%s", rtpasfpay->asfinfo.packet_size, + rtpasfpay->config); + maxps = + g_strdup_printf ("%" G_GUINT32_FORMAT, rtpasfpay->asfinfo.packet_size); + gst_basertppayload_set_outcaps (GST_BASE_RTP_PAYLOAD (rtpasfpay), "maxps", + G_TYPE_STRING, maxps, "config", G_TYPE_STRING, rtpasfpay->config, NULL); + g_free (maxps); + + return GST_FLOW_OK; + +error: + ret = GST_FLOW_ERROR; + GST_ERROR_OBJECT (rtpasfpay, "Error while parsing headers"); + return GST_FLOW_ERROR; +} + +static GstFlowReturn +gst_rtp_asf_pay_handle_buffer (GstBaseRTPPayload * rtppay, GstBuffer * buffer) +{ + GstRtpAsfPay *rtpasfpay = GST_RTP_ASF_PAY_CAST (rtppay); + + if (G_UNLIKELY (rtpasfpay->state == ASF_END)) { + GST_LOG_OBJECT (rtpasfpay, + "Dropping buffer as we already pushed all packets"); + gst_buffer_unref (buffer); + return GST_FLOW_UNEXPECTED; /* we already finished our job */ + } + + /* receive headers + * we only accept if they are in a single buffer */ + if (G_UNLIKELY (rtpasfpay->state == ASF_NOT_STARTED)) { + guint64 header_size; + + if (GST_BUFFER_SIZE (buffer) < 24) { /* guid+object size size */ + GST_ERROR_OBJECT (rtpasfpay, + "Buffer too small, smaller than a Guid and object size"); + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; + } + + header_size = gst_asf_match_and_peek_obj_size (GST_BUFFER_DATA (buffer), + &(guids[ASF_HEADER_OBJECT_INDEX])); + if (header_size > 0) { + GST_DEBUG_OBJECT (rtpasfpay, "ASF header guid received, size %" + G_GUINT64_FORMAT, header_size); + + if (GST_BUFFER_SIZE (buffer) < header_size) { + GST_ERROR_OBJECT (rtpasfpay, "Headers should be contained in a single" + " buffer"); + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; + } else { + rtpasfpay->state = ASF_DATA_OBJECT; + + /* clear previous headers, if any */ + if (rtpasfpay->headers) { + gst_buffer_unref (rtpasfpay->headers); + } + + GST_DEBUG_OBJECT (rtpasfpay, "Storing headers"); + if (GST_BUFFER_SIZE (buffer) == header_size) { + rtpasfpay->headers = buffer; + return GST_FLOW_OK; + } else { + /* headers are a subbuffer of thie buffer */ + GstBuffer *aux = gst_buffer_create_sub (buffer, header_size, + GST_BUFFER_SIZE (buffer) - header_size); + rtpasfpay->headers = gst_buffer_create_sub (buffer, 0, header_size); + gst_buffer_replace (&buffer, aux); + } + } + } else { + GST_ERROR_OBJECT (rtpasfpay, "Missing ASF header start"); + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; + } + } + + if (G_UNLIKELY (rtpasfpay->state == ASF_DATA_OBJECT)) { + if (GST_BUFFER_SIZE (buffer) != ASF_DATA_OBJECT_SIZE) { + GST_ERROR_OBJECT (rtpasfpay, "Received buffer of different size of " + "the data object header"); + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; + } + + if (gst_asf_match_guid (GST_BUFFER_DATA (buffer), + &(guids[ASF_DATA_OBJECT_INDEX]))) { + GST_DEBUG_OBJECT (rtpasfpay, "Received data object header"); + rtpasfpay->headers = gst_buffer_join (rtpasfpay->headers, buffer); + rtpasfpay->state = ASF_PACKETS; + + return gst_rtp_asf_pay_parse_headers (rtpasfpay); + } else { + GST_ERROR_OBJECT (rtpasfpay, "Unexpected object received (was expecting " + "data object)"); + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; + } + } + + if (G_LIKELY (rtpasfpay->state == ASF_PACKETS)) { + /* in broadcast mode we can't trust the packets count information + * from the headers + * We assume that if this is on broadcast mode it is a live stream + * and we are going to keep receiving packets indefinitely + */ + if (rtpasfpay->asfinfo.broadcast || + rtpasfpay->packets_count < rtpasfpay->asfinfo.packets_count) { + GST_DEBUG_OBJECT (rtpasfpay, "Received packet %" + G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, + rtpasfpay->packets_count, rtpasfpay->asfinfo.packets_count); + rtpasfpay->packets_count++; + return gst_rtp_asf_pay_handle_packet (rtpasfpay, buffer); + } else { + GST_INFO_OBJECT (rtpasfpay, "Packets ended"); + rtpasfpay->state = ASF_END; + gst_buffer_unref (buffer); + return GST_FLOW_UNEXPECTED; + } + } + + gst_buffer_unref (buffer); + return GST_FLOW_OK; +} + +gboolean +gst_rtp_asf_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpasfpay", + GST_RANK_NONE, GST_TYPE_RTP_ASF_PAY); +} diff --git a/gst/asfmux/gstrtpasfpay.h b/gst/asfmux/gstrtpasfpay.h new file mode 100644 index 00000000..8c7505a5 --- /dev/null +++ b/gst/asfmux/gstrtpasfpay.h @@ -0,0 +1,87 @@ +/* ASF RTP Payloader plugin for GStreamer + * Copyright (C) 2009 Thiago Santos <thiagoss@embedded.ufcg.edu.br> + * + * 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_RTP_ASF_PAY_H__ +#define __GST_RTP_ASF_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstbasertppayload.h> +#include <gst/rtp/gstrtpbuffer.h> +#include <gst/base/gstadapter.h> + +#include "gstasfobjects.h" + +G_BEGIN_DECLS +#define GST_TYPE_RTP_ASF_PAY \ + (gst_rtp_asf_pay_get_type()) +#define GST_RTP_ASF_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_ASF_PAY,GstRtpAsfPay)) +#define GST_RTP_ASF_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_ASF_PAY,GstRtpAsfPayClass)) +#define GST_IS_RTP_ASF_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_ASF_PAY)) +#define GST_IS_RTP_ASF_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_ASF_PAY)) +#define GST_RTP_ASF_PAY_CAST(obj) ((GstRtpAsfPay*)(obj)) + enum GstRtpAsfPayState +{ + ASF_NOT_STARTED, + ASF_DATA_OBJECT, + ASF_PACKETS, + ASF_END +}; + +typedef struct _GstRtpAsfPay GstRtpAsfPay; +typedef struct _GstRtpAsfPayClass GstRtpAsfPayClass; + +struct _GstRtpAsfPay +{ + GstBaseRTPPayload rtppay; + + enum GstRtpAsfPayState state; + + guint32 first_ts; + gchar *config; + guint64 packets_count; + GstAsfFileInfo asfinfo; + + /* current output buffer */ + GstBuffer *current; + guint32 cur_off; + guint32 ts; + gboolean has_ts; + gboolean marker; + + /* keeping it here to avoid allocs/frees */ + GstAsfPacketInfo packetinfo; + + GstBuffer *headers; +}; + +struct _GstRtpAsfPayClass +{ + GstBaseRTPPayloadClass parent_class; +}; + +GType gst_rtp_asf_pay_get_type (void); +gboolean gst_rtp_asf_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS +#endif /* __GST_RTP_ASF_PAY_H__ */ diff --git a/gst/camerabin/camerabinvideo.c b/gst/camerabin/camerabinvideo.c index bc915243..f9c9d875 100644 --- a/gst/camerabin/camerabinvideo.c +++ b/gst/camerabin/camerabinvideo.c @@ -186,6 +186,11 @@ gst_camerabin_video_init (GstCameraBinVideo * vid, vid->mute = ARG_DEFAULT_MUTE; + vid->aud_src_probe_id = 0; + vid->vid_src_probe_id = 0; + vid->vid_tee_probe_id = 0; + vid->vid_sink_probe_id = 0; + /* Create src and sink ghost pads */ vid->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK); gst_element_add_pad (GST_ELEMENT (vid), vid->sinkpad); @@ -194,7 +199,7 @@ gst_camerabin_video_init (GstCameraBinVideo * vid, gst_element_add_pad (GST_ELEMENT (vid), vid->srcpad); /* Add probe for handling eos when stopping recording */ - gst_pad_add_event_probe (vid->sinkpad, + vid->vid_sink_probe_id = gst_pad_add_event_probe (vid->sinkpad, G_CALLBACK (camerabin_video_sink_have_event), vid); } @@ -206,6 +211,11 @@ gst_camerabin_video_dispose (GstCameraBinVideo * vid) g_string_free (vid->filename, TRUE); vid->filename = NULL; + if (vid->vid_sink_probe_id) { + gst_pad_remove_event_probe (vid->sinkpad, vid->vid_sink_probe_id); + vid->vid_sink_probe_id = 0; + } + if (vid->user_post) { gst_object_unref (vid->user_post); vid->user_post = NULL; @@ -439,6 +449,10 @@ camerabin_video_pad_aud_src_have_buffer (GstPad * pad, GstBuffer * buffer, { GstCameraBinVideo *vid = (GstCameraBinVideo *) u_data; + GST_LOG ("buffer in with size %d duration %" G_GINT64_FORMAT " ts %" + GST_TIME_FORMAT, GST_BUFFER_SIZE (buffer), GST_BUFFER_DURATION (buffer), + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer))); + if (vid->calculate_adjust_ts_aud) { GstEvent *event; GstPad *peerpad = NULL; @@ -561,7 +575,7 @@ gst_camerabin_video_create_elements (GstCameraBinVideo * vid) } /* Add probe for rewriting video timestamps */ - gst_pad_add_buffer_probe (vid->tee_video_srcpad, + vid->vid_tee_probe_id = gst_pad_add_buffer_probe (vid->tee_video_srcpad, G_CALLBACK (camerabin_video_pad_tee_src0_have_buffer), vid); #ifdef USE_TIMEOVERLAY @@ -666,12 +680,12 @@ gst_camerabin_video_create_elements (GstCameraBinVideo * vid) vid_srcpad = gst_element_get_static_pad (queue, "src"); gst_ghost_pad_set_target (GST_GHOST_PAD (vid->srcpad), vid_srcpad); /* Never let video bin eos events reach view finder */ - gst_pad_add_event_probe (vid_srcpad, + vid->vid_src_probe_id = gst_pad_add_event_probe (vid_srcpad, G_CALLBACK (gst_camerabin_drop_eos_probe), vid); gst_object_unref (vid_srcpad); pad = gst_element_get_static_pad (vid->aud_src, "src"); - gst_pad_add_buffer_probe (pad, + vid->aud_src_probe_id = gst_pad_add_buffer_probe (pad, G_CALLBACK (camerabin_video_pad_aud_src_have_buffer), vid); gst_object_unref (pad); @@ -700,6 +714,32 @@ gst_camerabin_video_destroy_elements (GstCameraBinVideo * vid) { GST_DEBUG ("destroying video elements"); + /* Remove buffer probe from audio src pad */ + if (vid->aud_src_probe_id) { + GstPad *pad = gst_element_get_static_pad (vid->aud_src, "src"); + if (pad) { + gst_pad_remove_buffer_probe (pad, vid->aud_src_probe_id); + gst_object_unref (pad); + } + vid->aud_src_probe_id = 0; + } + + /* Remove EOS event probe from videobin srcpad (queue's srcpad) */ + if (vid->vid_src_probe_id) { + GstPad *pad = gst_ghost_pad_get_target (GST_GHOST_PAD (vid->srcpad)); + if (pad) { + gst_pad_remove_event_probe (pad, vid->vid_src_probe_id); + gst_object_unref (pad); + } + vid->vid_src_probe_id = 0; + } + + /* Remove buffer probe from video tee srcpad */ + if (vid->vid_tee_probe_id) { + gst_pad_remove_buffer_probe (vid->tee_video_srcpad, vid->vid_tee_probe_id); + vid->vid_tee_probe_id = 0; + } + /* Release tee request pads */ if (vid->tee_video_srcpad) { gst_element_release_request_pad (vid->tee, vid->tee_video_srcpad); diff --git a/gst/camerabin/camerabinvideo.h b/gst/camerabin/camerabinvideo.h index dd094d52..c318e9c3 100644 --- a/gst/camerabin/camerabinvideo.h +++ b/gst/camerabin/camerabinvideo.h @@ -82,6 +82,12 @@ struct _GstCameraBinVideo GstEvent *pending_eos; + /* Probe IDs */ + gulong aud_src_probe_id; + gulong vid_src_probe_id; + gulong vid_tee_probe_id; + gulong vid_sink_probe_id; + gboolean mute; }; diff --git a/gst/camerabin/gstcamerabin.c b/gst/camerabin/gstcamerabin.c index 22a33477..c3010ab8 100644 --- a/gst/camerabin/gstcamerabin.c +++ b/gst/camerabin/gstcamerabin.c @@ -1095,7 +1095,6 @@ gst_camerabin_get_allowed_input_caps (GstCameraBin * camera) GstCaps *caps = NULL; GstPad *pad = NULL, *peer_pad = NULL; GstState state; - gboolean temp_videosrc_pause = FALSE; GstElement *videosrc; g_return_val_if_fail (camera != NULL, NULL); @@ -1121,27 +1120,25 @@ gst_camerabin_get_allowed_input_caps (GstCameraBin * camera) state = GST_STATE (videosrc); - /* Make this function work also in READY and NULL state */ - if (state == GST_STATE_READY || state == GST_STATE_NULL) { - GST_DEBUG_OBJECT (camera, "setting videosrc to paused temporarily"); - temp_videosrc_pause = TRUE; + /* Make this function work also in NULL state */ + if (state == GST_STATE_NULL) { + GST_DEBUG_OBJECT (camera, "setting videosrc to ready temporarily"); peer_pad = gst_pad_get_peer (pad); if (peer_pad) { gst_pad_unlink (pad, peer_pad); } - /* Set videosrc to PAUSED to open video device */ + /* Set videosrc to READY to open video device */ gst_element_set_locked_state (videosrc, TRUE); - gst_element_set_state (videosrc, GST_STATE_PAUSED); + gst_element_set_state (videosrc, GST_STATE_READY); } camera->allowed_caps = gst_pad_get_caps (pad); /* Restore state and re-link if necessary */ - if (temp_videosrc_pause) { + if (state == GST_STATE_NULL) { GST_DEBUG_OBJECT (camera, "restoring videosrc state %d", state); /* Reset videosrc to NULL state, some drivers seem to need this */ gst_element_set_state (videosrc, GST_STATE_NULL); - gst_element_set_state (videosrc, state); if (peer_pad) { gst_pad_link (pad, peer_pad); gst_object_unref (peer_pad); @@ -1597,13 +1594,11 @@ gst_camerabin_start_video_recording (GstCameraBin * camera) gst_camerabin_rewrite_tags (camera); /* Pause the pipeline in order to distribute new clock in paused_to_playing */ - /* audio src timestamps will be 0 without state change to READY. ??? */ - gst_element_set_state (GST_ELEMENT (camera), GST_STATE_READY); - gst_element_set_locked_state (camera->vidbin, FALSE); state_ret = gst_element_set_state (GST_ELEMENT (camera), GST_STATE_PAUSED); if (state_ret != GST_STATE_CHANGE_FAILURE) { g_mutex_lock (camera->capture_mutex); + gst_element_set_locked_state (camera->vidbin, FALSE); g_object_set (G_OBJECT (camera->src_out_sel), "resend-latest", FALSE, "active-pad", camera->pad_src_vid, NULL); diff --git a/gst/camerabin/gstcamerabinphotography.c b/gst/camerabin/gstcamerabinphotography.c index cb17abca..c5689149 100644 --- a/gst/camerabin/gstcamerabinphotography.c +++ b/gst/camerabin/gstcamerabinphotography.c @@ -42,7 +42,7 @@ gst_camerabin_set_ev_compensation (GstPhotography * photo, gfloat ev_compensation) { GstCameraBin *camera; - gboolean ret = FALSE; + gboolean ret = TRUE; g_return_val_if_fail (photo != NULL, FALSE); @@ -82,7 +82,7 @@ static gboolean gst_camerabin_set_iso_speed (GstPhotography * photo, guint iso_speed) { GstCameraBin *camera; - gboolean ret = FALSE; + gboolean ret = TRUE; g_return_val_if_fail (photo != NULL, FALSE); @@ -120,7 +120,7 @@ gst_camerabin_set_white_balance_mode (GstPhotography * photo, GstWhiteBalanceMode white_balance_mode) { GstCameraBin *camera; - gboolean ret = FALSE; + gboolean ret = TRUE; g_return_val_if_fail (photo != NULL, FALSE); @@ -161,7 +161,7 @@ gst_camerabin_set_colour_tone_mode (GstPhotography * photo, GstColourToneMode colour_tone_mode) { GstCameraBin *camera; - gboolean ret = FALSE; + gboolean ret = TRUE; g_return_val_if_fail (photo != NULL, FALSE); @@ -201,7 +201,7 @@ static gboolean gst_camerabin_set_flash_mode (GstPhotography * photo, GstFlashMode flash_mode) { GstCameraBin *camera; - gboolean ret = FALSE; + gboolean ret = TRUE; g_return_val_if_fail (photo != NULL, FALSE); @@ -269,7 +269,7 @@ static gboolean gst_camerabin_set_scene_mode (GstPhotography * photo, GstSceneMode scene_mode) { GstCameraBin *camera; - gboolean ret = FALSE; + gboolean ret = TRUE; g_return_val_if_fail (photo != NULL, FALSE); @@ -345,7 +345,7 @@ static gboolean gst_camerabin_set_config (GstPhotography * photo, GstPhotoSettings * config) { GstCameraBin *camera; - gboolean ret = FALSE; + gboolean ret = TRUE; g_return_val_if_fail (photo != NULL, FALSE); camera = GST_CAMERABIN (photo); diff --git a/gst/flacparse/Makefile.am b/gst/flacparse/Makefile.am index 00e6f93f..8278130b 100644 --- a/gst/flacparse/Makefile.am +++ b/gst/flacparse/Makefile.am @@ -1,6 +1,6 @@ plugin_LTLIBRARIES = libgstflacparse.la -libgstflacparse_la_SOURCES = gstflac.c gstbaseparse.c gstflacparse.c gstbitreader.c gstbytereader.c +libgstflacparse_la_SOURCES = gstflac.c gstbaseparse.c gstflacparse.c libgstflacparse_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) libgstflacparse_la_LIBADD = \ $(GST_PLUGINS_BASE_LIBS) -lgsttag-$(GST_MAJORMINOR) \ @@ -9,4 +9,4 @@ libgstflacparse_la_LIBADD = \ libgstflacparse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstflacparse_la_LIBTOOLFLAGS = --tag=disable-static -noinst_HEADERS = gstbitreader.h gstbytereader.h gstbaseparse.h gstflacparse.h +noinst_HEADERS = gstbaseparse.h gstflacparse.h diff --git a/gst/flacparse/gstbaseparse.c b/gst/flacparse/gstbaseparse.c index 85fb8d71..ccc2f3eb 100644 --- a/gst/flacparse/gstbaseparse.c +++ b/gst/flacparse/gstbaseparse.c @@ -895,6 +895,8 @@ gst_base_parse_drain (GstBaseParse * parse) { guint avail; + GST_DEBUG_OBJECT (parse, "draining"); + for (;;) { avail = gst_adapter_available (parse->priv->adapter); if (!avail) @@ -904,6 +906,12 @@ gst_base_parse_drain (GstBaseParse * parse) if (gst_base_parse_chain (parse->sinkpad, NULL) != GST_FLOW_OK) { break; } + + /* nothing changed, maybe due to truncated frame; break infinite loop */ + if (avail == gst_adapter_available (parse->adapter)) { + GST_DEBUG_OBJECT (parse, "no change during draining; flushing"); + gst_adapter_clear (parse->adapter); + } } } @@ -1026,6 +1034,13 @@ gst_base_parse_chain (GstPad * pad, GstBuffer * buffer) skip = -1; if (bclass->check_valid_frame (parse, tmpbuf, &fsize, &skip)) { + if (gst_adapter_available (parse->adapter) < fsize) { + GST_DEBUG_OBJECT (parse, + "found valid frame but not enough data available (only %d bytes)", + gst_adapter_available (parse->adapter)); + gst_buffer_unref (tmpbuf); + goto done; + } break; } if (skip > 0) { diff --git a/gst/flacparse/gstbitreader.c b/gst/flacparse/gstbitreader.c deleted file mode 100644 index 7647ab94..00000000 --- a/gst/flacparse/gstbitreader.c +++ /dev/null @@ -1,400 +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. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstbitreader.h" - -#include <string.h> - -/** - * SECTION:gstbitreader - * @short_description: Reads any number of bits from a memory buffer - * - * #GstBitReader provides a bit reader that can read any number of bits - * from a memory buffer. It provides functions for reading any number of bits - * into 8, 16, 32 and 64 bit variables. - */ - -/** - * gst_bit_reader_new: - * @data: Data from which the #GstBitReader should read - * @size: Size of @data in bytes - * - * Create a new #GstBitReader instance, which will read from @data. - * - * Returns: a new #GstBitReader instance - * - * Since: 0.10.22 - */ -GstBitReader * -gst_bit_reader_new (const guint8 * data, guint size) -{ - GstBitReader *ret = g_slice_new0 (GstBitReader); - - ret->data = data; - ret->size = size; - - return ret; -} - -/** - * gst_bit_reader_new_from_buffer: - * @buffer: Buffer from which the #GstBitReader should read - * - * Create a new #GstBitReader instance, which will read from the - * #GstBuffer @buffer. - * - * Returns: a new #GstBitReader instance - * - * Since: 0.10.22 - */ -GstBitReader * -gst_bit_reader_new_from_buffer (const GstBuffer * buffer) -{ - g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL); - - return gst_bit_reader_new (GST_BUFFER_DATA (buffer), - GST_BUFFER_SIZE (buffer)); -} - -/** - * gst_bit_reader_free: - * @reader: a #GstBitReader instance - * - * Frees a #GstBitReader instance, which was previously allocated by - * gst_bit_reader_new() or gst_bit_reader_new_from_buffer(). - * - * Since: 0.10.22 - */ -void -gst_bit_reader_free (GstBitReader * reader) -{ - g_return_if_fail (reader != NULL); - - g_slice_free (GstBitReader, reader); -} - -/** - * gst_bit_reader_init: - * @reader: a #GstBitReader instance - * @data: Data from which the #GstBitReader should read - * @size: Size of @data in bytes - * - * Initializes a #GstBitReader instance to read from @data. This function - * can be called on already initialized instances. - * - * Since: 0.10.22 - */ -void -gst_bit_reader_init (GstBitReader * reader, const guint8 * data, guint size) -{ - g_return_if_fail (reader != NULL); - - reader->data = data; - reader->size = size; - reader->byte = reader->bit = 0; -} - -/** - * gst_bit_reader_init: - * @reader: a #GstBitReader instance - * @buffer: Buffer from which the #GstBitReader should read - * - * Initializes a #GstBitReader instance to read from @buffer. This function - * can be called on already initialized instances. - * - * Since: 0.10.22 - */ -void -gst_bit_reader_init_from_buffer (GstBitReader * reader, - const GstBuffer * buffer) -{ - g_return_if_fail (GST_IS_BUFFER (buffer)); - - gst_bit_reader_init (reader, GST_BUFFER_DATA (buffer), - GST_BUFFER_SIZE (buffer)); -} - -/** - * gst_bit_reader_set_pos: - * @reader: a #GstBitReader instance - * @pos: The new position in bits - * - * Sets the new position of a #GstBitReader instance to @pos in bits. - * - * Returns: %TRUE if the position could be set successfully, %FALSE - * otherwise. - * - * Since: 0.10.22 - */ -gboolean -gst_bit_reader_set_pos (GstBitReader * reader, guint pos) -{ - g_return_val_if_fail (reader != NULL, FALSE); - - if (pos > reader->size * 8) - return FALSE; - - reader->byte = pos / 8; - reader->bit = pos % 8; - - return TRUE; -} - -/** - * gst_bit_reader_get_pos: - * @reader: a #GstBitReader instance - * - * Returns the current position of a #GstBitReader instance in bits. - * - * Returns: The current position of @reader in bits. - * - * Since: 0.10.22 - */ -guint -gst_bit_reader_get_pos (const GstBitReader * reader) -{ - g_return_val_if_fail (reader != NULL, 0); - - return reader->byte * 8 + reader->bit; -} - -/** - * gst_bit_reader_get_remaining: - * @reader: a #GstBitReader instance - * - * Returns the remaining number of bits of a #GstBitReader instance. - * - * Returns: The remaining number of bits of @reader instance. - * - * Since: 0.10.22 - */ -guint -gst_bit_reader_get_remaining (const GstBitReader * reader) -{ - g_return_val_if_fail (reader != NULL, 0); - - return reader->size * 8 - (reader->byte * 8 + reader->bit); -} - -/** - * gst_bit_reader_skip: - * @reader: a #GstBitReader instance - * @nbits: the number of bits to skip - * - * Skips @nbits bits of the #GstBitReader instance. - * - * Returns: %TRUE if @nbits bits could be skipped, %FALSE otherwise. - * - * Since: 0.10.22 - */ -gboolean -gst_bit_reader_skip (GstBitReader * reader, guint nbits) -{ - g_return_val_if_fail (reader != NULL, FALSE); - - if (gst_bit_reader_get_remaining (reader) < nbits) - return FALSE; - - reader->bit += nbits; - reader->byte += reader->bit / 8; - reader->bit = reader->bit % 8; - - return TRUE; -} - -/** - * gst_bit_reader_skip_to_byte: - * @reader: a #GstBitReader instance - * - * Skips until the next byte. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ -gboolean -gst_bit_reader_skip_to_byte (GstBitReader * reader) -{ - g_return_val_if_fail (reader != NULL, FALSE); - - if (reader->byte > reader->size) - return FALSE; - - if (reader->bit) { - reader->bit = 0; - reader->byte++; - } - - return TRUE; -} - -/** - * gst_bit_reader_get_bits_uint8: - * @reader: a #GstBitReader instance - * @val: Pointer to a #guint8 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_bit_reader_get_bits_uint16: - * @reader: a #GstBitReader instance - * @val: Pointer to a #guint16 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_bit_reader_get_bits_uint32: - * @reader: a #GstBitReader instance - * @val: Pointer to a #guint32 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_bit_reader_get_bits_uint64: - * @reader: a #GstBitReader instance - * @val: Pointer to a #guint64 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_bit_reader_peek_bits_uint8: - * @reader: a #GstBitReader instance - * @val: Pointer to a #guint8 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_bit_reader_peek_bits_uint16: - * @reader: a #GstBitReader instance - * @val: Pointer to a #guint16 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_bit_reader_peek_bits_uint32: - * @reader: a #GstBitReader instance - * @val: Pointer to a #guint32 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_bit_reader_peek_bits_uint64: - * @reader: a #GstBitReader instance - * @val: Pointer to a #guint64 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -#define GST_BIT_READER_READ_BITS(bits) \ -gboolean \ -gst_bit_reader_get_bits_uint##bits (GstBitReader *reader, guint##bits *val, guint nbits) \ -{ \ - guint##bits ret = 0; \ - \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - g_return_val_if_fail (nbits <= bits, FALSE); \ - \ - if (reader->byte * 8 + reader->bit + nbits > reader->size * 8) \ - return FALSE; \ - \ - while (nbits > 0) { \ - guint toread = MIN (nbits, 8 - reader->bit); \ - \ - ret <<= toread; \ - ret |= (reader->data[reader->byte] & (0xff >> reader->bit)) >> (8 - toread - reader->bit); \ - \ - reader->bit += toread; \ - if (reader->bit >= 8) { \ - reader->byte++; \ - reader->bit = 0; \ - } \ - nbits -= toread; \ - } \ - \ - *val = ret; \ - return TRUE; \ -} \ -\ -gboolean \ -gst_bit_reader_peek_bits_uint##bits (const GstBitReader *reader, guint##bits *val, guint nbits) \ -{ \ - GstBitReader tmp; \ - \ - g_return_val_if_fail (reader != NULL, FALSE); \ - tmp = *reader; \ - return gst_bit_reader_get_bits_uint##bits (&tmp, val, nbits); \ -} - -GST_BIT_READER_READ_BITS (8); -GST_BIT_READER_READ_BITS (16); -GST_BIT_READER_READ_BITS (32); -GST_BIT_READER_READ_BITS (64); diff --git a/gst/flacparse/gstbitreader.h b/gst/flacparse/gstbitreader.h deleted file mode 100644 index 5dc16a30..00000000 --- a/gst/flacparse/gstbitreader.h +++ /dev/null @@ -1,99 +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_BIT_READER_H__ -#define __GST_BIT_READER_H__ - -#include <gst/gst.h> - -G_BEGIN_DECLS - -/** - * GstBitReader: - * @data: Data from which the bit reader will read - * @size: Size of @data in bytes - * @byte: Current byte position - * @bit: Bit position in the current byte - * - * A bit reader instance. - */ -typedef struct { - const guint8 *data; - guint size; - - guint byte; /* Byte position */ - guint bit; /* Bit position in the current byte */ -} GstBitReader; - -GstBitReader * gst_bit_reader_new (const guint8 *data, guint size); -GstBitReader * gst_bit_reader_new_from_buffer (const GstBuffer *buffer); -void gst_bit_reader_free (GstBitReader *reader); - -void gst_bit_reader_init (GstBitReader *reader, const guint8 *data, guint size); -void gst_bit_reader_init_from_buffer (GstBitReader *reader, const GstBuffer *buffer); - -gboolean gst_bit_reader_set_pos (GstBitReader *reader, guint pos); - -guint gst_bit_reader_get_pos (const GstBitReader *reader); -guint gst_bit_reader_get_remaining (const GstBitReader *reader); - -gboolean gst_bit_reader_skip (GstBitReader *reader, guint nbits); -gboolean gst_bit_reader_skip_to_byte (GstBitReader *reader); - -gboolean gst_bit_reader_get_bits_uint8 (GstBitReader *reader, guint8 *val, guint nbits); -gboolean gst_bit_reader_get_bits_uint16 (GstBitReader *reader, guint16 *val, guint nbits); -gboolean gst_bit_reader_get_bits_uint32 (GstBitReader *reader, guint32 *val, guint nbits); -gboolean gst_bit_reader_get_bits_uint64 (GstBitReader *reader, guint64 *val, guint nbits); - -gboolean gst_bit_reader_peek_bits_uint8 (const GstBitReader *reader, guint8 *val, guint nbits); -gboolean gst_bit_reader_peek_bits_uint16 (const GstBitReader *reader, guint16 *val, guint nbits); -gboolean gst_bit_reader_peek_bits_uint32 (const GstBitReader *reader, guint32 *val, guint nbits); -gboolean gst_bit_reader_peek_bits_uint64 (const GstBitReader *reader, guint64 *val, guint nbits); - -/** - * GST_BIT_READER_INIT: - * @data: Data from which the #GstBitReader should read - * @size: Size of @data in bytes - * - * A #GstBitReader must be initialized with this macro, before it can be - * used. This macro can used be to initialize a variable, but it cannot - * be assigned to a variable. In that case you have to use - * gst_bit_reader_init(). - * - * Since: 0.10.22 - */ -#define GST_BIT_READER_INIT(data, size) {data, size, 0, 0} - -/** - * GST_BIT_READER_INIT_FROM_BUFFER: - * @buffer: Buffer from which the #GstBitReader should read - * - * A #GstBitReader must be initialized with this macro, before it can be - * used. This macro can used be to initialize a variable, but it cannot - * be assigned to a variable. In that case you have to use - * gst_bit_reader_init(). - * - * Since: 0.10.22 - */ -#define GST_BIT_READER_INIT_FROM_BUFFER(buffer) {GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 0, 0} - -G_END_DECLS - -#endif /* __GST_BIT_READER_H__ */ diff --git a/gst/flacparse/gstbytereader.c b/gst/flacparse/gstbytereader.c deleted file mode 100644 index cb91ea79..00000000 --- a/gst/flacparse/gstbytereader.c +++ /dev/null @@ -1,1231 +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. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstbytereader.h" - -#include <string.h> - -/** - * SECTION:gstbytereader - * @short_description: Reads different integer and floating point types from a memory buffer - * - * #GstByteReader provides a byte reader that can read different integer and - * floating point types from a memory buffer. It provides functions for reading - * signed/unsigned, little/big endian integers of 8, 16, 24, 32 and 64 bits - * and functions for reading little/big endian floating points numbers of - * 32 and 64 bits. - */ - - -/* Copied from gst/floatcast/floatcast.h as this is in gst-plugins-base */ - -inline static gdouble -GDOUBLE_SWAP_LE_BE (gdouble in) -{ - union - { - guint64 i; - gdouble d; - } u; - - u.d = in; - u.i = GUINT64_SWAP_LE_BE (u.i); - return u.d; -} - -inline static gfloat -GFLOAT_SWAP_LE_BE (gfloat in) -{ - union - { - guint32 i; - gfloat f; - } u; - - u.f = in; - u.i = GUINT32_SWAP_LE_BE (u.i); - return u.f; -} - -#if G_BYTE_ORDER == G_LITTLE_ENDIAN -#define GFLOAT_TO_LE(val) ((gfloat) (val)) -#define GFLOAT_TO_BE(val) (GFLOAT_SWAP_LE_BE (val)) -#define GDOUBLE_TO_LE(val) ((gdouble) (val)) -#define GDOUBLE_TO_BE(val) (GDOUBLE_SWAP_LE_BE (val)) - -#elif G_BYTE_ORDER == G_BIG_ENDIAN -#define GFLOAT_TO_LE(val) (GFLOAT_SWAP_LE_BE (val)) -#define GFLOAT_TO_BE(val) ((gfloat) (val)) -#define GDOUBLE_TO_LE(val) (GDOUBLE_SWAP_LE_BE (val)) -#define GDOUBLE_TO_BE(val) ((gdouble) (val)) - -#else /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */ -#error unknown ENDIAN type -#endif /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */ - -#define GFLOAT_FROM_LE(val) (GFLOAT_TO_LE (val)) -#define GFLOAT_FROM_BE(val) (GFLOAT_TO_BE (val)) -#define GDOUBLE_FROM_LE(val) (GDOUBLE_TO_LE (val)) -#define GDOUBLE_FROM_BE(val) (GDOUBLE_TO_BE (val)) - - -/** - * gst_byte_reader_new: - * @data: Data from which the #GstByteReader should read - * @size: Size of @data in bytes - * - * Create a new #GstByteReader instance, which will read from @data. - * - * Returns: a new #GstByteReader instance - * - * Since: 0.10.22 - */ -GstByteReader * -gst_byte_reader_new (const guint8 * data, guint size) -{ - GstByteReader *ret = g_slice_new0 (GstByteReader); - - ret->data = data; - ret->size = size; - - return ret; -} - -/** - * gst_byte_reader_new_from_buffer: - * @buffer: Buffer from which the #GstByteReader should read - * - * Create a new #GstByteReader instance, which will read from the - * #GstBuffer @buffer. - * - * Returns: a new #GstByteReader instance - * - * Since: 0.10.22 - */ -GstByteReader * -gst_byte_reader_new_from_buffer (const GstBuffer * buffer) -{ - g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL); - - return gst_byte_reader_new (GST_BUFFER_DATA (buffer), - GST_BUFFER_SIZE (buffer)); -} - -/** - * gst_byte_reader_free: - * @reader: a #GstByteReader instance - * - * Frees a #GstByteReader instance, which was previously allocated by - * gst_byte_reader_new() or gst_byte_reader_new_from_buffer(). - * - * Since: 0.10.22 - */ -void -gst_byte_reader_free (GstByteReader * reader) -{ - g_return_if_fail (reader != NULL); - - g_slice_free (GstByteReader, reader); -} - -/** - * gst_byte_reader_init: - * @reader: a #GstByteReader instance - * @data: Data from which the #GstByteReader should read - * @size: Size of @data in bytes - * - * Initializes a #GstByteReader instance to read from @data. This function - * can be called on already initialized instances. - * - * Since: 0.10.22 - */ -void -gst_byte_reader_init (GstByteReader * reader, const guint8 * data, guint size) -{ - g_return_if_fail (reader != NULL); - - reader->data = data; - reader->size = size; - reader->byte = 0; -} - -/** - * gst_byte_reader_init: - * @reader: a #GstByteReader instance - * @buffer: Buffer from which the #GstByteReader should read - * - * Initializes a #GstByteReader instance to read from @buffer. This function - * can be called on already initialized instances. - * - * Since: 0.10.22 - */ -void -gst_byte_reader_init_from_buffer (GstByteReader * reader, - const GstBuffer * buffer) -{ - g_return_if_fail (GST_IS_BUFFER (buffer)); - - gst_byte_reader_init (reader, GST_BUFFER_DATA (buffer), - GST_BUFFER_SIZE (buffer)); -} - -/** - * gst_byte_reader_set_pos: - * @reader: a #GstByteReader instance - * @pos: The new position in bytes - * - * Sets the new position of a #GstByteReader instance to @pos in bytes. - * - * Returns: %TRUE if the position could be set successfully, %FALSE - * otherwise. - * - * Since: 0.10.22 - */ -gboolean -gst_byte_reader_set_pos (GstByteReader * reader, guint pos) -{ - g_return_val_if_fail (reader != NULL, FALSE); - - if (pos > reader->size) - return FALSE; - - reader->byte = pos; - - return TRUE; -} - -/** - * gst_byte_reader_get_pos: - * @reader: a #GstByteReader instance - * - * Returns the current position of a #GstByteReader instance in bytes. - * - * Returns: The current position of @reader in bytes. - * - * Since: 0.10.22 - */ -guint -gst_byte_reader_get_pos (const GstByteReader * reader) -{ - g_return_val_if_fail (reader != NULL, 0); - - return reader->byte; -} - -/** - * gst_byte_reader_get_remaining: - * @reader: a #GstByteReader instance - * - * Returns the remaining number of bytes of a #GstByteReader instance. - * - * Returns: The remaining number of bytes of @reader instance. - * - * Since: 0.10.22 - */ -guint -gst_byte_reader_get_remaining (const GstByteReader * reader) -{ - g_return_val_if_fail (reader != NULL, 0); - - return reader->size - reader->byte; -} - -/** - * gst_byte_reader_skip: - * @reader: a #GstByteReader instance - * @nbytes: the number of bytes to skip - * - * Skips @nbytes bytes of the #GstByteReader instance. - * - * Returns: %TRUE if @nbytes bytes could be skipped, %FALSE otherwise. - * - * Since: 0.10.22 - */ -gboolean -gst_byte_reader_skip (GstByteReader * reader, guint nbytes) -{ - g_return_val_if_fail (reader != NULL, FALSE); - - if (gst_byte_reader_get_remaining (reader) < nbytes) - return FALSE; - - reader->byte += nbytes; - - return TRUE; -} - -/** - * gst_byte_reader_get_uint8: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint8 to store the result - * - * Read an unsigned 8 bit integer into @val and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_int8: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint8 to store the result - * - * Read a signed 8 bit integer into @val and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_uint8: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint8 to store the result - * - * Read a signed 8 bit integer into @val but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_int8: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint8 to store the result - * - * Read a signed 8 bit integer into @val but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_uint16_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint16 to store the result - * - * Read an unsigned 16 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_int16_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint16 to store the result - * - * Read a signed 16 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_uint16_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint16 to store the result - * - * Read a signed 16 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_int16_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint16 to store the result - * - * Read a signed 16 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_uint16_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint16 to store the result - * - * Read an unsigned 16 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_int16_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint16 to store the result - * - * Read a signed 16 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_uint16_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint16 to store the result - * - * Read a signed 16 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_int16_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint16 to store the result - * - * Read a signed 16 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_uint24_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint32 to store the result - * - * Read an unsigned 24 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_int24_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint32 to store the result - * - * Read a signed 24 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_uint24_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint32 to store the result - * - * Read a signed 24 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_int24_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint32 to store the result - * - * Read a signed 24 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_uint24_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint32 to store the result - * - * Read an unsigned 24 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_int24_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint32 to store the result - * - * Read a signed 24 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_uint24_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint32 to store the result - * - * Read a signed 24 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_int24_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint32 to store the result - * - * Read a signed 24 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - - -/** - * gst_byte_reader_get_uint32_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint32 to store the result - * - * Read an unsigned 32 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_int32_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint32 to store the result - * - * Read a signed 32 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_uint32_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint32 to store the result - * - * Read a signed 32 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_int32_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint32 to store the result - * - * Read a signed 32 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_uint32_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint32 to store the result - * - * Read an unsigned 32 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_int32_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint32 to store the result - * - * Read a signed 32 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_uint32_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint32 to store the result - * - * Read a signed 32 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_int32_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint32 to store the result - * - * Read a signed 32 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_uint64_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint64 to store the result - * - * Read an unsigned 64 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_int64_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint64 to store the result - * - * Read a signed 64 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_uint64_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint64 to store the result - * - * Read a signed 64 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_int64_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint64 to store the result - * - * Read a signed 64 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_uint64_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint64 to store the result - * - * Read an unsigned 64 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_int64_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint64 to store the result - * - * Read a signed 64 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_uint64_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint64 to store the result - * - * Read a signed 64 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_int64_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint64 to store the result - * - * Read a signed 64 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -#define GST_BYTE_READER_READ_INTS(bits) \ -gboolean \ -gst_byte_reader_get_uint##bits##_le (GstByteReader *reader, guint##bits *val) \ -{ \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - *val = GST_READ_UINT##bits##_LE (&reader->data[reader->byte]); \ - reader->byte += bits / 8; \ - return TRUE; \ -} \ -\ -gboolean \ -gst_byte_reader_get_uint##bits##_be (GstByteReader *reader, guint##bits *val) \ -{ \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - *val = GST_READ_UINT##bits##_BE (&reader->data[reader->byte]); \ - reader->byte += bits / 8; \ - return TRUE; \ -} \ -\ -gboolean \ -gst_byte_reader_get_int##bits##_le (GstByteReader *reader, gint##bits *val) \ -{ \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - *val = GST_READ_UINT##bits##_LE (&reader->data[reader->byte]); \ - reader->byte += bits / 8; \ - return TRUE; \ -} \ -\ -gboolean \ -gst_byte_reader_get_int##bits##_be (GstByteReader *reader, gint##bits *val) \ -{ \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - *val = GST_READ_UINT##bits##_BE (&reader->data[reader->byte]); \ - reader->byte += bits / 8; \ - return TRUE; \ -} \ -gboolean \ -gst_byte_reader_peek_uint##bits##_le (GstByteReader *reader, guint##bits *val) \ -{ \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - *val = GST_READ_UINT##bits##_LE (&reader->data[reader->byte]); \ - return TRUE; \ -} \ -\ -gboolean \ -gst_byte_reader_peek_uint##bits##_be (GstByteReader *reader, guint##bits *val) \ -{ \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - *val = GST_READ_UINT##bits##_BE (&reader->data[reader->byte]); \ - return TRUE; \ -} \ -\ -gboolean \ -gst_byte_reader_peek_int##bits##_le (GstByteReader *reader, gint##bits *val) \ -{ \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - *val = GST_READ_UINT##bits##_LE (&reader->data[reader->byte]); \ - return TRUE; \ -} \ -\ -gboolean \ -gst_byte_reader_peek_int##bits##_be (GstByteReader *reader, gint##bits *val) \ -{ \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - *val = GST_READ_UINT##bits##_BE (&reader->data[reader->byte]); \ - return TRUE; \ -} - - -GST_BYTE_READER_READ_INTS (16); -GST_BYTE_READER_READ_INTS (32); -GST_BYTE_READER_READ_INTS (64); - -gboolean -gst_byte_reader_get_uint8 (GstByteReader * reader, guint8 * val) -{ - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 1 > reader->size) - return FALSE; - - *val = GST_READ_UINT8 (&reader->data[reader->byte]); - reader->byte++; - return TRUE; -} - -gboolean -gst_byte_reader_get_int8 (GstByteReader * reader, gint8 * val) -{ - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 1 > reader->size) - return FALSE; - - *val = GST_READ_UINT8 (&reader->data[reader->byte]); - reader->byte++; - return TRUE; -} - -gboolean -gst_byte_reader_peek_uint8 (GstByteReader * reader, guint8 * val) -{ - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 1 > reader->size) - return FALSE; - - *val = GST_READ_UINT8 (&reader->data[reader->byte]); - return TRUE; -} - -gboolean -gst_byte_reader_peek_int8 (GstByteReader * reader, gint8 * val) -{ - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 1 > reader->size) - return FALSE; - - *val = GST_READ_UINT8 (&reader->data[reader->byte]); - return TRUE; -} - -gboolean -gst_byte_reader_get_uint24_le (GstByteReader * reader, guint32 * val) -{ - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 3 > reader->size) - return FALSE; - - *val = - (reader->data[reader->byte] | (reader->data[reader->byte + - 1] << 8) | (reader->data[reader->byte + 2] << 16)); - reader->byte += 3; - return TRUE; -} - -gboolean -gst_byte_reader_get_uint24_be (GstByteReader * reader, guint32 * val) -{ - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 3 > reader->size) - return FALSE; - - *val = - (reader->data[reader->byte + 2] | (reader->data[reader->byte + - 1] << 8) | (reader->data[reader->byte] << 16)); - reader->byte += 3; - return TRUE; -} - -gboolean -gst_byte_reader_get_int24_le (GstByteReader * reader, gint32 * val) -{ - guint32 ret; - - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 3 > reader->size) - return FALSE; - - ret = - (reader->data[reader->byte] | (reader->data[reader->byte + - 1] << 8) | (reader->data[reader->byte + 2] << 16)); - - if (ret & 0x00800000) - ret |= 0xff000000; - - reader->byte += 3; - - *val = ret; - return TRUE; -} - -gboolean -gst_byte_reader_get_int24_be (GstByteReader * reader, gint32 * val) -{ - guint32 ret; - - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 3 > reader->size) - return FALSE; - - ret = - (reader->data[reader->byte + 2] | (reader->data[reader->byte + - 1] << 8) | (reader->data[reader->byte] << 16)); - if (ret & 0x00800000) - ret |= 0xff000000; - - reader->byte += 3; - - *val = ret; - return TRUE; -} - -gboolean -gst_byte_reader_peek_uint24_le (GstByteReader * reader, guint32 * val) -{ - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 3 > reader->size) - return FALSE; - - *val = - (reader->data[reader->byte] | (reader->data[reader->byte + - 1] << 8) | (reader->data[reader->byte + 2] << 16)); - return TRUE; -} - -gboolean -gst_byte_reader_peek_uint24_be (GstByteReader * reader, guint32 * val) -{ - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 3 > reader->size) - return FALSE; - - *val = - (reader->data[reader->byte + 2] | (reader->data[reader->byte + - 1] << 8) | (reader->data[reader->byte] << 16)); - return TRUE; -} - -gboolean -gst_byte_reader_peek_int24_le (GstByteReader * reader, gint32 * val) -{ - guint32 ret; - - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 3 > reader->size) - return FALSE; - - ret = - (reader->data[reader->byte] | (reader->data[reader->byte + - 1] << 8) | (reader->data[reader->byte + 2] << 16)); - - if (ret & 0x00800000) - ret |= 0xff000000; - - *val = ret; - return TRUE; -} - -gboolean -gst_byte_reader_peek_int24_be (GstByteReader * reader, gint32 * val) -{ - guint32 ret; - - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 3 > reader->size) - return FALSE; - - ret = - (reader->data[reader->byte + 2] | (reader->data[reader->byte + - 1] << 8) | (reader->data[reader->byte] << 16)); - if (ret & 0x00800000) - ret |= 0xff000000; - - *val = ret; - return TRUE; -} - -/** - * gst_byte_reader_get_float32_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gfloat to store the result - * - * Read a 32 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_float32_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gfloat to store the result - * - * Read a 32 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_float32_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gfloat to store the result - * - * Read a 32 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_float32_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gfloat to store the result - * - * Read a 32 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_float64_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gdouble to store the result - * - * Read a 64 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_float64_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gdouble to store the result - * - * Read a 64 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_float64_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gdouble to store the result - * - * Read a 64 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_float64_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gdouble to store the result - * - * Read a 64 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -#define GST_BYTE_READER_READ_FLOATS(bits, type, TYPE) \ -gboolean \ -gst_byte_reader_get_float##bits##_le (GstByteReader *reader, g##type *val) \ -{ \ - g##type ret; \ - \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - memcpy (&ret, &reader->data[reader->byte], bits / 8); \ - *val = G##TYPE##_FROM_LE (ret); \ - reader->byte += bits / 8; \ - return TRUE; \ -} \ -gboolean \ -gst_byte_reader_get_float##bits##_be (GstByteReader *reader, g##type *val) \ -{ \ - g##type ret; \ - \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - memcpy (&ret, &reader->data[reader->byte], bits / 8); \ - *val = G##TYPE##_FROM_BE (ret); \ - reader->byte += bits / 8; \ - return TRUE; \ -} \ -gboolean \ -gst_byte_reader_peek_float##bits##_le (GstByteReader *reader, g##type *val) \ -{ \ - g##type ret; \ - \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - memcpy (&ret, &reader->data[reader->byte], bits / 8); \ - *val = G##TYPE##_FROM_LE (ret); \ - return TRUE; \ -} \ -gboolean \ -gst_byte_reader_peek_float##bits##_be (GstByteReader *reader, g##type *val) \ -{ \ - g##type ret; \ - \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - memcpy (&ret, &reader->data[reader->byte], bits / 8); \ - *val = G##TYPE##_FROM_BE (ret); \ - return TRUE; \ -} - -GST_BYTE_READER_READ_FLOATS (32, float, FLOAT); -GST_BYTE_READER_READ_FLOATS (64, double, DOUBLE); diff --git a/gst/flacparse/gstbytereader.h b/gst/flacparse/gstbytereader.h deleted file mode 100644 index 28534c00..00000000 --- a/gst/flacparse/gstbytereader.h +++ /dev/null @@ -1,134 +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_BYTE_READER_H__ -#define __GST_BYTE_READER_H__ - -#include <gst/gst.h> - -G_BEGIN_DECLS - -/** - * GstByteReader: - * @data: Data from which the bit reader will read - * @size: Size of @data in bytes - * @byte: Current byte position - * - * A byte reader instance. - */ -typedef struct { - const guint8 *data; - guint size; - - guint byte; /* Byte position */ -} GstByteReader; - -GstByteReader * gst_byte_reader_new (const guint8 *data, guint size); -GstByteReader * gst_byte_reader_new_from_buffer (const GstBuffer *buffer); -void gst_byte_reader_free (GstByteReader *reader); - -void gst_byte_reader_init (GstByteReader *reader, const guint8 *data, guint size); -void gst_byte_reader_init_from_buffer (GstByteReader *reader, const GstBuffer *buffer); - -gboolean gst_byte_reader_set_pos (GstByteReader *reader, guint pos); - -guint gst_byte_reader_get_pos (const GstByteReader *reader); -guint gst_byte_reader_get_remaining (const GstByteReader *reader); - -gboolean gst_byte_reader_skip (GstByteReader *reader, guint nbytes); - -gboolean gst_byte_reader_get_uint8 (GstByteReader *reader, guint8 *val); -gboolean gst_byte_reader_get_int8 (GstByteReader *reader, gint8 *val); -gboolean gst_byte_reader_get_uint16_le (GstByteReader *reader, guint16 *val); -gboolean gst_byte_reader_get_int16_le (GstByteReader *reader, gint16 *val); -gboolean gst_byte_reader_get_uint16_be (GstByteReader *reader, guint16 *val); -gboolean gst_byte_reader_get_int16_be (GstByteReader *reader, gint16 *val); -gboolean gst_byte_reader_get_uint24_le (GstByteReader *reader, guint32 *val); -gboolean gst_byte_reader_get_int24_le (GstByteReader *reader, gint32 *val); -gboolean gst_byte_reader_get_uint24_be (GstByteReader *reader, guint32 *val); -gboolean gst_byte_reader_get_int24_be (GstByteReader *reader, gint32 *val); -gboolean gst_byte_reader_get_uint32_le (GstByteReader *reader, guint32 *val); -gboolean gst_byte_reader_get_int32_le (GstByteReader *reader, gint32 *val); -gboolean gst_byte_reader_get_uint32_be (GstByteReader *reader, guint32 *val); -gboolean gst_byte_reader_get_int32_be (GstByteReader *reader, gint32 *val); -gboolean gst_byte_reader_get_uint64_le (GstByteReader *reader, guint64 *val); -gboolean gst_byte_reader_get_int64_le (GstByteReader *reader, gint64 *val); -gboolean gst_byte_reader_get_uint64_be (GstByteReader *reader, guint64 *val); -gboolean gst_byte_reader_get_int64_be (GstByteReader *reader, gint64 *val); - -gboolean gst_byte_reader_peek_uint8 (GstByteReader *reader, guint8 *val); -gboolean gst_byte_reader_peek_int8 (GstByteReader *reader, gint8 *val); -gboolean gst_byte_reader_peek_uint16_le (GstByteReader *reader, guint16 *val); -gboolean gst_byte_reader_peek_int16_le (GstByteReader *reader, gint16 *val); -gboolean gst_byte_reader_peek_uint16_be (GstByteReader *reader, guint16 *val); -gboolean gst_byte_reader_peek_int16_be (GstByteReader *reader, gint16 *val); -gboolean gst_byte_reader_peek_uint24_le (GstByteReader *reader, guint32 *val); -gboolean gst_byte_reader_peek_int24_le (GstByteReader *reader, gint32 *val); -gboolean gst_byte_reader_peek_uint24_be (GstByteReader *reader, guint32 *val); -gboolean gst_byte_reader_peek_int24_be (GstByteReader *reader, gint32 *val); -gboolean gst_byte_reader_peek_uint32_le (GstByteReader *reader, guint32 *val); -gboolean gst_byte_reader_peek_int32_le (GstByteReader *reader, gint32 *val); -gboolean gst_byte_reader_peek_uint32_be (GstByteReader *reader, guint32 *val); -gboolean gst_byte_reader_peek_int32_be (GstByteReader *reader, gint32 *val); -gboolean gst_byte_reader_peek_uint64_le (GstByteReader *reader, guint64 *val); -gboolean gst_byte_reader_peek_int64_le (GstByteReader *reader, gint64 *val); -gboolean gst_byte_reader_peek_uint64_be (GstByteReader *reader, guint64 *val); -gboolean gst_byte_reader_peek_int64_be (GstByteReader *reader, gint64 *val); - -gboolean gst_byte_reader_get_float32_le (GstByteReader *reader, gfloat *val); -gboolean gst_byte_reader_get_float32_be (GstByteReader *reader, gfloat *val); -gboolean gst_byte_reader_get_float64_le (GstByteReader *reader, gdouble *val); -gboolean gst_byte_reader_get_float64_be (GstByteReader *reader, gdouble *val); - -gboolean gst_byte_reader_peek_float32_le (GstByteReader *reader, gfloat *val); -gboolean gst_byte_reader_peek_float32_be (GstByteReader *reader, gfloat *val); -gboolean gst_byte_reader_peek_float64_le (GstByteReader *reader, gdouble *val); -gboolean gst_byte_reader_peek_float64_be (GstByteReader *reader, gdouble *val); - -/** - * GST_BYTE_READER_INIT: - * @data: Data from which the #GstByteReader should read - * @size: Size of @data in bytes - * - * A #GstByteReader must be initialized with this macro, before it can be - * used. This macro can used be to initialize a variable, but it cannot - * be assigned to a variable. In that case you have to use - * gst_byte_reader_init(). - * - * Since: 0.10.22 - */ -#define GST_BYTE_READER_INIT(data, size) {data, size, 0} - -/** - * GST_BYTE_READER_INIT_FROM_BUFFER: - * @buffer: Buffer from which the #GstByteReader should read - * - * A #GstByteReader must be initialized with this macro, before it can be - * used. This macro can used be to initialize a variable, but it cannot - * be assigned to a variable. In that case you have to use - * gst_byte_reader_init(). - * - * Since: 0.10.22 - */ -#define GST_BYTE_READER_INIT_FROM_BUFFER(buffer) {GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 0} - -G_END_DECLS - -#endif /* __GST_BYTE_READER_H__ */ diff --git a/gst/flacparse/gstflacparse.c b/gst/flacparse/gstflacparse.c index 871082b1..d65ba062 100644 --- a/gst/flacparse/gstflacparse.c +++ b/gst/flacparse/gstflacparse.c @@ -28,8 +28,8 @@ #include <gst/tag/tag.h> #include <gst/audio/audio.h> -#include "gstbitreader.h" -#include "gstbytereader.h" +#include <gst/base/gstbitreader.h> +#include <gst/base/gstbytereader.h> GST_DEBUG_CATEGORY_STATIC (flacparse_debug); #define GST_CAT_DEFAULT flacparse_debug diff --git a/gst/mpegdemux/gstmpegdefs.h b/gst/mpegdemux/gstmpegdefs.h index 375f1dc0..40551137 100644 --- a/gst/mpegdemux/gstmpegdefs.h +++ b/gst/mpegdemux/gstmpegdefs.h @@ -147,39 +147,52 @@ * 0x0F-0x7F ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved * 0x80-0xFF User Private */ -#define ST_RESERVED 0x00 -#define ST_VIDEO_MPEG1 0x01 -#define ST_VIDEO_MPEG2 0x02 -#define ST_AUDIO_MPEG1 0x03 -#define ST_AUDIO_MPEG2 0x04 -#define ST_PRIVATE_SECTIONS 0x05 -#define ST_PRIVATE_DATA 0x06 -#define ST_MHEG 0x07 -#define ST_DSMCC 0x08 -#define ST_H222_1 0x09 +#define ST_RESERVED 0x00 +#define ST_VIDEO_MPEG1 0x01 +#define ST_VIDEO_MPEG2 0x02 +#define ST_AUDIO_MPEG1 0x03 +#define ST_AUDIO_MPEG2 0x04 +#define ST_PRIVATE_SECTIONS 0x05 +#define ST_PRIVATE_DATA 0x06 +#define ST_MHEG 0x07 +#define ST_DSMCC 0x08 +#define ST_H222_1 0x09 /* later extensions */ -#define ST_AUDIO_AAC 0x0f -#define ST_VIDEO_MPEG4 0x10 -#define ST_VIDEO_H264 0x1b +#define ST_AUDIO_AAC 0x0f +#define ST_VIDEO_MPEG4 0x10 +#define ST_VIDEO_H264 0x1b /* Un-official Dirac extension */ -#define ST_VIDEO_DIRAC 0xd1 +#define ST_VIDEO_DIRAC 0xd1 /* private stream types */ -#define ST_PS_AUDIO_AC3 0x81 -#define ST_PS_AUDIO_DTS 0x8a -#define ST_PS_AUDIO_LPCM 0x8b +#define ST_PS_AUDIO_AC3 0x81 +#define ST_PS_AUDIO_DTS 0x8a +#define ST_PS_AUDIO_LPCM 0x8b #define ST_PS_DVD_SUBPICTURE 0xff -/* Blu-ray PGS subpictures */ +/* Blu-ray related */ +#define ST_BD_AUDIO_LPCM 0x80 +#define ST_BD_AUDIO_AC3 0x81 +#define ST_BD_AUDIO_DTS 0x82 +#define ST_BD_AUDIO_AC3_TRUE_HD 0x83 +#define ST_BD_AUDIO_AC3_PLUS 0x84 +#define ST_BD_AUDIO_DTS_HD 0x85 #define ST_BD_PGS_SUBPICTURE 0x90 +#define ST_BD_IGS 0x91 +#define ST_BD_SUBTITLE 0x92 +#define ST_BD_SECONDARY_AC3_PLUS 0xa1 +#define ST_BD_SECONDARY_DTS_HD 0xa2 + +/* VC1 extension */ +#define ST_VIDEO_VC1 0xea /* HDV AUX stream mapping * 0xA0 ISO/IEC 61834-11 * 0xA1 ISO/IEC 61834-11 */ -#define ST_HDV_AUX_A 0xa0 -#define ST_HDV_AUX_V 0xa1 +#define ST_HDV_AUX_A 0xa0 +#define ST_HDV_AUX_V 0xa1 /* Un-official time-code stream */ #define ST_PS_TIMECODE 0xd2 @@ -202,16 +215,16 @@ /* sync:4 == 00xx ! pts:3 ! 1 ! pts:15 ! 1 | pts:15 ! 1 */ #define READ_TS(data, target, lost_sync_label) \ if ((*data & 0x01) != 0x01) goto lost_sync_label; \ - target = ((guint64) (*data++ & 0x0E)) << 29; \ - target |= ((guint64) (*data++ )) << 22; \ + target = ((guint64) (*data++ & 0x0E)) << 29; \ + target |= ((guint64) (*data++ )) << 22; \ if ((*data & 0x01) != 0x01) goto lost_sync_label; \ - target |= ((guint64) (*data++ & 0xFE)) << 14; \ - target |= ((guint64) (*data++ )) << 7; \ + target |= ((guint64) (*data++ & 0xFE)) << 14; \ + target |= ((guint64) (*data++ )) << 7; \ if ((*data & 0x01) != 0x01) goto lost_sync_label; \ target |= ((guint64) (*data++ & 0xFE)) >> 1; /* some extra GstFlowReturn values used internally */ -#define GST_FLOW_NEED_MORE_DATA -100 -#define GST_FLOW_LOST_SYNC -101 +#define GST_FLOW_NEED_MORE_DATA -100 +#define GST_FLOW_LOST_SYNC -101 #endif /* __GST_MPEG_DEFS_H__ */ diff --git a/gst/mpegdemux/gstmpegdemux.c b/gst/mpegdemux/gstmpegdemux.c index 75d5960f..5fe27711 100644 --- a/gst/mpegdemux/gstmpegdemux.c +++ b/gst/mpegdemux/gstmpegdemux.c @@ -53,7 +53,7 @@ #define MAX_DVD_AUDIO_STREAMS 8 #define MAX_DVD_SUBPICTURE_STREAMS 32 -#define BLOCK_SZ 4096 +#define BLOCK_SZ 32768 #define SCAN_SCR_SZ 12 #define SCAN_PTS_SZ 80 @@ -77,42 +77,6 @@ typedef enum GST_DEBUG_CATEGORY_STATIC (gstflupsdemux_debug); #define GST_CAT_DEFAULT (gstflupsdemux_debug) -#ifndef GST_CHECK_VERSION -#define GST_CHECK_VERSION(major,minor,micro) \ - (GST_VERSION_MAJOR > (major) || \ - (GST_VERSION_MAJOR == (major) && GST_VERSION_MINOR > (minor)) || \ - (GST_VERSION_MAJOR == (major) && GST_VERSION_MINOR == (minor) && \ - GST_VERSION_MICRO >= (micro))) -#endif - -#if !GST_CHECK_VERSION(0,10,9) -#define GST_BUFFER_IS_DISCONT(buffer) \ - (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) -#endif - -#if GST_CHECK_VERSION(0,10,6) -#define HAVE_NEWSEG_FULL -#else -static GstBuffer * -gst_adapter_take_buffer (GstAdapter * adapter, guint nbytes) -{ - GstBuffer *buf = NULL; - - if (G_UNLIKELY (nbytes > adapter->size)) - return NULL; - - buf = gst_buffer_new_and_alloc (nbytes); - - if (G_UNLIKELY (!buf)) - return NULL; - - /* Slow... */ - memcpy (GST_BUFFER_DATA (buf), gst_adapter_peek (adapter, nbytes), nbytes); - - return buf; -} -#endif - /* elementfactory information */ static GstElementDetails flups_demux_details = { "The Fluendo MPEG Program Stream Demuxer", @@ -176,7 +140,7 @@ static GstStaticPadTemplate audio_template = GST_PAD_SRC, GST_PAD_SOMETIMES, GST_STATIC_CAPS ("audio/mpeg, " - "mpegversion = (int) 1;" + "mpegversion = (int) { 1, 4 };" "audio/x-private1-lpcm; " "audio/x-private1-ac3;" "audio/x-private1-dts;" "audio/ac3") ); @@ -211,6 +175,7 @@ static void gst_flups_demux_loop (GstPad * pad); static gboolean gst_flups_demux_src_event (GstPad * pad, GstEvent * event); static gboolean gst_flups_demux_src_query (GstPad * pad, GstQuery * query); +static const GstQueryType *gst_flups_demux_src_query_type (GstPad * pad); static GstStateChangeReturn gst_flups_demux_change_state (GstElement * element, GstStateChange transition); @@ -220,9 +185,9 @@ 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, +static inline void gst_flups_demux_send_segment_updates (GstFluPSDemux * demux, GstClockTime new_time); -static void gst_flups_demux_clear_times (GstFluPSDemux * demux); +static inline void gst_flups_demux_clear_times (GstFluPSDemux * demux); static GstElementClass *parent_class = NULL; @@ -316,6 +281,9 @@ gst_flups_demux_init (GstFluPSDemux * demux) demux->streams = g_malloc0 (sizeof (GstFluPSStream *) * (GST_FLUPS_DEMUX_MAX_STREAMS)); + demux->streams_found = + g_malloc0 (sizeof (GstFluPSStream *) * (GST_FLUPS_DEMUX_MAX_STREAMS)); + demux->found_count = 0; } @@ -324,6 +292,7 @@ gst_flups_demux_finalize (GstFluPSDemux * demux) { gst_flups_demux_reset (demux); g_free (demux->streams); + g_free (demux->streams_found); G_OBJECT_CLASS (parent_class)->finalize (G_OBJECT (demux)); } @@ -346,6 +315,9 @@ gst_flups_demux_reset (GstFluPSDemux * demux) demux->streams[i] = NULL; } } + memset (demux->streams_found, 0, + sizeof (GstFluPSStream *) * (GST_FLUPS_DEMUX_MAX_STREAMS)); + demux->found_count = 0; p_ev = &demux->lang_codes; gst_event_replace (p_ev, NULL); @@ -402,7 +374,12 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type) case ST_PRIVATE_DATA: case ST_MHEG: case ST_DSMCC: + break; case ST_AUDIO_AAC: + template = klass->audio_template; + name = g_strdup_printf ("audio_%02x", id); + caps = gst_caps_new_simple ("audio/mpeg", + "mpegversion", G_TYPE_INT, 4, NULL); break; case ST_VIDEO_H264: template = klass->video_template; @@ -454,6 +431,8 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type) GST_DEBUG_FUNCPTR (gst_flups_demux_src_event)); gst_pad_set_query_function (stream->pad, GST_DEBUG_FUNCPTR (gst_flups_demux_src_query)); + gst_pad_set_query_type_function (stream->pad, + GST_DEBUG_FUNCPTR (gst_flups_demux_src_query_type)); gst_pad_use_fixed_caps (stream->pad); gst_pad_set_caps (stream->pad, caps); gst_caps_unref (caps); @@ -480,6 +459,7 @@ gst_flups_demux_get_stream (GstFluPSDemux * demux, gint id, gint type) gst_element_add_pad (GST_ELEMENT (demux), stream->pad); demux->streams[id] = stream; + demux->streams_found[demux->found_count++] = stream; } return stream; @@ -503,13 +483,13 @@ gst_flups_demux_send_data (GstFluPSDemux * demux, GstFluPSStream * stream, goto no_stream; /* timestamps */ - if (demux->next_pts != G_MAXUINT64) + if (G_UNLIKELY (demux->next_pts != G_MAXUINT64)) timestamp = MPEGTIME_TO_GSTTIME (demux->next_pts); else timestamp = GST_CLOCK_TIME_NONE; /* discont */ - if (stream->need_segment) { + if (G_UNLIKELY (stream->need_segment)) { gint64 time, start, stop; GstEvent *newsegment; @@ -545,7 +525,6 @@ gst_flups_demux_send_data (GstFluPSDemux * demux, GstFluPSStream * stream, else time = 0; -#ifdef HAVE_NEWSEG_FULL GST_INFO_OBJECT (demux, "sending new segment: rate %g applied_rate %g " "start: %" GST_TIME_FORMAT ", stop: %" GST_TIME_FORMAT ", time: %" GST_TIME_FORMAT " to pad %" GST_PTR_FORMAT, @@ -556,16 +535,6 @@ gst_flups_demux_send_data (GstFluPSDemux * demux, GstFluPSStream * stream, newsegment = gst_event_new_new_segment_full (FALSE, demux->sink_segment.rate, demux->sink_segment.applied_rate, GST_FORMAT_TIME, start, stop, time); -#else - GST_INFO_OBJECT (demux, "sending new segment: rate %g " - "start: %" GST_TIME_FORMAT ", stop: %" GST_TIME_FORMAT - ", time: %" GST_TIME_FORMAT " to pad %" GST_PTR_FORMAT, - demux->sink_segment.rate, GST_TIME_ARGS (start), - GST_TIME_ARGS (stop), GST_TIME_ARGS (time), stream->pad); - - newsegment = gst_event_new_new_segment (FALSE, - demux->sink_segment.rate, GST_FORMAT_TIME, start, stop, time); -#endif gst_pad_push_event (stream->pad, newsegment); @@ -628,17 +597,17 @@ no_stream: } } -static void +static inline void gst_flups_demux_mark_discont (GstFluPSDemux * demux, gboolean discont, gboolean need_segment) { - gint id; + gint i, count = demux->found_count; /* mark discont on all streams */ - for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) { - GstFluPSStream *stream = demux->streams[id]; + for (i = 0; i < count; i++) { + GstFluPSStream *stream = demux->streams_found[i]; - if (stream) { + if (G_LIKELY (stream)) { stream->discont |= discont; stream->need_segment |= need_segment; GST_DEBUG_OBJECT (demux, "marked stream as discont %d, need_segment %d", @@ -647,14 +616,14 @@ gst_flups_demux_mark_discont (GstFluPSDemux * demux, gboolean discont, } } -static gboolean +static inline gboolean gst_flups_demux_send_event (GstFluPSDemux * demux, GstEvent * event) { - gint id; + gint i, count = demux->found_count; gboolean ret = FALSE; - for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) { - GstFluPSStream *stream = demux->streams[id]; + for (i = 0; i < count; i++) { + GstFluPSStream *stream = demux->streams_found[i]; if (stream && !stream->notlinked) { (void) gst_event_ref (event); @@ -715,7 +684,7 @@ gst_flups_demux_handle_dvd_event (GstFluPSDemux * demux, GstEvent * event) break; case 0x2: case 0x3: - /* MPEG audio without and with extension stream are + /* MPEG audio without and with extension stream are * treated the same */ stream_id = 0xC0 + i; temp = gst_flups_demux_get_stream (demux, stream_id, ST_AUDIO_MPEG1); @@ -777,27 +746,27 @@ gst_flups_demux_flush (GstFluPSDemux * demux) demux->bytes_since_scr = 0; } -static void +static inline void gst_flups_demux_clear_times (GstFluPSDemux * demux) { - gint id; + gint i, count = demux->found_count; /* Clear the last ts for all streams */ - for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) { - GstFluPSStream *stream = demux->streams[id]; + for (i = 0; i < count; i++) { + GstFluPSStream *stream = demux->streams_found[i]; - if (stream) { + if (G_LIKELY (stream)) { stream->last_seg_start = stream->last_ts = GST_CLOCK_TIME_NONE; } } } -static void +static inline void gst_flups_demux_send_segment_updates (GstFluPSDemux * demux, GstClockTime new_time) { /* Advance all lagging streams by sending a segment update */ - gint id; + gint i, count = demux->found_count; GstEvent *event = NULL; /* FIXME: Handle reverse playback */ @@ -805,8 +774,8 @@ gst_flups_demux_send_segment_updates (GstFluPSDemux * demux, if (new_time > demux->src_segment.stop) return; - for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) { - GstFluPSStream *stream = demux->streams[id]; + for (i = 0; i < count; i++) { + GstFluPSStream *stream = demux->streams_found[i]; if (stream) { if (stream->last_ts == GST_CLOCK_TIME_NONE || @@ -840,17 +809,15 @@ gst_flups_demux_send_segment_updates (GstFluPSDemux * demux, gst_event_unref (event); } -static void +static inline void gst_flups_demux_close_segment (GstFluPSDemux * demux) { - gint id; + gint i, count = demux->found_count; GstEvent *event = NULL; guint64 base_time; -#if POST_10_10 GST_INFO_OBJECT (demux, "closing running segment %" GST_SEGMENT_FORMAT, &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 @@ -859,7 +826,6 @@ gst_flups_demux_close_segment (GstFluPSDemux * demux) if ((base_time = demux->base_time) == (guint64) - 1) base_time = 0; - /* Close the current segment for a linear playback */ if (demux->src_segment.rate >= 0) { /* for forward playback, we played from start to last_stop */ @@ -881,8 +847,8 @@ gst_flups_demux_close_segment (GstFluPSDemux * demux) } if (event) { - for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) { - GstFluPSStream *stream = demux->streams[id]; + for (i = 0; i < count; i++) { + GstFluPSStream *stream = demux->streams_found[i]; if (stream && !stream->notlinked && !stream->need_segment) { (void) gst_event_ref (event); @@ -905,14 +871,7 @@ gst_flups_demux_close_segment (GstFluPSDemux * demux) static inline gboolean have_open_streams (GstFluPSDemux * demux) { - gint id; - - for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) { - if (demux->streams[id]) - return TRUE; - } - - return FALSE; + return (demux->streams_found[0] != NULL); } static gboolean @@ -942,7 +901,6 @@ gst_flups_demux_sink_event (GstPad * pad, GstEvent * event) /* Close current segment */ gst_flups_demux_close_segment (demux); -#ifdef HAVE_NEWSEG_FULL { gdouble arate; @@ -960,19 +918,6 @@ gst_flups_demux_sink_event (GstPad * pad, GstEvent * event) } } -#else - gst_event_parse_new_segment (event, &update, &rate, &format, - &start, &stop, &time); - gst_segment_set_newsegment (&demux->sink_segment, update, rate, - format, start, stop, time); - if (format == GST_FORMAT_BYTES && demux->scr_rate_n != G_MAXUINT64 - && demux->scr_rate_d != G_MAXUINT64) { - - gst_segment_set_newsegment (&demux->src_segment, update, rate, - GST_FORMAT_TIME, BYTES_TO_GSTTIME (start), BYTES_TO_GSTTIME (stop), - BYTES_TO_GSTTIME (time)); - } -#endif GST_INFO_OBJECT (demux, "received new segment: rate %g " "format %d, start: %" G_GINT64_FORMAT ", stop: %" G_GINT64_FORMAT @@ -1096,10 +1041,8 @@ gst_flups_demux_do_seek (GstFluPSDemux * demux, GstSegment * seeksegment) scr = MAX (demux->first_scr, scr); fscr = scr; -#if POST_10_10 GST_INFO_OBJECT (demux, "sink segment configured %" GST_SEGMENT_FORMAT ", trying to go at SCR: %" G_GUINT64_FORMAT, &demux->sink_segment, scr); -#endif offset = MIN (gst_util_uint64_scale (scr, scr_rate_n, scr_rate_d), demux->sink_segment.stop); @@ -1156,6 +1099,7 @@ gst_flups_demux_handle_seek_pull (GstFluPSDemux * demux, GstEvent * event) if (flush) { /* Flush start up and downstream to make sure data flow and loops are idle */ + demux->flushing = TRUE; gst_flups_demux_send_event (demux, gst_event_new_flush_start ()); gst_pad_push_event (demux->sinkpad, gst_event_new_flush_start ()); } else { @@ -1168,25 +1112,22 @@ gst_flups_demux_handle_seek_pull (GstFluPSDemux * demux, GstEvent * event) if (flush) { /* Stop flushing upstream we need to pull */ + demux->flushing = FALSE; 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->src_segment, sizeof (GstSegment)); -#if POST_10_10 GST_DEBUG_OBJECT (demux, "segment before configure %" GST_SEGMENT_FORMAT, &demux->src_segment); -#endif /* Apply the seek to our segment */ gst_segment_set_seek (&seeksegment, rate, format, flags, start_type, start, stop_type, stop, &update); -#if POST_10_10 GST_DEBUG_OBJECT (demux, "seek segment configured %" GST_SEGMENT_FORMAT, &seeksegment); -#endif if (flush || seeksegment.last_stop != demux->src_segment.last_stop) { /* Do the actual seeking */ @@ -1204,10 +1145,8 @@ gst_flups_demux_handle_seek_pull (GstFluPSDemux * demux, GstEvent * event) /* update the rate in our src segment */ demux->sink_segment.rate = rate; -#if POST_10_10 GST_DEBUG_OBJECT (demux, "seek segment adjusted %" GST_SEGMENT_FORMAT, &seeksegment); -#endif if (flush) { /* Stop flushing, the sinks are at time 0 now */ @@ -1283,6 +1222,19 @@ gst_flups_demux_src_event (GstPad * pad, GstEvent * event) return res; } +static const GstQueryType * +gst_flups_demux_src_query_type (GstPad * pad) +{ + static const GstQueryType types[] = { + GST_QUERY_POSITION, + GST_QUERY_DURATION, + GST_QUERY_SEEKING, + 0 + }; + + return types; +} + static gboolean gst_flups_demux_src_query (GstPad * pad, GstQuery * query) { @@ -1349,7 +1301,7 @@ gst_flups_demux_src_query (GstPad * pad, GstQuery * query) break; } - /* Upstream didn't know, so we can only answer TIME queries from + /* Upstream didn't know, so we can only answer TIME queries from * here on */ if (format != GST_FORMAT_TIME) { GST_DEBUG_OBJECT (demux, "duration not supported for format %d", @@ -1423,7 +1375,7 @@ gst_flups_demux_src_query (GstPad * pad, GstQuery * query) * have the SCR */ peerquery = gst_query_new_seeking (GST_FORMAT_BYTES); - res = gst_pad_peer_query (demux->sinkpad, query); + res = gst_pad_peer_query (demux->sinkpad, peerquery); if (!res || demux->scr_rate_n == G_MAXUINT64 || demux->scr_rate_d == G_MAXUINT64) { gst_query_set_seeking (query, fmt, FALSE, -1, -1); @@ -1482,6 +1434,33 @@ gst_flups_demux_reset_psm (GstFluPSDemux * demux) #undef FILL_TYPE } +/* ISO/IEC 13818-1: + * pack_header() { + * pack_start_code 32 bslbf -+ + * '01' 2 bslbf | + * system_clock_reference_base [32..30] 3 bslbf | + * marker_bit 1 bslbf | + * system_clock_reference_base [29..15] 15 bslbf | + * marker_bit 1 bslbf | + * system_clock_reference_base [14..0] 15 bslbf | + * marker_bit 1 bslbf | 112 bits + * system_clock_reference_extension 9 ubslbf | + * marker_bit 1 bslbf | + * program_mux_rate 22 ubslbf | + * marker_bit 1 bslbf | + * marker_bit 1 bslbf | + * reserved 5 bslbf | + * pack_stuffing_length 3 ubslbf -+ + * + * for (i = 0; i < pack_stuffing_length; i++) { + * stuffing_byte '1111 1111' 8 bslbf + * } + * + * 112 bits = 14 bytes, as max value for pack_stuffing_length is 7, then + * in total it's needed 14 + 7 = 21 bytes. + */ +#define PACK_START_SIZE 21 + static GstFlowReturn gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) { @@ -1491,21 +1470,24 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) guint64 scr, scr_adjusted, new_rate; guint64 scr_rate_n; guint64 scr_rate_d; + guint avail = gst_adapter_available (demux->adapter); GST_DEBUG ("parsing pack start"); - /* fixed length to begin with, start code and two scr values */ - length = 8 + 4; - - if (!(data = gst_adapter_peek (demux->adapter, length))) + if (G_UNLIKELY (avail < PACK_START_SIZE)) goto need_more_data; + data = gst_adapter_peek (demux->adapter, PACK_START_SIZE); + /* skip start code */ data += 4; scr1 = GUINT32_FROM_BE (*(guint32 *) data); scr2 = GUINT32_FROM_BE (*(guint32 *) (data + 4)); + /* fixed length to begin with, start code and two scr values */ + length = 8 + 4; + /* start parsing the stream */ if ((*data & 0xc0) == 0x40) { guint32 scr_ext; @@ -1517,13 +1499,11 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) /* mpeg2 has more data */ length += 2; - if (gst_adapter_available (demux->adapter) < length) - goto need_more_data; /* :2=01 ! scr:3 ! marker:1==1 ! scr:15 ! marker:1==1 ! scr:15 */ /* check markers */ - if ((scr1 & 0xc4000400) != 0x44000400) + if (G_UNLIKELY ((scr1 & 0xc4000400) != 0x44000400)) goto lost_sync; scr = ((guint64) scr1 & 0x38000000) << 3; @@ -1532,7 +1512,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) scr |= ((guint64) scr2 & 0xf8000000) >> 27; /* marker:1==1 ! scr_ext:9 ! marker:1==1 */ - if ((scr2 & 0x04010000) != 0x04010000) + if (G_UNLIKELY ((scr2 & 0x04010000) != 0x04010000)) goto lost_sync; scr_ext = (scr2 & 0x03fe0000) >> 17; @@ -1549,7 +1529,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) data += 6; /* PMR:22 ! :2==11 ! reserved:5 ! stuffing_len:3 */ next32 = (GUINT32_FROM_BE ((*(guint32 *) data))); - if ((next32 & 0x00000300) != 0x00000300) + if (G_UNLIKELY ((next32 & 0x00000300) != 0x00000300)) goto lost_sync; new_rate = (next32 & 0xfffffc00) >> 10; @@ -1558,6 +1538,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) GST_DEBUG_OBJECT (demux, "stuffing bytes: %d", stuffing_bytes); data += 4; + length += stuffing_bytes; while (stuffing_bytes--) { if (*data++ != 0xff) goto lost_sync; @@ -1567,10 +1548,10 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) demux->is_mpeg2_pack = FALSE; /* check markers */ - if ((scr1 & 0xf1000100) != 0x21000100) + if (G_UNLIKELY ((scr1 & 0xf1000100) != 0x21000100)) goto lost_sync; - if ((scr2 & 0x01800001) != 0x01800001) + if (G_UNLIKELY ((scr2 & 0x01800001) != 0x01800001)) goto lost_sync; /* :4=0010 ! scr:3 ! marker:1==1 ! scr:15 ! marker:1==1 ! scr:15 ! marker:1==1 */ @@ -1599,14 +1580,14 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) GST_TIME_ARGS (MPEGTIME_TO_GSTTIME ((guint64) scr))); /* keep the first src in order to calculate delta time */ - if (demux->first_scr == G_MAXUINT64) { + if (G_UNLIKELY (demux->first_scr == G_MAXUINT64)) { demux->first_scr = scr; demux->first_scr_offset = demux->cur_scr_offset; demux->base_time = MPEGTIME_TO_GSTTIME (demux->first_scr); /* at begin consider the new_rate as the scr rate, bytes/clock ticks */ scr_rate_n = new_rate; scr_rate_d = CLOCK_FREQ; - } else if (demux->first_scr_offset != demux->cur_scr_offset) { + } else if (G_LIKELY (demux->first_scr_offset != demux->cur_scr_offset)) { /* estimate byte rate related to the SCR */ scr_rate_n = demux->cur_scr_offset - demux->first_scr_offset; scr_rate_d = scr_adjusted - demux->first_scr; @@ -1625,7 +1606,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) scr_rate_n, scr_rate_d, (float) scr_rate_n / scr_rate_d); /* adjustment of the SCR */ - if (demux->current_scr != G_MAXUINT64) { + if (G_LIKELY (demux->current_scr != G_MAXUINT64)) { gint64 diff; guint64 old_scr, old_mux_rate, bss, adjust = 0; @@ -1636,8 +1617,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) /* Bytes since SCR is the amount we placed in the adapter since then * (demux->bytes_since_scr) minus the amount remaining in the adapter, * clamped to >= 0 */ - bss = MAX (0, (gint) (demux->bytes_since_scr - - gst_adapter_available (demux->adapter))); + bss = MAX (0, (gint) (demux->bytes_since_scr - avail)); /* estimate the new SCR using the previous one according the notes on point 2.5.2.2 of the ISO/IEC 13818-1 document */ @@ -1656,14 +1636,14 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) /* calculate the absolute deference between the last scr and the new one */ - if (old_scr > scr_adjusted) + if (G_UNLIKELY (old_scr > scr_adjusted)) diff = old_scr - scr_adjusted; else diff = scr_adjusted - old_scr; /* if the difference is more than 1 second we need to reconfigure adjustment */ - if (diff > CLOCK_FREQ) { + if (G_UNLIKELY (diff > CLOCK_FREQ)) { demux->scr_adjust = demux->next_scr - scr; GST_DEBUG_OBJECT (demux, "discont found, diff: %" G_GINT64_FORMAT ", adjust %" G_GINT64_FORMAT, diff, demux->scr_adjust); @@ -1684,7 +1664,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) /* Reset the bytes_since_scr value to count the data remaining in the * adapter */ - demux->bytes_since_scr = gst_adapter_available (demux->adapter); + demux->bytes_since_scr = avail; gst_adapter_flush (demux->adapter, length); ADAPTER_OFFSET_FLUSH (length); @@ -1702,6 +1682,32 @@ need_more_data: } } +/* ISO/IEC 13818-1: + * system_header () { + * system_header_start_code 32 bslbf -+ + * header_length 16 uimsbf | + * marker_bit 1 bslbf | + * rate_bound 22 uimsbf | + * marker_bit 1 bslbf | + * audio_bound 6 uimsbf | + * fixed_flag 1 bslbf | + * CSPS_flag 1 bslbf | 96 bits + * system_audio_lock_flag 1 bslbf | + * system_video_lock_flag 1 bslbf | + * marker_bit 1 bslbf | + * video_bound 5 uimsbf | + * packet_rate_restriction_flag 1 bslbf | + * reserved_bits 7 bslbf -+ + * while (nextbits () = = '1') { + * stream_id 8 uimsbf -+ + * '11' 2 bslbf | 24 bits + * P-STD_buffer_bound_scale 1 bslbf | + * P-STD_buffer_size_bound 13 uimsbf -+ + * } + * } + * 96 bits = 12 bytes, 24 bits = 3 bytes. + */ + static GstFlowReturn gst_flups_demux_parse_sys_head (GstFluPSDemux * demux) { @@ -1941,7 +1947,15 @@ gst_flups_demux_parse_psm (GstFluPSDemux * demux) GST_DEBUG_OBJECT (demux, "Stream type %02X with id %02X and %u bytes info", stream_type, stream_id, stream_info_length); - demux->psm[stream_id] = stream_type; + if (G_LIKELY (stream_id != 0xbd)) + demux->psm[stream_id] = stream_type; + else { + /* Ignore stream type for private_stream_1 and discover it looking at + * the stream data. + * Fixes demuxing some clips with lpcm that was wrongly declared as + * mpeg audio */ + GST_DEBUG_OBJECT (demux, "stream type for private_stream_1 ignored"); + } es_map_base += stream_info_length; } @@ -1993,7 +2007,7 @@ gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first, if (start_code == ID_PRIVATE_STREAM_1 && datalen >= 2) { guint8 nframes; - /* VDR writes A52 streams without any header bytes + /* VDR writes A52 streams without any header bytes * (see ftp://ftp.mplayerhq.hu/MPlayer/samples/MPEG-VOB/vdr-AC3) */ if (datalen >= 4) { guint hdr = GST_READ_UINT32_BE (data); @@ -2049,7 +2063,7 @@ gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first, demux->current_stream = gst_flups_demux_get_stream (demux, id, stream_type); } - if (demux->current_stream == NULL) { + if (G_UNLIKELY (demux->current_stream == NULL)) { GST_DEBUG_OBJECT (demux, "Dropping buffer for unknown stream id 0x%02x", id); goto done; @@ -2105,7 +2119,7 @@ gst_flups_demux_resync (GstFluPSDemux * demux, gboolean save) gboolean found; avail = gst_adapter_available (demux->adapter); - if (avail < 4) + if (G_UNLIKELY (avail < 4)) goto need_data; /* Common case, read 4 bytes an check it */ @@ -2121,7 +2135,7 @@ gst_flups_demux_resync (GstFluPSDemux * demux, gboolean save) return TRUE; } - /* Otherwise, we are starting at byte 4 and we need to search + /* Otherwise, we are starting at byte 4 and we need to search the sync code in all available data in the adapter */ offset = 4; if (offset >= avail) @@ -2556,12 +2570,10 @@ gst_flups_sink_get_duration (GstFluPSDemux * demux) gst_segment_set_last_stop (&demux->src_segment, GST_FORMAT_TIME, demux->src_segment.start); } -#if POST_10_10 GST_INFO_OBJECT (demux, "sink segment configured %" GST_SEGMENT_FORMAT, &demux->sink_segment); GST_INFO_OBJECT (demux, "src segment configured %" GST_SEGMENT_FORMAT, &demux->src_segment); -#endif res = TRUE; @@ -2603,6 +2615,11 @@ gst_flups_demux_loop (GstPad * pad) demux = GST_FLUPS_DEMUX (gst_pad_get_parent (pad)); + if (G_UNLIKELY (demux->flushing)) { + ret = GST_FLOW_WRONG_STATE; + goto pause; + } + if (G_UNLIKELY (demux->sink_segment.format == GST_FORMAT_UNDEFINED)) gst_flups_sink_get_duration (demux); @@ -2851,6 +2868,10 @@ gst_flups_demux_chain (GstPad * pad, GstBuffer * buffer) save = TRUE; while (gst_flups_demux_resync (demux, save)) { gboolean ps_sync = TRUE; + if (G_UNLIKELY (demux->flushing)) { + ret = GST_FLOW_WRONG_STATE; + goto done; + } /* now switch on last synced byte */ switch (demux->last_sync_code) { diff --git a/gst/mpegdemux/gstmpegdemux.h b/gst/mpegdemux/gstmpegdemux.h index 29b3d5e5..a8822350 100644 --- a/gst/mpegdemux/gstmpegdemux.h +++ b/gst/mpegdemux/gstmpegdemux.h @@ -101,6 +101,7 @@ struct _GstFluPSDemux GstPad *sinkpad; gboolean random_access; /* If we operate in pull mode */ + gboolean flushing; GstAdapter *adapter; GstAdapter *rev_adapter; @@ -136,6 +137,8 @@ struct _GstFluPSDemux guint64 next_pts; guint64 next_dts; GstFluPSStream **streams; + GstFluPSStream **streams_found; + gint found_count; gboolean need_no_more_pads; /* Indicates an MPEG-2 stream */ diff --git a/gst/mpegdemux/gstmpegtsdemux.c b/gst/mpegdemux/gstmpegtsdemux.c index 8de7cc75..0a6f28a5 100644 --- a/gst/mpegdemux/gstmpegtsdemux.c +++ b/gst/mpegdemux/gstmpegtsdemux.c @@ -48,37 +48,17 @@ #include <string.h> #include <stdlib.h> -#ifdef USE_LIBOIL #include <liboil/liboil.h> -#endif #include "gstmpegdefs.h" #include "gstmpegtsdemux.h" #include "flutspatinfo.h" #include "flutspmtinfo.h" -#ifndef GST_CHECK_VERSION -#define GST_CHECK_VERSION(major,minor,micro) \ - (GST_VERSION_MAJOR > (major) || \ - (GST_VERSION_MAJOR == (major) && GST_VERSION_MINOR > (minor)) || \ - (GST_VERSION_MAJOR == (major) && GST_VERSION_MINOR == (minor) && \ - GST_VERSION_MICRO >= (micro))) -#endif - -#ifndef GST_BUFFER_IS_DISCONT -#define GST_BUFFER_IS_DISCONT(buffer) \ - (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) -#endif - GST_DEBUG_CATEGORY_STATIC (gstmpegtsdemux_debug); #define GST_CAT_DEFAULT (gstmpegtsdemux_debug) /* elementfactory information */ -#ifdef USE_LIBOIL -#define LONGNAME "The Fluendo MPEG Transport stream demuxer (liboil build)" -#else -#define LONGNAME "The Fluendo MPEG Transport stream demuxer" -#endif #ifndef __always_inline #if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) @@ -95,7 +75,7 @@ GST_DEBUG_CATEGORY_STATIC (gstmpegtsdemux_debug); #endif static GstElementDetails mpegts_demux_details = { - LONGNAME, + "The Fluendo MPEG Transport stream demuxer", "Codec/Demuxer", "Demultiplexes MPEG2 Transport Streams", "Wim Taymans <wim@fluendo.com>" @@ -139,7 +119,10 @@ enum "mpegversion = (int) { 1, 2, 4 }, " \ "systemstream = (boolean) FALSE; " \ "video/x-h264;" \ - "video/x-dirac" \ + "video/x-dirac;" \ + "video/x-wmv," \ + "wmvversion = (int) 3, " \ + "format = (fourcc) WVC1" \ ) #define AUDIO_CAPS \ @@ -153,8 +136,9 @@ enum "dynamic_range = (int) [ 0, 255 ], " \ "emphasis = (boolean) { FALSE, TRUE }, " \ "mute = (boolean) { FALSE, TRUE }; " \ - "audio/x-ac3;" \ - "audio/x-dts" \ + "audio/x-ac3; audio/x-eac3;" \ + "audio/x-dts;" \ + "audio/x-private1-lpcm" \ ) /* Can also use the subpicture pads for text subtitles? */ @@ -212,6 +196,7 @@ static gboolean gst_mpegts_demux_sink_setcaps (GstPad * pad, GstCaps * caps); static GstClock *gst_mpegts_demux_provide_clock (GstElement * element); static gboolean gst_mpegts_demux_src_pad_query (GstPad * pad, GstQuery * query); +static const GstQueryType *gst_mpegts_demux_src_pad_query_type (GstPad * pad); static GstStateChangeReturn gst_mpegts_demux_change_state (GstElement * element, GstStateChange transition); @@ -355,9 +340,7 @@ gst_mpegts_demux_init (GstMpegTSDemux * demux) demux->pcr[1] = -1; demux->cache_duration = GST_CLOCK_TIME_NONE; demux->base_pts = GST_CLOCK_TIME_NONE; -#ifdef USE_LIBOIL oil_init (); -#endif } static void @@ -402,7 +385,10 @@ gst_mpegts_demux_reset (GstMpegTSDemux * demux) gst_section_filter_uninit (&stream->section_filter); break; } - + if (stream->pes_buffer) { + gst_buffer_unref (stream->pes_buffer); + stream->pes_buffer = NULL; + } g_free (stream); demux->streams[i] = NULL; } @@ -577,6 +563,7 @@ gst_mpegts_stream_is_video (GstMpegTSStream * stream) case ST_VIDEO_MPEG2: case ST_VIDEO_MPEG4: case ST_VIDEO_H264: + case ST_VIDEO_VC1: return TRUE; case ST_VIDEO_DIRAC: return gst_mpegts_is_dirac_stream (stream); @@ -673,10 +660,18 @@ gst_mpegts_demux_fill_stream (GstMpegTSStream * stream, guint8 id, caps = gst_caps_new_simple ("video/x-dirac", NULL); } break; - case ST_PS_AUDIO_AC3: + case ST_VIDEO_VC1: + template = klass->video_template; + name = g_strdup_printf ("video_%04x", stream->PID); + caps = gst_caps_new_simple ("video/x-wmv", + "wmvversion", G_TYPE_INT, 3, + "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('W', 'V', 'C', '1'), + NULL); + break; + case ST_BD_AUDIO_AC3: template = klass->audio_template; name = g_strdup_printf ("audio_%04x", stream->PID); - caps = gst_caps_new_simple ("audio/x-ac3", NULL); + caps = gst_caps_new_simple ("audio/x-eac3", NULL); break; case ST_PS_AUDIO_DTS: template = klass->audio_template; @@ -688,6 +683,11 @@ gst_mpegts_demux_fill_stream (GstMpegTSStream * stream, guint8 id, name = g_strdup_printf ("audio_%04x", stream->PID); caps = gst_caps_new_simple ("audio/x-lpcm", NULL); break; + case ST_BD_AUDIO_LPCM: + template = klass->audio_template; + name = g_strdup_printf ("audio_%04x", stream->PID); + 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_%04x", stream->PID); @@ -713,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_query_type_function (stream->pad, + GST_DEBUG_FUNCPTR (gst_mpegts_demux_src_pad_query_type)); gst_pad_set_event_function (stream->pad, GST_DEBUG_FUNCPTR (gst_mpegts_demux_src_event)); g_free (name); @@ -855,10 +857,6 @@ gst_mpegts_demux_send_tags_for_stream (GstMpegTSDemux * demux, } } -#ifndef GST_FLOW_IS_SUCCESS -#define GST_FLOW_IS_SUCCESS(ret) ((ret) >= GST_FLOW_OK) -#endif - static GstFlowReturn gst_mpegts_demux_combine_flows (GstMpegTSDemux * demux, GstMpegTSStream * stream, GstFlowReturn ret) @@ -1537,11 +1535,7 @@ gst_mpegts_stream_parse_private_section (GstMpegTSStream * stream, /* just dump this down the pad */ if (gst_pad_alloc_buffer (stream->pad, 0, datalen, NULL, &buffer) == GST_FLOW_OK) { -#ifdef USE_LIBOIL oil_memcpy (buffer->data, data, datalen); -#else - memcpy (buffer->data, data, datalen); -#endif gst_pad_push (stream->pad, buffer); } @@ -2104,11 +2098,7 @@ gst_mpegts_stream_pes_buffer_push (GstMpegTSStream * stream, stream->pes_buffer_used = 0; } out_data = GST_BUFFER_DATA (stream->pes_buffer) + stream->pes_buffer_used; -#ifdef USE_LIBOIL oil_memcpy (out_data, in_data, in_size); -#else - memcpy (out_data, in_data, in_size); -#endif stream->pes_buffer_used += in_size; done: return ret; @@ -2136,11 +2126,7 @@ gst_mpegts_demux_push_fragment (GstMpegTSStream * stream, { GstFlowReturn ret; GstBuffer *es_buf = gst_buffer_new_and_alloc (in_size); -#ifdef USE_LIBOIL oil_memcpy (GST_BUFFER_DATA (es_buf), in_data, in_size); -#else - memcpy (GST_BUFFER_DATA (es_buf), in_data, in_size); -#endif ret = gst_pes_filter_push (&stream->filter, es_buf); /* If PES filter return ok then PES fragment buffering @@ -2280,11 +2266,7 @@ gst_mpegts_demux_parse_stream (GstMpegTSDemux * demux, GstMpegTSStream * stream, /* FIXME: try to use data directly instead of creating a buffer and pushing in into adapter at section filter */ sec_buf = gst_buffer_new_and_alloc (datalen); -#ifdef USE_LIBOIL oil_memcpy (GST_BUFFER_DATA (sec_buf), data, datalen); -#else - memcpy (GST_BUFFER_DATA (sec_buf), data, datalen); -#endif if (gst_section_filter_push (&stream->section_filter, payload_unit_start_indicator, continuity_counter, sec_buf)) { GST_DEBUG_OBJECT (demux, "section finished"); @@ -2701,6 +2683,19 @@ gst_mpegts_demux_provide_clock (GstElement * element) return NULL; } +static const GstQueryType * +gst_mpegts_demux_src_pad_query_type (GstPad * pad) +{ + static const GstQueryType types[] = { + GST_QUERY_LATENCY, + GST_QUERY_DURATION, + GST_QUERY_SEEKING, + 0 + }; + + return types; +} + static gboolean gst_mpegts_demux_src_pad_query (GstPad * pad, GstQuery * query) { @@ -2800,11 +2795,18 @@ gst_mpegts_demux_src_pad_query (GstPad * pad, GstQuery * query) goto beach; } + /* We can't say anything about seekability if we didn't + * have a second PCR yet because the bitrate is calculated + * from this + */ + if (demux->bitrate == -1 && demux->pcr[1] == -1) + goto beach; + /* We can seek if upstream supports BYTES seeks and we * have a bitrate */ peerquery = gst_query_new_seeking (GST_FORMAT_BYTES); - res = gst_pad_peer_query (demux->sinkpad, query); + res = gst_pad_peer_query (demux->sinkpad, peerquery); if (!res || demux->bitrate == -1) { gst_query_set_seeking (query, fmt, FALSE, -1, -1); } else { diff --git a/gst/qtmux/gstqtmux.c b/gst/qtmux/gstqtmux.c index ba155f6a..f1001966 100644 --- a/gst/qtmux/gstqtmux.c +++ b/gst/qtmux/gstqtmux.c @@ -1553,10 +1553,16 @@ gst_qt_mux_audio_sink_set_caps (GstPad * pad, GstCaps * caps) if (!gst_structure_get_int (structure, "width", &width) || !gst_structure_get_int (structure, "depth", &depth) || - !gst_structure_get_boolean (structure, "signed", &sign) || - !gst_structure_get_int (structure, "endianness", &endianness)) { - GST_DEBUG_OBJECT (qtmux, - "broken caps, width/depth/signed/endianness field missing"); + !gst_structure_get_boolean (structure, "signed", &sign)) { + GST_DEBUG_OBJECT (qtmux, "broken caps, width/depth/signed field missing"); + goto refuse_caps; + } + + if (depth <= 8) { + endianness = G_BYTE_ORDER; + } else if (!gst_structure_get_boolean (structure, + "endianness", &endianness)) { + GST_DEBUG_OBJECT (qtmux, "broken caps, endianness field missing"); goto refuse_caps; } diff --git a/gst/rawparse/Makefile.am b/gst/rawparse/Makefile.am index 88e4e8c0..b56db45b 100644 --- a/gst/rawparse/Makefile.am +++ b/gst/rawparse/Makefile.am @@ -3,7 +3,7 @@ plugin_LTLIBRARIES = libgstrawparse.la libgstrawparse_la_SOURCES = gstrawparse.c gstaudioparse.c gstvideoparse.c plugin.c libgstrawparse_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) -libgstrawparse_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) +libgstrawparse_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) -lgstvideo-@GST_MAJORMINOR@ libgstrawparse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstrawparse_la_LIBTOOLFLAGS = --tag=disable-static diff --git a/gst/rawparse/gstaudioparse.c b/gst/rawparse/gstaudioparse.c index f7eb6651..37a4f69d 100644 --- a/gst/rawparse/gstaudioparse.c +++ b/gst/rawparse/gstaudioparse.c @@ -56,22 +56,16 @@ static void gst_audio_parse_update_frame_size (GstAudioParse * ap); GST_DEBUG_CATEGORY_STATIC (gst_audio_parse_debug); #define GST_CAT_DEFAULT gst_audio_parse_debug -static const GstElementDetails gst_audio_parse_details = -GST_ELEMENT_DETAILS ("Audio Parse", - "Filter/Audio", - "Converts stream into audio frames", - "Sebastian Dröge <slomo@circular-chaos.org>"); - enum { - ARG_0, - ARG_FORMAT, - ARG_RATE, - ARG_CHANNELS, - ARG_ENDIANNESS, - ARG_WIDTH, - ARG_DEPTH, - ARG_SIGNED + PROP_0, + PROP_FORMAT, + PROP_RATE, + PROP_CHANNELS, + PROP_ENDIANNESS, + PROP_WIDTH, + PROP_DEPTH, + PROP_SIGNED }; @@ -128,7 +122,10 @@ gst_audio_parse_base_init (gpointer g_class) GST_DEBUG_CATEGORY_INIT (gst_audio_parse_debug, "audioparse", 0, "audioparse element"); - gst_element_class_set_details (gstelement_class, &gst_audio_parse_details); + gst_element_class_set_details_simple (gstelement_class, "Audio Parse", + "Filter/Audio", + "Converts stream into audio frames", + "Sebastian Dröge <sebastian.droege@collabora.co.uk>"); caps = gst_caps_from_string ("audio/x-raw-int," @@ -161,38 +158,41 @@ gst_audio_parse_class_init (GstAudioParseClass * klass) rp_class->get_caps = gst_audio_parse_get_caps; - g_object_class_install_property (gobject_class, ARG_FORMAT, + g_object_class_install_property (gobject_class, PROP_FORMAT, g_param_spec_enum ("format", "Format", "Format of audio samples in raw stream", GST_AUDIO_PARSE_FORMAT, - GST_AUDIO_PARSE_FORMAT_INT, G_PARAM_READWRITE)); + GST_AUDIO_PARSE_FORMAT_INT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_RATE, + g_object_class_install_property (gobject_class, PROP_RATE, g_param_spec_int ("rate", "Rate", "Rate of audio samples in raw stream", - 1, INT_MAX, 44100, G_PARAM_READWRITE)); + 1, INT_MAX, 44100, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_CHANNELS, + g_object_class_install_property (gobject_class, PROP_CHANNELS, g_param_spec_int ("channels", "Channels", "Number of channels in raw stream", 1, INT_MAX, 2, - G_PARAM_READWRITE)); + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_WIDTH, + g_object_class_install_property (gobject_class, PROP_WIDTH, g_param_spec_int ("width", "Width", "Width of audio samples in raw stream", 1, INT_MAX, 16, - G_PARAM_READWRITE)); + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_DEPTH, + g_object_class_install_property (gobject_class, PROP_DEPTH, g_param_spec_int ("depth", "Depth", "Depth of audio samples in raw stream", 1, INT_MAX, 16, - G_PARAM_READWRITE)); + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_SIGNED, + g_object_class_install_property (gobject_class, PROP_SIGNED, g_param_spec_boolean ("signed", "signed", - "Sign of audio samples in raw stream", TRUE, G_PARAM_READWRITE)); + "Sign of audio samples in raw stream", TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_ENDIANNESS, + g_object_class_install_property (gobject_class, PROP_ENDIANNESS, g_param_spec_enum ("endianness", "Endianness", "Endianness of audio samples in raw stream", - GST_AUDIO_PARSE_ENDIANNESS, G_BYTE_ORDER, G_PARAM_READWRITE)); + GST_AUDIO_PARSE_ENDIANNESS, G_BYTE_ORDER, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } static void @@ -218,25 +218,25 @@ gst_audio_parse_set_property (GObject * object, guint prop_id, g_return_if_fail (!gst_raw_parse_is_negotiated (GST_RAW_PARSE (ap))); switch (prop_id) { - case ARG_FORMAT: + case PROP_FORMAT: ap->format = g_value_get_enum (value); break; - case ARG_RATE: + case PROP_RATE: gst_raw_parse_set_fps (GST_RAW_PARSE (ap), g_value_get_int (value), 1); break; - case ARG_CHANNELS: + case PROP_CHANNELS: ap->channels = g_value_get_int (value); break; - case ARG_WIDTH: + case PROP_WIDTH: ap->width = g_value_get_int (value); break; - case ARG_DEPTH: + case PROP_DEPTH: ap->depth = g_value_get_int (value); break; - case ARG_SIGNED: + case PROP_SIGNED: ap->signedness = g_value_get_boolean (value); break; - case ARG_ENDIANNESS: + case PROP_ENDIANNESS: ap->endianness = g_value_get_enum (value); break; default: @@ -254,29 +254,29 @@ gst_audio_parse_get_property (GObject * object, guint prop_id, GValue * value, GstAudioParse *ap = GST_AUDIO_PARSE (object); switch (prop_id) { - case ARG_FORMAT: + case PROP_FORMAT: g_value_set_enum (value, ap->format); break; - case ARG_RATE:{ + case PROP_RATE:{ gint fps_n, fps_d; gst_raw_parse_get_fps (GST_RAW_PARSE (ap), &fps_n, &fps_d); g_value_set_int (value, fps_n); break; } - case ARG_CHANNELS: + case PROP_CHANNELS: g_value_set_int (value, ap->channels); break; - case ARG_WIDTH: + case PROP_WIDTH: g_value_set_int (value, ap->width); break; - case ARG_DEPTH: + case PROP_DEPTH: g_value_set_int (value, ap->depth); break; - case ARG_SIGNED: + case PROP_SIGNED: g_value_set_boolean (value, ap->signedness); break; - case ARG_ENDIANNESS: + case PROP_ENDIANNESS: g_value_set_enum (value, ap->endianness); break; default: diff --git a/gst/rawparse/gstrawparse.c b/gst/rawparse/gstrawparse.c index d5dae600..07e42c28 100644 --- a/gst/rawparse/gstrawparse.c +++ b/gst/rawparse/gstrawparse.c @@ -100,8 +100,6 @@ gst_raw_parse_init (GstRawParse * rp, GstRawParseClass * g_class) rp->sinkpad = gst_pad_new_from_static_template (&gst_raw_parse_sink_pad_template, "sink"); - gst_element_add_pad (GST_ELEMENT (rp), rp->sinkpad); - gst_pad_set_chain_function (rp->sinkpad, GST_DEBUG_FUNCPTR (gst_raw_parse_chain)); gst_pad_set_event_function (rp->sinkpad, @@ -110,6 +108,7 @@ gst_raw_parse_init (GstRawParse * rp, GstRawParseClass * g_class) GST_DEBUG_FUNCPTR (gst_raw_parse_sink_activate)); gst_pad_set_activatepull_function (rp->sinkpad, GST_DEBUG_FUNCPTR (gst_raw_parse_sink_activatepull)); + gst_element_add_pad (GST_ELEMENT (rp), rp->sinkpad); src_pad_template = gst_element_class_get_pad_template (element_class, "src"); @@ -120,15 +119,13 @@ gst_raw_parse_init (GstRawParse * rp, GstRawParseClass * g_class) g_assert_not_reached (); } - gst_element_add_pad (GST_ELEMENT (rp), rp->srcpad); - gst_pad_set_event_function (rp->srcpad, GST_DEBUG_FUNCPTR (gst_raw_parse_src_event)); - gst_pad_set_query_type_function (rp->srcpad, GST_DEBUG_FUNCPTR (gst_raw_parse_src_query_type)); gst_pad_set_query_function (rp->srcpad, GST_DEBUG_FUNCPTR (gst_raw_parse_src_query)); + gst_element_add_pad (GST_ELEMENT (rp), rp->srcpad); rp->adapter = gst_adapter_new (); @@ -914,6 +911,7 @@ gst_raw_parse_src_query_type (GstPad * pad) GST_QUERY_POSITION, GST_QUERY_DURATION, GST_QUERY_CONVERT, + GST_QUERY_SEEKING, 0 }; @@ -992,6 +990,29 @@ gst_raw_parse_src_query (GstPad * pad, GstQuery * query) gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); break; } + case GST_QUERY_SEEKING:{ + GstFormat fmt; + + ret = TRUE; + gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL); + if (fmt != GST_FORMAT_TIME && fmt != GST_FORMAT_DEFAULT + && fmt != GST_FORMAT_BYTES) { + gst_query_set_seeking (query, fmt, FALSE, -1, -1); + } else if (rp->mode == GST_ACTIVATE_PUSH) { + GstQuery *peerquery = gst_query_new_seeking (GST_FORMAT_BYTES); + gboolean seekable; + + seekable = gst_pad_peer_query (rp->sinkpad, peerquery); + if (seekable) + gst_query_parse_seeking (peerquery, NULL, &seekable, NULL, NULL); + + gst_query_unref (peerquery); + gst_query_set_seeking (query, fmt, seekable, seekable ? 0 : -1, -1); + } else { + gst_query_set_seeking (query, fmt, TRUE, 0, -1); + } + break; + } default: /* else forward upstream */ ret = gst_pad_peer_query (rp->sinkpad, query); diff --git a/gst/rawparse/gstrawparse.h b/gst/rawparse/gstrawparse.h index da0de444..0a2c01b3 100644 --- a/gst/rawparse/gstrawparse.h +++ b/gst/rawparse/gstrawparse.h @@ -48,16 +48,16 @@ struct _GstRawParse { GstElement parent; - /* private */ + /* <private> */ GstPad *sinkpad; GstPad *srcpad; GstActivateMode mode; GstAdapter *adapter; - int framesize; - int fps_d; - int fps_n; + gint framesize; + gint fps_d; + gint fps_n; gboolean discont; guint64 n_frames; diff --git a/gst/rawparse/gstvideoparse.c b/gst/rawparse/gstvideoparse.c index d245fa75..16dde999 100644 --- a/gst/rawparse/gstvideoparse.c +++ b/gst/rawparse/gstvideoparse.c @@ -1,6 +1,6 @@ /* GStreamer * Copyright (C) 2006 David A. Schleef <ds@schleef.org> - * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org> + * Copyright (C) 2007,2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> * * gstvideoparse.c: * @@ -31,23 +31,6 @@ #include "gstvideoparse.h" -typedef enum -{ - GST_VIDEO_PARSE_FORMAT_I420, - GST_VIDEO_PARSE_FORMAT_YV12, - GST_VIDEO_PARSE_FORMAT_YUY2, - GST_VIDEO_PARSE_FORMAT_UYVY, - GST_VIDEO_PARSE_FORMAT_v210, - GST_VIDEO_PARSE_FORMAT_RGB = 10, - GST_VIDEO_PARSE_FORMAT_GRAY -} GstVideoParseFormat; - -typedef enum -{ - GST_VIDEO_PARSE_ENDIANNESS_LITTLE = 1234, - GST_VIDEO_PARSE_ENDIANNESS_BIG = 4321 -} GstVideoParseEndianness; - static void gst_video_parse_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_video_parse_get_property (GObject * object, guint prop_id, @@ -62,76 +45,18 @@ static void gst_video_parse_update_frame_size (GstVideoParse * vp); GST_DEBUG_CATEGORY_STATIC (gst_video_parse_debug); #define GST_CAT_DEFAULT gst_video_parse_debug -static const GstElementDetails gst_video_parse_details = -GST_ELEMENT_DETAILS ("Video Parse", - "Filter/Video", - "Converts stream into video frames", - "David Schleef <ds@schleef.org>, " - "Sebastian Dröge <slomo@circular-chaos.org>"); - enum { - ARG_0, - ARG_WIDTH, - ARG_HEIGHT, - ARG_FORMAT, - ARG_PAR, - ARG_FRAMERATE, - ARG_INTERLACED, - ARG_TOP_FIELD_FIRST, - ARG_BPP, - ARG_DEPTH, - ARG_ENDIANNESS, - ARG_RED_MASK, - ARG_GREEN_MASK, - ARG_BLUE_MASK, - ARG_ALPHA_MASK + PROP_0, + PROP_FORMAT, + PROP_WIDTH, + PROP_HEIGHT, + PROP_PAR, + PROP_FRAMERATE, + PROP_INTERLACED, + PROP_TOP_FIELD_FIRST }; - -#define GST_VIDEO_PARSE_FORMAT (gst_video_parse_format_get_type ()) -static GType -gst_video_parse_format_get_type (void) -{ - static GType video_parse_format_type = 0; - static const GEnumValue format_types[] = { - {GST_VIDEO_PARSE_FORMAT_I420, "I420", "I420"}, - {GST_VIDEO_PARSE_FORMAT_YV12, "YV12", "YV12"}, - {GST_VIDEO_PARSE_FORMAT_YUY2, "YUY2", "YUY2"}, - {GST_VIDEO_PARSE_FORMAT_UYVY, "UYVY", "UYVY"}, - {GST_VIDEO_PARSE_FORMAT_v210, "v210", "v210"}, - {GST_VIDEO_PARSE_FORMAT_RGB, "RGB", "RGB"}, - {GST_VIDEO_PARSE_FORMAT_GRAY, "GRAY", "GRAY"}, - {0, NULL, NULL} - }; - - if (!video_parse_format_type) { - video_parse_format_type = - g_enum_register_static ("GstVideoParseFormat", format_types); - } - - return video_parse_format_type; -} - -#define GST_VIDEO_PARSE_ENDIANNESS (gst_video_parse_endianness_get_type ()) -static GType -gst_video_parse_endianness_get_type (void) -{ - static GType video_parse_endianness_type = 0; - static const GEnumValue endian_types[] = { - {GST_VIDEO_PARSE_ENDIANNESS_LITTLE, "Little Endian", "little"}, - {GST_VIDEO_PARSE_ENDIANNESS_BIG, "Big Endian", "big"}, - {0, NULL, NULL} - }; - - if (!video_parse_endianness_type) { - video_parse_endianness_type = - g_enum_register_static ("GstVideoParseEndianness", endian_types); - } - - return video_parse_endianness_type; -} - GST_BOILERPLATE (GstVideoParse, gst_video_parse, GstRawParse, GST_TYPE_RAW_PARSE); @@ -145,12 +70,15 @@ gst_video_parse_base_init (gpointer g_class) GST_DEBUG_CATEGORY_INIT (gst_video_parse_debug, "videoparse", 0, "videoparse element"); - gst_element_class_set_details (gstelement_class, &gst_video_parse_details); + gst_element_class_set_details_simple (gstelement_class, "Video Parse", + "Filter/Video", + "Converts stream into video frames", + "David Schleef <ds@schleef.org>, " + "Sebastian Dröge <sebastian.droege@collabora.co.uk>"); caps = - gst_caps_from_string (GST_VIDEO_CAPS_YUV - ("{ I420, YV12, YUY2, UYVY, v210 }") ";" - "video/x-raw-rgb; video/x-raw-gray"); + gst_caps_from_string + ("video/x-raw-rgb; video/x-raw-yuv; video/x-raw-gray; video/x-raw-bayer"); gst_raw_parse_class_set_src_pad_template (rp_class, caps); gst_raw_parse_class_set_multiple_frames_per_buffer (rp_class, FALSE); @@ -169,57 +97,32 @@ gst_video_parse_class_init (GstVideoParseClass * klass) rp_class->get_caps = gst_video_parse_get_caps; rp_class->set_buffer_flags = gst_video_parse_set_buffer_flags; - g_object_class_install_property (gobject_class, ARG_WIDTH, + g_object_class_install_property (gobject_class, PROP_FORMAT, + g_param_spec_enum ("format", "Format", "Format of images in raw stream", + GST_TYPE_VIDEO_FORMAT, GST_VIDEO_FORMAT_I420, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_WIDTH, g_param_spec_int ("width", "Width", "Width of images in raw stream", - 0, INT_MAX, 320, G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_HEIGHT, + 0, INT_MAX, 320, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_HEIGHT, g_param_spec_int ("height", "Height", "Height of images in raw stream", - 0, INT_MAX, 240, G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_FORMAT, - g_param_spec_enum ("format", "Format", "Format of images in raw stream", - GST_VIDEO_PARSE_FORMAT, GST_VIDEO_PARSE_FORMAT_I420, - G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_FRAMERATE, + 0, INT_MAX, 240, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_FRAMERATE, gst_param_spec_fraction ("framerate", "Frame Rate", "Frame rate of images in raw stream", 0, 1, 100, 1, 25, 1, - G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_INTERLACED, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_PAR, + gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio", + "Pixel aspect ratio of images in raw stream", 1, 100, 100, 1, 1, 1, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_INTERLACED, g_param_spec_boolean ("interlaced", "Interlaced flag", - "True if video is interlaced", FALSE, G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_TOP_FIELD_FIRST, + "True if video is interlaced", FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_TOP_FIELD_FIRST, g_param_spec_boolean ("top-field-first", "Top field first", "True if top field is earlier than bottom field", TRUE, - G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_PAR, - gst_param_spec_fraction ("pixel_aspect_ratio", "Pixel Aspect Ratio", - "Pixel aspect ratio of images in raw stream", 1, 100, 100, 1, 1, 1, - G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_BPP, - g_param_spec_int ("bpp", "Bpp", "Bits per pixel of images in raw stream", - 0, INT_MAX, 24, G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_DEPTH, - g_param_spec_int ("depth", "Depth", "Depth of images in raw stream", 0, - INT_MAX, 24, G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_ENDIANNESS, - g_param_spec_enum ("endianness", "Endianness", - "Endianness of images in raw stream", GST_VIDEO_PARSE_ENDIANNESS, - G_BYTE_ORDER, G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_RED_MASK, - g_param_spec_int ("red-mask", "Red mask", - "Red mask of images in raw stream", INT_MIN, INT_MAX, - GST_VIDEO_BYTE1_MASK_24_INT, G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_GREEN_MASK, - g_param_spec_int ("green-mask", "Green mask", - "Green mask of images in raw stream", INT_MIN, INT_MAX, - GST_VIDEO_BYTE2_MASK_24_INT, G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_BLUE_MASK, - g_param_spec_int ("blue-mask", "Blue mask", - "Blue mask of images in raw stream", INT_MIN, INT_MAX, - GST_VIDEO_BYTE3_MASK_24_INT, G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_ALPHA_MASK, - g_param_spec_int ("alpha-mask", "Alpha mask", - "Alpha mask of images in raw stream", INT_MIN, INT_MAX, 0, - G_PARAM_READWRITE)); + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } static void @@ -227,16 +130,9 @@ gst_video_parse_init (GstVideoParse * vp, GstVideoParseClass * g_class) { vp->width = 320; vp->height = 240; - vp->format = GST_VIDEO_PARSE_FORMAT_I420; + vp->format = GST_VIDEO_FORMAT_I420; vp->par_n = 1; vp->par_d = 1; - vp->bpp = 24; - vp->depth = 24; - vp->endianness = G_BYTE_ORDER; - vp->red_mask = GST_VIDEO_BYTE1_MASK_24_INT; - vp->green_mask = GST_VIDEO_BYTE2_MASK_24_INT; - vp->blue_mask = GST_VIDEO_BYTE3_MASK_24_INT; - vp->alpha_mask = 0; gst_video_parse_update_frame_size (vp); gst_raw_parse_set_fps (GST_RAW_PARSE (vp), 25, 1); @@ -251,50 +147,29 @@ gst_video_parse_set_property (GObject * object, guint prop_id, g_return_if_fail (!gst_raw_parse_is_negotiated (GST_RAW_PARSE (vp))); switch (prop_id) { - case ARG_WIDTH: + case PROP_FORMAT: + vp->format = g_value_get_enum (value); + break; + case PROP_WIDTH: vp->width = g_value_get_int (value); break; - case ARG_HEIGHT: + case PROP_HEIGHT: vp->height = g_value_get_int (value); break; - case ARG_FORMAT: - vp->format = g_value_get_enum (value); - break; - case ARG_FRAMERATE: + case PROP_FRAMERATE: gst_raw_parse_set_fps (GST_RAW_PARSE (vp), gst_value_get_fraction_numerator (value), gst_value_get_fraction_denominator (value)); break; - case ARG_INTERLACED: - vp->interlaced = g_value_get_boolean (value); - break; - case ARG_TOP_FIELD_FIRST: - vp->top_field_first = g_value_get_boolean (value); - break; - case ARG_PAR: + case PROP_PAR: vp->par_n = gst_value_get_fraction_numerator (value); vp->par_d = gst_value_get_fraction_denominator (value); break; - case ARG_BPP: - vp->bpp = g_value_get_int (value); - break; - case ARG_DEPTH: - vp->depth = g_value_get_int (value); - break; - case ARG_ENDIANNESS: - vp->endianness = g_value_get_enum (value); - break; - case ARG_RED_MASK: - vp->red_mask = g_value_get_int (value); - break; - case ARG_GREEN_MASK: - vp->green_mask = g_value_get_int (value); - break; - case ARG_BLUE_MASK: - vp->blue_mask = g_value_get_int (value); + case PROP_INTERLACED: + vp->interlaced = g_value_get_boolean (value); break; - case ARG_ALPHA_MASK: - vp->alpha_mask = g_value_get_int (value); + case PROP_TOP_FIELD_FIRST: + vp->top_field_first = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -311,51 +186,30 @@ gst_video_parse_get_property (GObject * object, guint prop_id, GValue * value, GstVideoParse *vp = GST_VIDEO_PARSE (object); switch (prop_id) { - case ARG_WIDTH: + case PROP_FORMAT: + g_value_set_enum (value, vp->format); + break; + case PROP_WIDTH: g_value_set_int (value, vp->width); break; - case ARG_HEIGHT: + case PROP_HEIGHT: g_value_set_int (value, vp->height); break; - case ARG_FORMAT: - g_value_set_enum (value, vp->format); - break; - case ARG_FRAMERATE:{ + case PROP_FRAMERATE:{ gint fps_n, fps_d; gst_raw_parse_get_fps (GST_RAW_PARSE (vp), &fps_n, &fps_d); gst_value_set_fraction (value, fps_n, fps_d); break; } - case ARG_INTERLACED: - g_value_set_boolean (value, vp->interlaced); - break; - case ARG_TOP_FIELD_FIRST: - g_value_set_boolean (value, vp->top_field_first); - break; - case ARG_PAR: + case PROP_PAR: gst_value_set_fraction (value, vp->par_n, vp->par_d); break; - case ARG_BPP: - g_value_set_int (value, vp->bpp); - break; - case ARG_DEPTH: - g_value_set_int (value, vp->depth); - break; - case ARG_ENDIANNESS: - g_value_set_enum (value, vp->endianness); - break; - case ARG_RED_MASK: - g_value_set_int (value, vp->red_mask); - break; - case ARG_GREEN_MASK: - g_value_set_int (value, vp->green_mask); - break; - case ARG_BLUE_MASK: - g_value_set_int (value, vp->blue_mask); + case PROP_INTERLACED: + g_value_set_boolean (value, vp->interlaced); break; - case ARG_ALPHA_MASK: - g_value_set_int (value, vp->alpha_mask); + case PROP_TOP_FIELD_FIRST: + g_value_set_boolean (value, vp->top_field_first); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -363,47 +217,12 @@ gst_video_parse_get_property (GObject * object, guint prop_id, GValue * value, } } -static guint32 -gst_video_parse_format_to_fourcc (GstVideoParseFormat format) -{ - switch (format) { - case GST_VIDEO_PARSE_FORMAT_I420: - return GST_MAKE_FOURCC ('I', '4', '2', '0'); - case GST_VIDEO_PARSE_FORMAT_YV12: - return GST_MAKE_FOURCC ('Y', 'V', '1', '2'); - case GST_VIDEO_PARSE_FORMAT_YUY2: - return GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'); - case GST_VIDEO_PARSE_FORMAT_UYVY: - return GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'); - case GST_VIDEO_PARSE_FORMAT_v210: - return GST_MAKE_FOURCC ('v', '2', '1', '0'); - default: - g_assert_not_reached (); - } - return 0; -} - void gst_video_parse_update_frame_size (GstVideoParse * vp) { gint framesize; - if (vp->format == GST_VIDEO_PARSE_FORMAT_I420 || - vp->format == GST_VIDEO_PARSE_FORMAT_YV12) { - framesize = GST_ROUND_UP_4 (vp->width) * GST_ROUND_UP_2 (vp->height) - + - 2 * (GST_ROUND_UP_8 (vp->width) / 2) * (GST_ROUND_UP_2 (vp->height) / - 2); - } else if (vp->format == GST_VIDEO_PARSE_FORMAT_YUY2 - || vp->format == GST_VIDEO_PARSE_FORMAT_UYVY) { - framesize = GST_ROUND_UP_4 (vp->width * 2) * vp->height; - } else if (vp->format == GST_VIDEO_PARSE_FORMAT_v210) { - framesize = ((vp->width + 47) / 48) * 128 * vp->height; - } else if (vp->format == GST_VIDEO_PARSE_FORMAT_RGB) { - framesize = GST_ROUND_UP_4 (vp->width * vp->bpp / 8) * vp->height; - } else { - framesize = GST_ROUND_UP_4 (vp->width * vp->bpp / 8) * vp->height; - } + framesize = gst_video_format_get_size (vp->format, vp->width, vp->height); gst_raw_parse_set_framesize (GST_RAW_PARSE (vp), framesize); } @@ -418,39 +237,10 @@ gst_video_parse_get_caps (GstRawParse * rp) gst_raw_parse_get_fps (rp, &fps_n, &fps_d); - if (vp->format < GST_VIDEO_PARSE_FORMAT_RGB) { - caps = gst_caps_new_simple ("video/x-raw-yuv", - "width", G_TYPE_INT, vp->width, - "height", G_TYPE_INT, vp->height, - "format", GST_TYPE_FOURCC, - gst_video_parse_format_to_fourcc (vp->format), "framerate", - GST_TYPE_FRACTION, fps_n, fps_d, "pixel-aspect-ratio", - GST_TYPE_FRACTION, vp->par_n, vp->par_d, - "interlaced", G_TYPE_BOOLEAN, vp->interlaced, NULL); - } else if (vp->format == GST_VIDEO_PARSE_FORMAT_RGB) { - caps = gst_caps_new_simple ("video/x-raw-rgb", - "width", G_TYPE_INT, vp->width, - "height", G_TYPE_INT, vp->height, - "bpp", G_TYPE_INT, vp->bpp, - "depth", G_TYPE_INT, vp->depth, - "framerate", GST_TYPE_FRACTION, fps_n, fps_d, - "interlaced", G_TYPE_BOOLEAN, vp->interlaced, - "pixel-aspect-ratio", GST_TYPE_FRACTION, vp->par_n, vp->par_d, - "red_mask", G_TYPE_INT, vp->red_mask, - "green_mask", G_TYPE_INT, vp->green_mask, - "blue_mask", G_TYPE_INT, vp->blue_mask, - "alpha_mask", G_TYPE_INT, vp->alpha_mask, - "endianness", G_TYPE_INT, vp->endianness, NULL); - } else { - caps = gst_caps_new_simple ("video/x-raw-gray", - "width", G_TYPE_INT, vp->width, - "height", G_TYPE_INT, vp->height, - "bpp", G_TYPE_INT, vp->bpp, - "depth", G_TYPE_INT, vp->depth, - "framerate", GST_TYPE_FRACTION, fps_n, fps_d, - "interlaced", G_TYPE_BOOLEAN, vp->interlaced, - "pixel-aspect-ratio", GST_TYPE_FRACTION, vp->par_n, vp->par_d, NULL); - } + caps = + gst_video_format_new_caps_interlaced (vp->format, vp->width, vp->height, + fps_n, fps_d, vp->par_n, vp->par_d, vp->interlaced); + return caps; } @@ -459,10 +249,6 @@ gst_video_parse_set_buffer_flags (GstRawParse * rp, GstBuffer * buffer) { GstVideoParse *vp = GST_VIDEO_PARSE (rp); -/* remove this after -bad deps on -base-0.10.23 */ -#ifndef GST_VIDEO_BUFFER_TFF -#define GST_VIDEO_BUFFER_TFF (GST_MINI_OBJECT_FLAG_LAST << 5) -#endif if (vp->interlaced) { if (vp->top_field_first) { GST_BUFFER_FLAG_SET (buffer, GST_VIDEO_BUFFER_TFF); diff --git a/gst/rawparse/gstvideoparse.h b/gst/rawparse/gstvideoparse.h index da210ea1..d2e2b363 100644 --- a/gst/rawparse/gstvideoparse.h +++ b/gst/rawparse/gstvideoparse.h @@ -1,6 +1,6 @@ /* GStreamer * Copyright (C) 2006 David A. Schleef <ds@schleef.org> - * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org> + * Copyright (C) 2007,2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> * * gstvideoparse.h: * @@ -49,20 +49,12 @@ struct _GstVideoParse GstRawParse parent; /* properties */ - int width; - int height; - guint32 format; - int par_n; - int par_d; + GstVideoFormat format; + gint width; + gint height; + gint par_n, par_d; gboolean interlaced; gboolean top_field_first; - int bpp; - int depth; - int endianness; - int red_mask; - int blue_mask; - int green_mask; - int alpha_mask; }; struct _GstVideoParseClass diff --git a/gst/rtpmanager/rtpsession.c b/gst/rtpmanager/rtpsession.c index d63d9d0e..3e17ec12 100644 --- a/gst/rtpmanager/rtpsession.c +++ b/gst/rtpmanager/rtpsession.c @@ -1577,7 +1577,7 @@ rtp_session_process_rb (RTPSession * sess, RTPSource * source, */ static void rtp_session_process_sr (RTPSession * sess, GstRTCPPacket * packet, - RTPArrivalStats * arrival) + RTPArrivalStats * arrival, gboolean * do_sync) { guint32 senderssrc, rtptime, packet_count, octet_count; guint64 ntptime; @@ -1594,6 +1594,12 @@ rtp_session_process_sr (RTPSession * sess, GstRTCPPacket * packet, if (!source) return; + /* don't try to do lip-sync for sources that sent a BYE */ + if (rtp_source_received_bye (source)) + *do_sync = FALSE; + else + *do_sync = TRUE; + prevsender = RTP_SOURCE_IS_SENDER (source); /* first update the source */ @@ -1711,6 +1717,7 @@ rtp_session_process_bye (RTPSession * sess, GstRTCPPacket * packet, { guint count, i; gchar *reason; + gboolean reconsider = FALSE; reason = gst_rtcp_packet_bye_get_reason (packet); GST_DEBUG ("got BYE packet (reason: %s)", GST_STR_NULL (reason)); @@ -1769,11 +1776,9 @@ rtp_session_process_bye (RTPSession * sess, GstRTCPPacket * packet, sess->next_rtcp_check_time += arrival->time; - RTP_SESSION_UNLOCK (sess); - /* notify app of reconsideration */ - if (sess->callbacks.reconsider) - sess->callbacks.reconsider (sess, sess->reconsider_user_data); - RTP_SESSION_LOCK (sess); + /* mark pending reconsider. We only want to signal the reconsideration + * once after we handled all the source in the bye packet */ + reconsider = TRUE; } } @@ -1784,6 +1789,13 @@ rtp_session_process_bye (RTPSession * sess, GstRTCPPacket * packet, g_object_unref (source); } + if (reconsider) { + RTP_SESSION_UNLOCK (sess); + /* notify app of reconsideration */ + if (sess->callbacks.reconsider) + sess->callbacks.reconsider (sess, sess->reconsider_user_data); + RTP_SESSION_LOCK (sess); + } g_free (reason); } @@ -1810,7 +1822,7 @@ rtp_session_process_rtcp (RTPSession * sess, GstBuffer * buffer, GstClockTime current_time) { GstRTCPPacket packet; - gboolean more, is_bye = FALSE, is_sr = FALSE; + gboolean more, is_bye = FALSE, do_sync = FALSE; RTPArrivalStats arrival; GstFlowReturn result = GST_FLOW_OK; @@ -1847,8 +1859,7 @@ rtp_session_process_rtcp (RTPSession * sess, GstBuffer * buffer, switch (type) { case GST_RTCP_TYPE_SR: - rtp_session_process_sr (sess, &packet, &arrival); - is_sr = TRUE; + rtp_session_process_sr (sess, &packet, &arrival, &do_sync); break; case GST_RTCP_TYPE_RR: rtp_session_process_rr (sess, &packet, &arrival); @@ -1858,6 +1869,8 @@ rtp_session_process_rtcp (RTPSession * sess, GstBuffer * buffer, break; case GST_RTCP_TYPE_BYE: is_bye = TRUE; + /* don't try to attempt lip-sync anymore for streams with a BYE */ + do_sync = FALSE; rtp_session_process_bye (sess, &packet, &arrival); break; case GST_RTCP_TYPE_APP: @@ -1885,7 +1898,7 @@ rtp_session_process_rtcp (RTPSession * sess, GstBuffer * buffer, RTP_SESSION_UNLOCK (sess); /* notify caller of sr packets in the callback */ - if (is_sr && sess->callbacks.sync_rtcp) + if (do_sync && sess->callbacks.sync_rtcp) result = sess->callbacks.sync_rtcp (sess, sess->source, buffer, sess->sync_rtcp_user_data); else diff --git a/gst/rtpmanager/rtpsource.c b/gst/rtpmanager/rtpsource.c index 40cdd238..28fa23ef 100644 --- a/gst/rtpmanager/rtpsource.c +++ b/gst/rtpmanager/rtpsource.c @@ -628,7 +628,7 @@ rtp_source_update_caps (RTPSource * src, GstCaps * caps) gint ival; /* nothing changed, return */ - if (src->caps == caps) + if (caps == NULL || src->caps == caps) return; s = gst_caps_get_structure (caps, 0); @@ -1074,6 +1074,7 @@ done: bad_sequence: { GST_WARNING ("unacceptable seqnum received"); + gst_buffer_unref (buffer); return GST_FLOW_OK; } } diff --git a/gst/rtpmux/gstrtpmux.c b/gst/rtpmux/gstrtpmux.c index 4c2bea09..23a6c7e7 100644 --- a/gst/rtpmux/gstrtpmux.c +++ b/gst/rtpmux/gstrtpmux.c @@ -83,8 +83,6 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%d", GST_STATIC_CAPS ("application/x-rtp") ); -static void gst_rtp_mux_finalize (GObject * object); - static GstPad *gst_rtp_mux_request_new_pad (GstElement * element, GstPadTemplate * templ, const gchar * name); static void gst_rtp_mux_release_pad (GstElement * element, GstPad * pad); @@ -125,7 +123,6 @@ gst_rtp_mux_class_init (GstRTPMuxClass * klass) gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; - gobject_class->finalize = gst_rtp_mux_finalize; gobject_class->get_property = gst_rtp_mux_get_property; gobject_class->set_property = gst_rtp_mux_set_property; @@ -211,16 +208,6 @@ gst_rtp_mux_init (GstRTPMux * object, GstRTPMuxClass * g_class) object->seqnum_offset = DEFAULT_SEQNUM_OFFSET; } -static void -gst_rtp_mux_finalize (GObject * object) -{ - GstRTPMux *rtp_mux; - - rtp_mux = GST_RTP_MUX (object); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - static GstPad * gst_rtp_mux_create_sinkpad (GstRTPMux * rtp_mux, GstPadTemplate * templ) { diff --git a/gst/selector/gstoutputselector.c b/gst/selector/gstoutputselector.c index 15c0a726..f5c89eef 100644 --- a/gst/selector/gstoutputselector.c +++ b/gst/selector/gstoutputselector.c @@ -238,7 +238,8 @@ gst_output_selector_get_property (GObject * object, guint prop_id, switch (prop_id) { case PROP_ACTIVE_PAD: GST_OBJECT_LOCK (object); - g_value_set_object (value, sel->active_srcpad); + g_value_set_object (value, + sel->pending_srcpad ? sel->pending_srcpad : sel->active_srcpad); GST_OBJECT_UNLOCK (object); break; case PROP_RESEND_LATEST:{ diff --git a/sys/Makefile.am b/sys/Makefile.am index 15c89e17..5608a8e7 100644 --- a/sys/Makefile.am +++ b/sys/Makefile.am @@ -28,6 +28,12 @@ else DIRECTDRAW_DIR= endif +if USE_DIRECTSOUND +DIRECTSOUND_DIR=directsound +else +DIRECTSOUND_DIR= +endif + if USE_FBDEV FBDEV_DIR=fbdev else @@ -76,8 +82,8 @@ else VDPAU_DIR= endif -SUBDIRS = $(ACM_DIR) $(DIRECTDRAW_DIR) $(DVB_DIR) $(FBDEV_DIR) $(OSS4_DIR) $(OSX_VIDEO_DIR) $(QT_DIR) $(VCD_DIR) $(VDPAU_DIR) $(WININET_DIR) +SUBDIRS = $(ACM_DIR) $(DIRECTDRAW_DIR) $(DIRECTSOUND_DIR) $(DVB_DIR) $(FBDEV_DIR) $(OSS4_DIR) $(OSX_VIDEO_DIR) $(QT_DIR) $(VCD_DIR) $(VDPAU_DIR) $(WININET_DIR) -DIST_SUBDIRS = acmenc acmmp3dec directdraw dvb fbdev dshowdecwrapper dshowsrcwrapper dshowvideosink \ +DIST_SUBDIRS = acmenc acmmp3dec directdraw directsound dvb fbdev dshowdecwrapper dshowsrcwrapper dshowvideosink \ oss4 osxvideo qtwrapper vcd vdpau wasapi wininet winks winscreencap diff --git a/sys/directdraw/gstdirectdrawsink.c b/sys/directdraw/gstdirectdrawsink.c index 144f6595..538227de 100644 --- a/sys/directdraw/gstdirectdrawsink.c +++ b/sys/directdraw/gstdirectdrawsink.c @@ -127,8 +127,11 @@ static gboolean gst_directdraw_sink_interface_supported (GstImplementsInterface * iface, GType type) { - g_assert (type == GST_TYPE_X_OVERLAY); - return TRUE; + if (type == GST_TYPE_X_OVERLAY) + return TRUE; + else if (type == GST_TYPE_NAVIGATION) + return TRUE; + return FALSE; } static void @@ -189,6 +192,71 @@ gst_directdraw_sink_xoverlay_interface_init (GstXOverlayClass * iface) } static void +gst_directdraw_sink_navigation_send_event (GstNavigation * navigation, + GstStructure * structure) +{ + GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (navigation); + GstEvent *event; + GstVideoRectangle src, dst, result; + double x, y, old_x, old_y; + GstPad *pad = NULL; + + src.w = GST_VIDEO_SINK_WIDTH (ddrawsink); + src.h = GST_VIDEO_SINK_HEIGHT (ddrawsink); + dst.w = ddrawsink->out_width; + dst.h = ddrawsink->out_height; + gst_video_sink_center_rect (src, dst, &result, FALSE); + + event = gst_event_new_navigation (structure); + + /* Our coordinates can be wrong here if we centered the video */ + + /* Converting pointer coordinates to the non scaled geometry */ + if (gst_structure_get_double (structure, "pointer_x", &old_x)) { + x = old_x; + + if (x >= result.x && x <= (result.x + result.w)) { + x -= result.x; + x *= ddrawsink->video_width; + x /= result.w; + } else { + x = 0; + } + GST_DEBUG_OBJECT (ddrawsink, "translated navigation event x " + "coordinate from %f to %f", old_x, x); + gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL); + } + if (gst_structure_get_double (structure, "pointer_y", &old_y)) { + y = old_y; + + if (y >= result.y && y <= (result.y + result.h)) { + y -= result.y; + y *= ddrawsink->video_height; + y /= result.h; + } else { + y = 0; + } + GST_DEBUG_OBJECT (ddrawsink, "translated navigation event y " + "coordinate from %f to %f", old_y, y); + gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL); + } + + pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ddrawsink)); + + if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) { + gst_pad_send_event (pad, event); + + gst_object_unref (pad); + } +} + +static void +gst_directdraw_sink_navigation_interface_init (GstNavigationInterface * iface) +{ + iface->send_event = gst_directdraw_sink_navigation_send_event; +} + +static void gst_directdraw_sink_init_interfaces (GType type) { static const GInterfaceInfo iface_info = { @@ -203,9 +271,16 @@ gst_directdraw_sink_init_interfaces (GType type) NULL, }; + static const GInterfaceInfo navigation_info = { + (GInterfaceInitFunc) gst_directdraw_sink_navigation_interface_init, + NULL, + NULL, + }; + g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info); g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &xoverlay_info); + g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &navigation_info); } /* Subclass of GstBuffer which manages buffer_pool surfaces lifetime */ @@ -484,6 +559,8 @@ gst_directdraw_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) ("Failed to get caps properties from caps"), (NULL)); return FALSE; } + GST_VIDEO_SINK_WIDTH (ddrawsink) = ddrawsink->video_width; + GST_VIDEO_SINK_HEIGHT (ddrawsink) = ddrawsink->video_height; ddrawsink->fps_n = gst_value_get_fraction_numerator (fps); ddrawsink->fps_d = gst_value_get_fraction_denominator (fps); @@ -1277,7 +1354,6 @@ gst_directdraw_sink_setup_ddraw (GstDirectDrawSink * ddrawsink) { gboolean bRet = TRUE; HRESULT hRes; - /* create an instance of the ddraw object use DDCREATE_EMULATIONONLY as first * parameter to force Directdraw to use the hardware emulation layer */ hRes = DirectDrawCreateEx ( /*DDCREATE_EMULATIONONLY */ 0, @@ -1320,6 +1396,118 @@ long FAR PASCAL WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { + case WM_CREATE:{ + LPCREATESTRUCT crs = (LPCREATESTRUCT) lParam; + /* Nail pointer to the video sink down to this window */ + SetWindowLongPtr (hWnd, GWLP_USERDATA, (LONG_PTR) crs->lpCreateParams); + break; + } + case WM_SIZE: + case WM_CHAR: + case WM_KEYDOWN: + case WM_KEYUP: + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: + case WM_MOUSEMOVE:{ + GstDirectDrawSink *ddrawsink; + ddrawsink = (GstDirectDrawSink *) GetWindowLongPtr (hWnd, GWLP_USERDATA); + + if (G_UNLIKELY (!ddrawsink)) + break; + + switch (message) { + case WM_SIZE:{ + GST_OBJECT_LOCK (ddrawsink); + ddrawsink->out_width = LOWORD (lParam); + ddrawsink->out_height = HIWORD (lParam); + GST_OBJECT_UNLOCK (ddrawsink); + GST_DEBUG_OBJECT (ddrawsink, "Window size is %dx%d", LOWORD (wParam), + HIWORD (wParam)); + break; + } + case WM_CHAR: + case WM_KEYDOWN: + case WM_KEYUP:{ + gunichar2 wcrep[128]; + if (GetKeyNameTextW (lParam, wcrep, 128)) { + gchar *utfrep = g_utf16_to_utf8 (wcrep, 128, NULL, NULL, NULL); + if (utfrep) { + if (message == WM_CHAR || message == WM_KEYDOWN) + gst_navigation_send_key_event (GST_NAVIGATION (ddrawsink), + "key-press", utfrep); + if (message == WM_CHAR || message == WM_KEYUP) + gst_navigation_send_key_event (GST_NAVIGATION (ddrawsink), + "key-release", utfrep); + g_free (utfrep); + } + } + break; + } + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_MOUSEMOVE:{ + gint x, y, button; + gchar *action; + + switch (message) { + case WM_MOUSEMOVE: + button = 0; + action = "mouse-move"; + break; + case WM_LBUTTONDOWN: + button = 1; + action = "mouse-button-press"; + break; + case WM_LBUTTONUP: + button = 1; + action = "mouse-button-release"; + break; + case WM_RBUTTONDOWN: + button = 2; + action = "mouse-button-press"; + break; + case WM_RBUTTONUP: + button = 2; + action = "mouse-button-release"; + break; + case WM_MBUTTONDOWN: + button = 3; + action = "mouse-button-press"; + break; + case WM_MBUTTONUP: + button = 3; + action = "mouse-button-release"; + break; + default: + button = 4; + } + + x = LOWORD (lParam); + y = HIWORD (lParam); + + if (button == 0) { + GST_DEBUG_OBJECT (ddrawsink, "Mouse moved to %dx%d", x, y); + } else + GST_DEBUG_OBJECT (ddrawsink, "Mouse button %d pressed at %dx%d", + button, x, y); + + if (button < 4) + gst_navigation_send_mouse_event (GST_NAVIGATION (ddrawsink), + action, button, x, y); + + break; + } + } + break; + } case WM_ERASEBKGND: return TRUE; case WM_CLOSE: @@ -1352,7 +1540,7 @@ gst_directdraw_sink_window_thread (GstDirectDrawSink * ddrawsink) ddrawsink->video_window = CreateWindowEx (0, "GStreamer-DirectDraw", "GStreamer-DirectDraw sink default window", WS_OVERLAPPEDWINDOW | WS_SIZEBOX, 0, 0, 640, 480, NULL, NULL, - WndClass.hInstance, NULL); + WndClass.hInstance, (LPVOID) ddrawsink); if (ddrawsink->video_window == NULL) return NULL; diff --git a/sys/directdraw/gstdirectdrawsink.h b/sys/directdraw/gstdirectdrawsink.h index 9cb5f788..1d7cc5d0 100644 --- a/sys/directdraw/gstdirectdrawsink.h +++ b/sys/directdraw/gstdirectdrawsink.h @@ -30,6 +30,7 @@ #include <gst/gst.h> #include <gst/video/gstvideosink.h> #include <gst/interfaces/xoverlay.h> +#include <gst/interfaces/navigation.h> #include <windows.h> #include <ddraw.h> diff --git a/sys/directsound/Makefile.am b/sys/directsound/Makefile.am new file mode 100644 index 00000000..31994014 --- /dev/null +++ b/sys/directsound/Makefile.am @@ -0,0 +1,12 @@ +plugin_LTLIBRARIES = libgstdirectsoundsrc.la
+
+libgstdirectsoundsrc_la_SOURCES = gstdirectsoundsrc.c gstdirectsoundplugin.c
+libgstdirectsoundsrc_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) \
+ $(GST_PLUGINS_BASE_CFLAGS)
+libgstdirectsoundsrc_la_LIBADD = $(DIRECTDRAW_LIBS) \
+ $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_MAJORMINOR) \
+ -lgstinterfaces-$(GST_MAJORMINOR) -ldsound
+libgstdirectsoundsrc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(DIRECTDRAW_LDFLAGS)
+libgstdirectsoundsrc_la_LIBTOOLFLAGS = --tag=disable-static
+
+noinst_HEADERS= gstdirectsoundsrc.h
diff --git a/sys/directsound/gstdirectsoundplugin.c b/sys/directsound/gstdirectsoundplugin.c new file mode 100644 index 00000000..e3816967 --- /dev/null +++ b/sys/directsound/gstdirectsoundplugin.c @@ -0,0 +1,49 @@ +/* GStreamer +* Copyright (C) 2005 Sebastien Moutte <sebastien@moutte.net> +* Copyright (C) 2007 Pioneers of the Inevitable <songbird@songbirdnest.com> +* +* gstdirectsoundplugin.c: +* +* 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. +* +* +* The development of this code was made possible due to the involvement +* of Pioneers of the Inevitable, the creators of the Songbird Music player +* +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstdirectsoundsrc.h" + + +static gboolean +plugin_init (GstPlugin * plugin) +{ + if (!gst_element_register (plugin, "directsoundsrc", GST_RANK_PRIMARY, + GST_TYPE_DIRECTSOUND_SRC)) + return FALSE; + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "directsoundsrc", + "Direct Sound Source plugin library", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/sys/directsound/gstdirectsoundsrc.c b/sys/directsound/gstdirectsoundsrc.c new file mode 100644 index 00000000..429d6b85 --- /dev/null +++ b/sys/directsound/gstdirectsoundsrc.c @@ -0,0 +1,587 @@ +/*
+ * GStreamer
+ * Copyright 2005 Thomas Vander Stichele <thomas@apestaart.org>
+ * Copyright 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
+ * Copyright 2005 Sébastien Moutte <sebastien@moutte.net>
+ * Copyright 2006 Joni Valtanen <joni.valtanen@movial.fi>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+ * which case the following provisions apply instead of the ones
+ * mentioned above:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ TODO: add device selection and check rate etc.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif /*
*/ +
+#include <gst/gst.h>
+#include <gst/audio/gstbaseaudiosrc.h>
+
+#include "gstdirectsoundsrc.h"
+
+#include <windows.h>
+#include <dsound.h>
+
GST_DEBUG_CATEGORY_STATIC (directsoundsrc_debug); +
+#define GST_CAT_DEFAULT directsoundsrc_debug
+
static GstElementDetails gst_directsound_src_details = +
GST_ELEMENT_DETAILS ("Audio Source (DIRECTCSOUND)",
"Source/Audio", +
"Capture from a soundcard via DIRECTSOUND", +
"Joni Valtanen <joni.valtanen@movial.fi>"); +
+/* defaults here */
+#define DEFAULT_DEVICE 0
+
+/* properties */
+ enum +{
PROP_0,
PROP_DEVICE
+}; +
static HRESULT (WINAPI * pDSoundCaptureCreate) (LPGUID, +
LPDIRECTSOUNDCAPTURE *,
LPUNKNOWN); +
static void gst_directsound_src_finalise (GObject * object); +
static void gst_directsound_src_set_property (GObject * object, +
guint prop_id,
const GValue * value,
GParamSpec * pspec); +
static void gst_directsound_src_get_property (GObject * object, +
guint prop_id,
GValue * value,
GParamSpec * pspec); +
static gboolean gst_directsound_src_open (GstAudioSrc * asrc); +
static gboolean gst_directsound_src_close (GstAudioSrc * asrc); +
static gboolean gst_directsound_src_prepare (GstAudioSrc * asrc, +
GstRingBufferSpec * spec); +
static gboolean gst_directsound_src_unprepare (GstAudioSrc * asrc); +
static void gst_directsound_src_reset (GstAudioSrc * asrc); +
static GstCaps *gst_directsound_src_getcaps (GstBaseSrc * bsrc); +
static guint gst_directsound_src_read (GstAudioSrc * asrc,
gpointer data, +
guint length); +
static void gst_directsound_src_dispose (GObject * object); +
static void gst_directsound_src_do_init (GType type); +
static guint gst_directsound_src_delay (GstAudioSrc * asrc); +
static GstStaticPadTemplate directsound_src_src_factory = +
GST_STATIC_PAD_TEMPLATE (
"src",
GST_PAD_SRC,
GST_PAD_ALWAYS, +
GST_STATIC_CAPS ("audio/x-raw-int, "
+ "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, "
+ "signed = (boolean) { TRUE, FALSE }, "
"width = (int) 16, "
+ "depth = (int) 16, "
"rate = (int) [ 1, MAX ], " + "channels = (int) [ 1, 2 ]; "
"audio/x-raw-int, "
+ "signed = (boolean) { TRUE, FALSE }, "
"width = (int) 8, "
+ "depth = (int) 8, "
"rate = (int) [ 1, MAX ], " + "channels = (int) [ 1, 2 ]")); +
static void
+gst_directsound_src_do_init (GType type)
+{ +
GST_DEBUG_CATEGORY_INIT (directsoundsrc_debug, "directsoundsrc", 0, +
"DirectSound Src"); +
}
GST_BOILERPLATE_FULL (GstDirectSoundSrc, gst_directsound_src, + GstAudioSrc,
GST_TYPE_AUDIO_SRC, gst_directsound_src_do_init); +
static void
+gst_directsound_src_dispose (GObject * object)
+{ +
G_OBJECT_CLASS (parent_class)->dispose (object); +
}
static void
+ +gst_directsound_src_finalise (GObject * object)
+{ +
GstDirectSoundSrc * dsoundsrc = GST_DIRECTSOUND_SRC (object); +
g_mutex_free (dsoundsrc->dsound_lock); +
}
static void
+ +gst_directsound_src_base_init (gpointer g_class)
+{ +
GstElementClass * element_class = GST_ELEMENT_CLASS (g_class); +
GST_DEBUG ("initializing directsoundsrc base\n"); +
gst_element_class_set_details (element_class, &gst_directsound_src_details); +
gst_element_class_add_pad_template (element_class, +
gst_static_pad_template_get (&directsound_src_src_factory)); +
}
+ +/* initialize the plugin's class */
+static void
+gst_directsound_src_class_init (GstDirectSoundSrcClass * klass)
+{ +
GObjectClass * gobject_class; +
GstElementClass * gstelement_class; +
GstBaseSrcClass * gstbasesrc_class; +
GstBaseAudioSrcClass * gstbaseaudiosrc_class; +
GstAudioSrcClass * gstaudiosrc_class; +
gobject_class = (GObjectClass *) klass; +
gstelement_class = (GstElementClass *) klass; +
gstbasesrc_class = (GstBaseSrcClass *) klass; +
gstbaseaudiosrc_class = (GstBaseAudioSrcClass *) klass; +
gstaudiosrc_class = (GstAudioSrcClass *) klass; +
GST_DEBUG ("initializing directsoundsrc class\n"); +
gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_directsound_src_finalise); +
gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_directsound_src_dispose); +
gobject_class->get_property = + GST_DEBUG_FUNCPTR (gst_directsound_src_get_property); +
gobject_class->set_property = + GST_DEBUG_FUNCPTR (gst_directsound_src_set_property); +
gstbasesrc_class->get_caps = + GST_DEBUG_FUNCPTR (gst_directsound_src_getcaps); +
gstaudiosrc_class->open = GST_DEBUG_FUNCPTR (gst_directsound_src_open); +
gstaudiosrc_class->close = GST_DEBUG_FUNCPTR (gst_directsound_src_close); +
gstaudiosrc_class->read = GST_DEBUG_FUNCPTR (gst_directsound_src_read); +
gstaudiosrc_class->prepare = GST_DEBUG_FUNCPTR (gst_directsound_src_prepare); +
gstaudiosrc_class->unprepare = + GST_DEBUG_FUNCPTR (gst_directsound_src_unprepare); +
gstaudiosrc_class->delay = GST_DEBUG_FUNCPTR (gst_directsound_src_delay); +
gstaudiosrc_class->reset = GST_DEBUG_FUNCPTR (gst_directsound_src_reset); +
}
static GstCaps *
+ +gst_directsound_src_getcaps (GstBaseSrc * bsrc)
+{ +
GstDirectSoundSrc * dsoundsrc; +
GstCaps * caps = NULL; +
GST_DEBUG ("get caps\n"); +
dsoundsrc = GST_DIRECTSOUND_SRC (bsrc); +
caps = gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD
+ (bsrc))); +
return caps; +
} + +
static void
+gst_directsound_src_set_property (GObject * object, guint prop_id, +
const GValue * value, GParamSpec * pspec)
+{ +
+ // GstDirectSoundSrc *src = GST_DIRECTSOUND_SRC (object);
+ GST_DEBUG ("set property\n"); +
switch (prop_id) +
{ +
+#if 0
+ /* FIXME */
+ case PROP_DEVICE: +
src->device = g_value_get_uint (value); +
break; +
+#endif /*
*/ + default: +
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +
break; +
} +
} + +
static void
+gst_directsound_src_get_property (GObject * object, guint prop_id, +
GValue * value, GParamSpec * pspec)
+{ +
+#if 0
+ GstDirectSoundSrc * src = GST_DIRECTSOUND_SRC (object); +
+#endif /*
*/ +
GST_DEBUG ("get property\n"); +
switch (prop_id) { +
+#if 0
+ /* FIXME */
+ case PROP_DEVICE: +
g_value_set_uint (value, src->device); +
break; +
+#endif /*
*/ + default: +
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +
break; +
} +
} + +
+/* initialize the new element
+ * instantiate pads and add them to element
+ * set functions
+ * initialize structure
+ */
+static void
+gst_directsound_src_init (GstDirectSoundSrc * src, +
GstDirectSoundSrcClass * gclass)
+{ +
GST_DEBUG ("initializing directsoundsrc\n"); +
src->dsound_lock = g_mutex_new (); +
}
static gboolean
+ +gst_directsound_src_open (GstAudioSrc * asrc)
+{ +
GstDirectSoundSrc * dsoundsrc; +
HRESULT hRes; /* Result for windows functions */ +
GST_DEBUG ("initializing directsoundsrc\n"); +
dsoundsrc = GST_DIRECTSOUND_SRC (asrc); +
+ /* Open dsound.dll */
+ dsoundsrc->DSoundDLL = LoadLibrary ("dsound.dll"); +
if (!dsoundsrc->DSoundDLL) { +
goto dsound_open; +
} +
+ /* Building the DLL Calls */
+ pDSoundCaptureCreate =
+ (void *) GetProcAddress (dsoundsrc->DSoundDLL, +
TEXT ("DirectSoundCaptureCreate")); +
+ /* If everything is not ok */
+ if (!pDSoundCaptureCreate) { +
goto capture_function; +
} +
+ /* FIXME: add here device selection */
+ /* Create capture object */
+ hRes = pDSoundCaptureCreate (NULL, &dsoundsrc->pDSC, NULL); +
if (FAILED (hRes)) { +
goto capture_object; +
} +
return TRUE; +
capture_function:
{ +
FreeLibrary (dsoundsrc->DSoundDLL); +
GST_ELEMENT_ERROR (dsoundsrc, RESOURCE, OPEN_READ,
+ ("Unable to get capturecreate function"), (NULL)); +
return FALSE; +
} +
capture_object:
{ +
FreeLibrary (dsoundsrc->DSoundDLL); +
GST_ELEMENT_ERROR (dsoundsrc, RESOURCE, OPEN_READ,
+ ("Unable to create capture object"), (NULL)); +
return FALSE; +
} +
dsound_open:
{ +
GST_ELEMENT_ERROR (dsoundsrc, RESOURCE, OPEN_READ,
+ ("Unable to open dsound.dll"), (NULL)); +
return FALSE; +
} +
} + +
static gboolean
+gst_directsound_src_close (GstAudioSrc * asrc)
+{ +
GstDirectSoundSrc * dsoundsrc; +
HRESULT hRes; /* Result for windows functions */ +
GST_DEBUG ("initializing directsoundsrc\n"); +
dsoundsrc = GST_DIRECTSOUND_SRC (asrc); +
+ /* Release capture handler */
+ hRes = IDirectSoundCapture_Release (dsoundsrc->pDSC); +
+ /* Close library */
+ FreeLibrary (dsoundsrc->DSoundDLL); +
return TRUE; +
} + +
static gboolean
+gst_directsound_src_prepare (GstAudioSrc * asrc, GstRingBufferSpec * spec)
+{ +
GstDirectSoundSrc * dsoundsrc; +
WAVEFORMATEX wfx; /* Wave format structure */ +
HRESULT hRes; /* Result for windows functions */ +
DSCBUFFERDESC descSecondary; /* Capturebuffer decsiption */ +
int fmt = 0; /* audio format */ +
dsoundsrc = GST_DIRECTSOUND_SRC (asrc); +
GST_DEBUG ("initializing directsoundsrc\n"); +
+ /* Define buffer */
+ memset (&wfx, 0, sizeof (WAVEFORMATEX)); +
wfx.wFormatTag = WAVE_FORMAT_PCM; /* should be WAVE_FORMAT_PCM */ +
wfx.nChannels = spec->channels; +
wfx.nSamplesPerSec = spec->rate; /* 8000|11025|22050|44100 */ +
wfx.wBitsPerSample = spec->width; // 8|16;
+
wfx.nBlockAlign = wfx.nChannels * (wfx.wBitsPerSample / 8); +
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; +
wfx.cbSize = 0; /* This size is allways for PCM-format */ +
+ /* 1 or 2 Channels etc...
+ FIXME: Never really tested. Is this ok?
+ */
+ if (spec->width == 16 && spec->channels == 1) { +
spec->format = GST_S16_LE; +
} else if (spec->width == 16 && spec->channels == 2) { +
spec->format = GST_U16_LE; +
} else if (spec->width == 8 && spec->channels == 1) { +
spec->format = GST_S8; +
} else if (spec->width == 8 && spec->channels == 2) { +
spec->format = GST_U8; +
} +
+ /* Set the buffer size to two seconds.
+ This should never reached.
+ */
+ dsoundsrc->buffer_size = wfx.nAvgBytesPerSec * 2; +
+ //notifysize * 16; //spec->width; /*original 16*/
+ GST_DEBUG ("Buffer size: %d", dsoundsrc->buffer_size); +
+ /* Init secondary buffer desciption */
+ memset (&descSecondary, 0, sizeof (DSCBUFFERDESC)); +
descSecondary.dwSize = sizeof (DSCBUFFERDESC); +
descSecondary.dwFlags = 0; +
descSecondary.dwReserved = 0; +
+ /* This is not primary buffer so have to set size */
+ descSecondary.dwBufferBytes = dsoundsrc->buffer_size; +
descSecondary.lpwfxFormat = &wfx; +
+ /* Create buffer */
+ hRes = + IDirectSoundCapture_CreateCaptureBuffer (dsoundsrc->pDSC,
&descSecondary, +
&dsoundsrc->pDSBSecondary,
NULL); +
if (hRes != DS_OK) { +
goto capture_buffer; +
} +
spec->channels = wfx.nChannels; +
spec->rate = wfx.nSamplesPerSec; +
spec->bytes_per_sample = (spec->width / 8) * spec->channels; +
dsoundsrc->bytes_per_sample = spec->bytes_per_sample; +
GST_DEBUG ("latency time: %llu - buffer time: %llu",
spec->latency_time, + spec->buffer_time); +
+ /* Buffer-time should be allways more than 2*latency */
+ if (spec->buffer_time < spec->latency_time * 2) { +
spec->buffer_time = spec->latency_time * 2; +
GST_WARNING ("buffer-time was less than latency"); +
} +
+ /* Save the times */
+ dsoundsrc->buffer_time = spec->buffer_time; +
dsoundsrc->latency_time = spec->latency_time; +
dsoundsrc->latency_size = + (gint) wfx.nAvgBytesPerSec *
dsoundsrc->latency_time / 1000000.0; +
spec->segsize = + (guint) (((double) spec->buffer_time / 1000000.0) *
wfx.nAvgBytesPerSec); +
+ /* just in case */
+ if (spec->segsize < 1) +
spec->segsize = 1; +
spec->segtotal = spec->width * (wfx.nAvgBytesPerSec / spec->segsize); +
GST_DEBUG ("bytes/sec: %d, buffer size: %d, segsize: %d, segtotal: %d", +
wfx.nAvgBytesPerSec,
dsoundsrc->buffer_size,
spec->segsize, +
spec->segtotal); +
spec->silence_sample[0] = 0; +
spec->silence_sample[1] = 0; +
spec->silence_sample[2] = 0; +
spec->silence_sample[3] = 0; +
if (spec->width != 16 && spec->width != 8) +
goto dodgy_width; +
+ /* Not readed anything yet */
+ dsoundsrc->current_circular_offset = 0; +
GST_DEBUG ("GstRingBufferSpec->channels: %d, GstRingBufferSpec->rate: %d, \
+GstRingBufferSpec->bytes_per_sample: %d\n\
+WAVEFORMATEX.nSamplesPerSec: %ld, WAVEFORMATEX.wBitsPerSample: %d, \
+WAVEFORMATEX.nBlockAlign: %d, WAVEFORMATEX.nAvgBytesPerSec: %ld\n",
spec->channels, spec->rate, spec->bytes_per_sample,
wfx.nSamplesPerSec, wfx.wBitsPerSample,
wfx.nBlockAlign, wfx.nAvgBytesPerSec); +
return TRUE; +
wrong_format:
{ +
GST_ELEMENT_ERROR (dsoundsrc, RESOURCE, OPEN_READ,
+ ("Unable to get format %d", spec->format), (NULL)); +
return FALSE; +
} +
capture_buffer:
{ +
GST_ELEMENT_ERROR (dsoundsrc, RESOURCE, OPEN_READ,
+ ("Unable to create capturebuffer"), (NULL)); +
return FALSE; +
} +
dodgy_width:
{ +
GST_ELEMENT_ERROR (dsoundsrc, RESOURCE, OPEN_READ,
+ ("Unexpected width %d", spec->width), (NULL)); +
return FALSE; +
} +
} + +
static gboolean
+gst_directsound_src_unprepare (GstAudioSrc * asrc)
+{ +
GstDirectSoundSrc * dsoundsrc; +
HRESULT hRes; /* Result for windows functions */ +
+ /* Resets */
+ GST_DEBUG ("unpreparing directsoundsrc"); +
dsoundsrc = GST_DIRECTSOUND_SRC (asrc); +
+ /* Stop capturing */
+ hRes = IDirectSoundCaptureBuffer_Stop (dsoundsrc->pDSBSecondary); +
+ /* Release buffer */
+ hRes = IDirectSoundCaptureBuffer_Release (dsoundsrc->pDSBSecondary); +
return TRUE; +
} + +
+/*
+return number of readed bytes */
+static guint
+gst_directsound_src_read (GstAudioSrc * asrc, gpointer data, guint length)
+{ +
GstDirectSoundSrc * dsoundsrc; +
HRESULT hRes; /* Result for windows functions */ +
DWORD dwCurrentCaptureCursor = 0; +
DWORD dwBufferSize = 0; +
LPVOID pLockedBuffer1 = NULL; +
LPVOID pLockedBuffer2 = NULL; +
DWORD dwSizeBuffer1 = 0; +
DWORD dwSizeBuffer2 = 0; +
DWORD dwStatus = 0; +
GST_DEBUG ("reading directsoundsrc\n"); +
dsoundsrc = GST_DIRECTSOUND_SRC (asrc); +
GST_DSOUND_LOCK (dsoundsrc); +
+ /* Get current buffer status */
+ hRes = + IDirectSoundCaptureBuffer_GetStatus (dsoundsrc->pDSBSecondary, +
&dwStatus); +
+ /* Starting capturing if not allready */
+ if (!(dwStatus & DSCBSTATUS_CAPTURING)) { +
hRes = + IDirectSoundCaptureBuffer_Start (dsoundsrc->pDSBSecondary, +
DSCBSTART_LOOPING); +
+ // Sleep (dsoundsrc->latency_time/1000);
+ GST_DEBUG ("capture started"); +
} +
+ // calculate_buffersize:
+ while (length > dwBufferSize) { +
Sleep (dsoundsrc->latency_time / 1000); +
hRes = +
IDirectSoundCaptureBuffer_GetCurrentPosition (dsoundsrc->pDSBSecondary, +
&dwCurrentCaptureCursor,
NULL); +
+ /* calculate the buffer */
+ if (dwCurrentCaptureCursor < dsoundsrc->current_circular_offset) { +
dwBufferSize = dsoundsrc->buffer_size -
+ (dsoundsrc->current_circular_offset -
dwCurrentCaptureCursor); +
} else { +
dwBufferSize = +
dwCurrentCaptureCursor - dsoundsrc->current_circular_offset; +
} +
} // while (...
+
+ /* Lock the buffer */
+ hRes = + IDirectSoundCaptureBuffer_Lock (dsoundsrc->pDSBSecondary, +
dsoundsrc->current_circular_offset,
length,
&pLockedBuffer1, +
&dwSizeBuffer1,
&pLockedBuffer2,
&dwSizeBuffer2,
0L); +
+ /* Copy buffer data to another buffer */
+ if (hRes == DS_OK) { +
memcpy (data, pLockedBuffer1, dwSizeBuffer1); +
} +
+ /* ...and if something is in another buffer */
+ if (pLockedBuffer2 != NULL) { +
memcpy ((data + dwSizeBuffer1), pLockedBuffer2, dwSizeBuffer2); +
} +
dsoundsrc->current_circular_offset += dwSizeBuffer1 + dwSizeBuffer2; +
dsoundsrc->current_circular_offset %= dsoundsrc->buffer_size; +
IDirectSoundCaptureBuffer_Unlock (dsoundsrc->pDSBSecondary,
pLockedBuffer1, +
dwSizeBuffer1,
pLockedBuffer2,
dwSizeBuffer2); +
GST_DSOUND_UNLOCK (dsoundsrc); +
+ /* return length (readed data size in bytes) */
+ return length; +
} + +
static guint
+gst_directsound_src_delay (GstAudioSrc * asrc)
+{ +
GstDirectSoundSrc * dsoundsrc; +
HRESULT hRes; +
DWORD dwCurrentCaptureCursor; +
DWORD dwBytesInQueue = 0; +
gint nNbSamplesInQueue = 0; +
GST_DEBUG ("Delay\n"); +
dsoundsrc = GST_DIRECTSOUND_SRC (asrc); +
+ /* evaluate the number of samples in queue in the circular buffer */
+ hRes = +
IDirectSoundCaptureBuffer_GetCurrentPosition (dsoundsrc->pDSBSecondary, +
&dwCurrentCaptureCursor,
NULL); +
+ /* FIXME: Check is this calculated right */
+ if (hRes == S_OK) { +
if (dwCurrentCaptureCursor < dsoundsrc->current_circular_offset) { +
dwBytesInQueue = +
dsoundsrc->buffer_size - (dsoundsrc->current_circular_offset - +
dwCurrentCaptureCursor); +
} else { +
dwBytesInQueue = +
dwCurrentCaptureCursor - dsoundsrc->current_circular_offset; +
} +
nNbSamplesInQueue = dwBytesInQueue / dsoundsrc->bytes_per_sample; +
} +
return nNbSamplesInQueue; +
} + +
static void
+gst_directsound_src_reset (GstAudioSrc * asrc)
+{ +
GstDirectSoundSrc * dsoundsrc; +
LPVOID pLockedBuffer = NULL; +
DWORD dwSizeBuffer = 0; +
GST_DEBUG ("reset directsoundsrc\n"); +
dsoundsrc = GST_DIRECTSOUND_SRC (asrc); +
+#if 0
+ IDirectSoundCaptureBuffer_Stop (dsoundsrc->pDSBSecondary); +
+#endif /*
*/ +
GST_DSOUND_LOCK (dsoundsrc); +
if (dsoundsrc->pDSBSecondary) { +
+ /*stop capturing */
+ HRESULT hRes = + IDirectSoundCaptureBuffer_Stop (dsoundsrc->pDSBSecondary); +
+ /*reset position */
+ /* hRes = IDirectSoundCaptureBuffer_SetCurrentPosition (dsoundsrc->pDSBSecondary, 0); */
+
+ /*reset the buffer */
+ hRes = + IDirectSoundCaptureBuffer_Lock (dsoundsrc->pDSBSecondary, +
dsoundsrc->current_circular_offset, dsoundsrc->buffer_size, +
pLockedBuffer, &dwSizeBuffer, NULL, NULL, 0L); +
if (SUCCEEDED (hRes)) { +
memset (pLockedBuffer, 0, dwSizeBuffer); +
hRes = +
IDirectSoundCaptureBuffer_Unlock (dsoundsrc->pDSBSecondary, + pLockedBuffer,
dwSizeBuffer, NULL, 0); +
} +
dsoundsrc->current_circular_offset = 0; +
} +
GST_DSOUND_UNLOCK (dsoundsrc); +
} + +
diff --git a/sys/directsound/gstdirectsoundsrc.h b/sys/directsound/gstdirectsoundsrc.h new file mode 100644 index 00000000..2d164fc8 --- /dev/null +++ b/sys/directsound/gstdirectsoundsrc.h @@ -0,0 +1,112 @@ +/*
+ * GStreamer
+ * Copyright 2005 Thomas Vander Stichele <thomas@apestaart.org>
+ * Copyright 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
+ * Copyright 2005 Sébastien Moutte <sebastien@moutte.net>
+ * Copyright 2006 Joni Valtanen <joni.valtanen@movial.fi>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+ * which case the following provisions apply instead of the ones
+ * mentioned above:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_DIRECTSOUNDSRC_H__
+#define __GST_DIRECTSOUNDSRC_H__
+
+#include <gst/gst.h>
+#include <gst/audio/gstaudiosrc.h>
+
+#include <windows.h>
+#include <dsound.h>
+
+/* add here some headers if needed */
+
+
+G_BEGIN_DECLS
+
+/* #defines don't like whitespacey bits */
+#define GST_TYPE_DIRECTSOUND_SRC (gst_directsound_src_get_type())
+#define GST_DIRECTSOUND_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DIRECTSOUND_SRC,GstDirectSoundSrc))
+#define GST_DIRECTSOUND_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DIRECTSOUND_SRC,GstDirectSoundSrcClass))
+#define GST_IS_DIRECTSOUND_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DIRECTSOUND_SRC))
+#define GST_IS_DIRECTSOUND_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DIRECTSOUND_SRC))
+
+typedef struct _GstDirectSoundSrc GstDirectSoundSrc;
+typedef struct _GstDirectSoundSrcClass GstDirectSoundSrcClass;
+
+#define GST_DSOUND_LOCK(obj) (g_mutex_lock (obj->dsound_lock))
+#define GST_DSOUND_UNLOCK(obj) (g_mutex_unlock (obj->dsound_lock))
+
+struct _GstDirectSoundSrc
+{
+
+ GstAudioSrc src;
+
+ HINSTANCE DSoundDLL; /* DLL instance */
+ LPDIRECTSOUNDCAPTURE pDSC; /* DirectSoundCapture*/
+ LPDIRECTSOUNDCAPTUREBUFFER pDSBSecondary; /*Secondaty capturebuffer*/
+ DWORD current_circular_offset;
+
+ HANDLE rghEvent;
+ DWORD notifysize;
+
+ guint buffer_size;
+ guint latency_size;
+ guint bytes_per_sample;
+
+ guint buffer_time;
+ guint latency_time;
+
+
+#if 0
+ guint device;
+#endif
+
+ GMutex *dsound_lock;
+
+};
+
+struct _GstDirectSoundSrcClass
+{
+ GstAudioSrcClass parent_class;
+};
+
+GType gst_directsound_src_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_DIRECTSOUNDSRC_H__ */
diff --git a/sys/dshowsrcwrapper/Makefile.am b/sys/dshowsrcwrapper/Makefile.am index 74bf83ed..de78c531 100755 --- a/sys/dshowsrcwrapper/Makefile.am +++ b/sys/dshowsrcwrapper/Makefile.am @@ -8,7 +8,6 @@ EXTRA_DIST = \ gstdshowfakesink.cpp \ gstdshowfakesink.h \ gstdshow.h \ - gstdshowinterface.h \ gstdshowsrcwrapper.cpp \ gstdshowvideosrc.cpp \ gstdshowvideosrc.h diff --git a/sys/dshowsrcwrapper/gstdshow.cpp b/sys/dshowsrcwrapper/gstdshow.cpp index ed27d631..4b090346 100755 --- a/sys/dshowsrcwrapper/gstdshow.cpp +++ b/sys/dshowsrcwrapper/gstdshow.cpp @@ -22,44 +22,6 @@ #include "gstdshow.h" #include "gstdshowfakesink.h" -CFactoryTemplate g_Templates[]= -{ - { - L"DSHOW fake sink filter" - , &CLSID_DshowFakeSink - , CDshowFakeSink::CreateInstance - , NULL - , NULL - } -}; - -int g_cTemplates = sizeof(g_Templates)/sizeof(g_Templates[0]); -static HINSTANCE g_hModule = NULL; - -extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID); -BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) -{ - if (!g_hModule) - g_hModule = (HINSTANCE)hModule; - - return DllEntryPoint((HINSTANCE)(hModule), dwReason, lpReserved); -} - -STDAPI DllRegisterServer() -{ - return AMovieDllRegisterServer2 (TRUE); -} - -STDAPI DllUnregisterServer() -{ - return AMovieDllRegisterServer2 (FALSE); -} - -HRESULT gst_dshow_register_fakefilters () -{ - return DllRegisterServer(); -} - void gst_dshow_free_mediatype (AM_MEDIA_TYPE *pmt) { diff --git a/sys/dshowsrcwrapper/gstdshow.h b/sys/dshowsrcwrapper/gstdshow.h index ec360b32..e71ca789 100755 --- a/sys/dshowsrcwrapper/gstdshow.h +++ b/sys/dshowsrcwrapper/gstdshow.h @@ -43,7 +43,7 @@ extern "C" { #endif /* register fake filters as COM object and as Direct Show filters in the registry */ -HRESULT gst_dshow_register_fakefilters (); +//HRESULT gst_dshow_register_fakefilters (); /* free memory of the input pin mediatype */ void gst_dshow_free_pin_mediatype (gpointer pt); diff --git a/sys/dshowsrcwrapper/gstdshowaudiosrc.cpp b/sys/dshowsrcwrapper/gstdshowaudiosrc.cpp index c213aae5..a0e2d78c 100755 --- a/sys/dshowsrcwrapper/gstdshowaudiosrc.cpp +++ b/sys/dshowsrcwrapper/gstdshowaudiosrc.cpp @@ -95,7 +95,7 @@ static void gst_dshowaudiosrc_reset (GstAudioSrc * asrc); static GstCaps *gst_dshowaudiosrc_getcaps_from_streamcaps (GstDshowAudioSrc * src, IPin * pin, IAMStreamConfig * streamcaps); static gboolean gst_dshowaudiosrc_push_buffer (byte * buffer, long size, - byte * src_object, UINT64 start, UINT64 stop); + gpointer src_object, UINT64 start, UINT64 stop); static void gst_dshowaudiosrc_init_interfaces (GType type) @@ -540,13 +540,8 @@ gst_dshowaudiosrc_open (GstAudioSrc * asrc) goto error; } - hres = CoCreateInstance (CLSID_DshowFakeSink, NULL, CLSCTX_INPROC, - IID_IBaseFilter, (LPVOID *) & src->dshow_fakesink); - if (hres != S_OK || !src->dshow_fakesink) { - GST_CAT_ERROR (dshowaudiosrc_debug, - "Can't create an instance of the directshow fakesink (error=%d)", hres); - goto error; - } + src->dshow_fakesink = new CDshowFakeSink; + src->dshow_fakesink->AddRef(); hres = src->filter_graph->AddFilter(src->audio_cap_filter, L"capture"); if (hres != S_OK) { @@ -587,7 +582,6 @@ static gboolean gst_dshowaudiosrc_prepare (GstAudioSrc * asrc, GstRingBufferSpec * spec) { HRESULT hres; - IGstDshowInterface *srcinterface = NULL; IPin *input_pin = NULL; GstDshowAudioSrc *src = GST_DSHOWAUDIOSRC (asrc); @@ -613,20 +607,9 @@ gst_dshowaudiosrc_prepare (GstAudioSrc * asrc, GstRingBufferSpec * spec) if (type) { pin_mediatype = (GstCapturePinMediaType *) type->data; - hres = src->dshow_fakesink->QueryInterface(IID_IGstDshowInterface, (LPVOID *) &srcinterface); - if (hres != S_OK || !srcinterface) { - GST_CAT_ERROR (dshowaudiosrc_debug, - "Can't get IGstDshowInterface interface from our dshow fakesink filter (error=%d)", - hres); - goto error; - } - - srcinterface->gst_set_media_type(pin_mediatype->mediatype); - srcinterface->gst_set_buffer_callback( - (push_buffer_func) gst_dshowaudiosrc_push_buffer, (byte *) src); - - if (srcinterface) - srcinterface->Release(); + src->dshow_fakesink->gst_set_media_type (pin_mediatype->mediatype); + src->dshow_fakesink->gst_set_buffer_callback( + (push_buffer_func) gst_dshowaudiosrc_push_buffer, src); gst_dshow_get_pin_from_filter (src->dshow_fakesink, PINDIR_INPUT, &input_pin); @@ -656,9 +639,6 @@ gst_dshowaudiosrc_prepare (GstAudioSrc * asrc, GstRingBufferSpec * spec) return TRUE; error: - if (srcinterface) - srcinterface->Release(); - return FALSE; } @@ -844,7 +824,7 @@ gst_dshowaudiosrc_getcaps_from_streamcaps (GstDshowAudioSrc * src, IPin * pin, } static gboolean -gst_dshowaudiosrc_push_buffer (byte * buffer, long size, byte * src_object, +gst_dshowaudiosrc_push_buffer (byte * buffer, long size, gpointer src_object, UINT64 start, UINT64 stop) { GstDshowAudioSrc *src = GST_DSHOWAUDIOSRC (src_object); diff --git a/sys/dshowsrcwrapper/gstdshowaudiosrc.h b/sys/dshowsrcwrapper/gstdshowaudiosrc.h index b8147011..d5cfe6e0 100755 --- a/sys/dshowsrcwrapper/gstdshowaudiosrc.h +++ b/sys/dshowsrcwrapper/gstdshowaudiosrc.h @@ -28,7 +28,7 @@ #include <gst/interfaces/propertyprobe.h> #include "gstdshow.h" -#include "gstdshowinterface.h" +#include "gstdshowfakesink.h" G_BEGIN_DECLS #define GST_TYPE_DSHOWAUDIOSRC (gst_dshowaudiosrc_get_type()) @@ -59,7 +59,7 @@ struct _GstDshowAudioSrc IBaseFilter *audio_cap_filter; /* dshow fakesink filter */ - IBaseFilter *dshow_fakesink; + CDshowFakeSink *dshow_fakesink; /* graph manager interfaces */ IMediaFilter *media_filter; diff --git a/sys/dshowsrcwrapper/gstdshowfakesink.cpp b/sys/dshowsrcwrapper/gstdshowfakesink.cpp index afc0a5ef..6c0c74dc 100755 --- a/sys/dshowsrcwrapper/gstdshowfakesink.cpp +++ b/sys/dshowsrcwrapper/gstdshowfakesink.cpp @@ -21,87 +21,30 @@ #include "gstdshowfakesink.h" - -CDshowFakeSink::CDshowFakeSink() - : m_hres(S_OK), CBaseRenderer(CLSID_DshowFakeSink, "DshowFakeSink", NULL, &m_hres) -{ - m_callback = NULL; -} - -CDshowFakeSink::~CDshowFakeSink() -{ - -} - -//Object creation. -CUnknown* WINAPI CDshowFakeSink::CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr) -{ - CDshowFakeSink *pNewObject = new CDshowFakeSink(); - g_print ("CDshowFakeSink::CreateInstance\n"); - if (pNewObject == NULL) { - *pHr = E_OUTOFMEMORY; - } - return pNewObject; -} - -STDMETHODIMP CDshowFakeSink::QueryInterface(REFIID riid, void **ppvObject) -{ - if (riid == IID_IGstDshowInterface) { - *ppvObject = (IGstDshowInterface*) this; - AddRef(); - return S_OK; - } - else - return CBaseRenderer::QueryInterface (riid, ppvObject); -} - -ULONG STDMETHODCALLTYPE CDshowFakeSink::AddRef() -{ - return CBaseRenderer::AddRef(); -} - -ULONG STDMETHODCALLTYPE CDshowFakeSink::Release() +CDshowFakeSink::CDshowFakeSink() : + m_hres(S_OK), + m_callback(NULL), + m_data(NULL), + CBaseRenderer(CLSID_DshowFakeSink, "DshowFakeSink", NULL, &m_hres) { - return CBaseRenderer::Release(); } - STDMETHODIMP CDshowFakeSink::gst_set_media_type (AM_MEDIA_TYPE *pmt) { m_MediaType.Set (*pmt); return S_OK; } -STDMETHODIMP CDshowFakeSink::gst_set_buffer_callback (push_buffer_func push, byte *data) +STDMETHODIMP CDshowFakeSink::gst_set_buffer_callback (push_buffer_func push, gpointer data) { m_callback = push; m_data = data; return S_OK; } -STDMETHODIMP CDshowFakeSink::gst_push_buffer (byte *buffer, __int64 start, __int64 stop, unsigned int size, bool discount) -{ - return E_NOTIMPL; -} - -STDMETHODIMP CDshowFakeSink::gst_flush () -{ - return E_NOTIMPL; -} - -STDMETHODIMP CDshowFakeSink::gst_set_sample_size(unsigned int size) -{ - return E_NOTIMPL; -} - HRESULT CDshowFakeSink::CheckMediaType(const CMediaType *pmt) { - VIDEOINFOHEADER *p1; - VIDEOINFOHEADER *p2; - if(pmt != NULL) - { - p1 = (VIDEOINFOHEADER *)pmt->Format(); - p2 = (VIDEOINFOHEADER *)m_MediaType.Format(); + if (pmt != NULL) { if (*pmt == m_MediaType) return S_OK; } diff --git a/sys/dshowsrcwrapper/gstdshowfakesink.h b/sys/dshowsrcwrapper/gstdshowfakesink.h index 7f419b27..51291c69 100755 --- a/sys/dshowsrcwrapper/gstdshowfakesink.h +++ b/sys/dshowsrcwrapper/gstdshowfakesink.h @@ -19,32 +19,34 @@ * Boston, MA 02111-1307, USA. */ -#include "gstdshowinterface.h" +#ifndef __GST_DHOW_FAKESINK_H__ +#define __GST_DHOW_FAKESINK_H__ -class CDshowFakeSink : public CBaseRenderer, - public IGstDshowInterface +#include "gstdshow.h" + +//{6A780808-9725-4d0b-8695-A4DD8D210773} +static const GUID CLSID_DshowFakeSink = + { 0x6a780808, 0x9725, 0x4d0b, { 0x86, 0x95, 0xa4, 0xdd, 0x8d, 0x21, 0x7, 0x73 } }; + +typedef bool (*push_buffer_func) (byte *buffer, long size, gpointer src_object, UINT64 start, UINT64 stop); + +class CDshowFakeSink : public CBaseRenderer { public: CDshowFakeSink (); - virtual ~CDshowFakeSink (); - - static CUnknown * WINAPI CreateInstance (LPUNKNOWN pUnk, HRESULT *pHr); + virtual ~CDshowFakeSink () {} virtual HRESULT CheckMediaType (const CMediaType *pmt); virtual HRESULT DoRenderSample (IMediaSample *pMediaSample); - STDMETHOD (QueryInterface)(REFIID riid, void **ppvObject); - ULONG STDMETHODCALLTYPE AddRef(); - ULONG STDMETHODCALLTYPE Release(); STDMETHOD (gst_set_media_type) (AM_MEDIA_TYPE *pmt); - STDMETHOD (gst_set_buffer_callback) (push_buffer_func push, byte *data); - STDMETHOD (gst_push_buffer) (byte *buffer, __int64 start, __int64 stop, unsigned int size, bool discount); - STDMETHOD (gst_flush) (); - STDMETHOD (gst_set_sample_size) (unsigned int size); + STDMETHOD (gst_set_buffer_callback) (push_buffer_func push, gpointer data); protected: HRESULT m_hres; CMediaType m_MediaType; push_buffer_func m_callback; - byte *m_data; -};
\ No newline at end of file + gpointer m_data; +}; + +#endif /* __GST_DSHOW_FAKESINK_H__ */ diff --git a/sys/dshowsrcwrapper/gstdshowinterface.h b/sys/dshowsrcwrapper/gstdshowinterface.h deleted file mode 100755 index 68328b95..00000000 --- a/sys/dshowsrcwrapper/gstdshowinterface.h +++ /dev/null @@ -1,163 +0,0 @@ -/* GStreamer - * Copyright (C) 2007 Sebastien Moutte <sebastien@moutte.net> - * - * gstdshowinterface.h: - * - * 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_DHOW_INTERFACE_H__ -#define __GST_DHOW_INTERFACE_H__ - -#include "gstdshow.h" - -#ifdef __cplusplus -typedef bool (*push_buffer_func) (byte *buffer, long size, byte *src_object, UINT64 start, UINT64 stop); -#endif - -/* verify that the <rpcndr.h> version is high enough to compile this file*/ -#ifndef __REQUIRED_RPCNDR_H_VERSION__ -#define __REQUIRED_RPCNDR_H_VERSION__ 440 -#endif - -#include "rpc.h" -#include "rpcndr.h" - -#ifndef __RPCNDR_H_VERSION__ -#error this stub requires an updated version of <rpcndr.h> -#endif // __RPCNDR_H_VERSION__ - -#ifndef COM_NO_WINDOWS_H -#include "windows.h" -#include "ole2.h" -#endif /*COM_NO_WINDOWS_H*/ - -//{6A780808-9725-4d0b-8695-A4DD8D210773} -static const GUID CLSID_DshowFakeSink - = { 0x6a780808, 0x9725, 0x4d0b, { 0x86, 0x95, 0xa4, 0xdd, 0x8d, 0x21, 0x7, 0x73 } }; - -// {FC36764C-6CD4-4C73-900F-3F40BF3F191A} -static const GUID IID_IGstDshowInterface = - { 0xfc36764c, 0x6cd4, 0x4c73, { 0x90, 0xf, 0x3f, 0x40, 0xbf, 0x3f, 0x19, 0x1a } }; - -#define CLSID_DSHOWFAKESINK_STRING "{6A780808-9725-4d0b-8695-A4DD8D210773}" - -typedef interface IGstDshowInterface IGstDshowInterface; - -/* header files for imported files */ -#include "oaidl.h" -#include "ocidl.h" - -void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t); -void __RPC_USER MIDL_user_free( void __RPC_FAR * ); - -#ifndef __IGstDshowInterface_INTERFACE_DEFINED__ -#define __IGstDshowInterface_INTERFACE_DEFINED__ - -#if defined(__cplusplus) && !defined(CINTERFACE) - - MIDL_INTERFACE("542C0A24-8BD1-46cb-AA57-3E46D006D2F3") - IGstDshowInterface : public IUnknown - { - public: - virtual HRESULT STDMETHODCALLTYPE gst_set_media_type( - AM_MEDIA_TYPE __RPC_FAR *pmt) = 0; - - virtual HRESULT STDMETHODCALLTYPE gst_set_buffer_callback( - push_buffer_func push, byte *data) = 0; - - virtual HRESULT STDMETHODCALLTYPE gst_push_buffer( - byte *buffer, __int64 start, __int64 stop, unsigned int size, bool discount) = 0; - - virtual HRESULT STDMETHODCALLTYPE gst_flush() = 0; - - virtual HRESULT STDMETHODCALLTYPE gst_set_sample_size(unsigned int size) = 0; - }; - -#else /* C style interface */ - - typedef struct IGstDshowInterfaceVtbl - { - BEGIN_INTERFACE - - HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( - IGstDshowInterface __RPC_FAR * This, - REFIID riid, - void __RPC_FAR *__RPC_FAR *ppvObject); - - ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( - IGstDshowInterface __RPC_FAR * This); - - ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( - IGstDshowInterface __RPC_FAR * This); - - HRESULT (STDMETHODCALLTYPE *gst_set_media_type )( - IGstDshowInterface __RPC_FAR * This, - AM_MEDIA_TYPE *pmt); - - HRESULT (STDMETHODCALLTYPE *gst_set_buffer_callback) ( - IGstDshowInterface __RPC_FAR * This, - byte * push, byte *data); - - HRESULT (STDMETHODCALLTYPE *gst_push_buffer) ( - IGstDshowInterface __RPC_FAR * This, - byte *buffer, __int64 start, __int64 stop, - unsigned int size, boolean discount); - - HRESULT (STDMETHODCALLTYPE *gst_flush) ( - IGstDshowInterface __RPC_FAR * This); - - HRESULT (STDMETHODCALLTYPE *gst_set_sample_size) ( - IGstDshowInterface __RPC_FAR * This, - unsigned int size); - - END_INTERFACE - } IGstDshowInterfaceVtbl; - - interface IGstDshowInterface - { - CONST_VTBL struct IGstDshowInterfaceVtbl __RPC_FAR *lpVtbl; - }; - -#define IGstDshowInterface_QueryInterface(This,riid,ppvObject) \ - (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) - -#define IGstDshowInterface_AddRef(This) \ - (This)->lpVtbl -> AddRef(This) - -#define IGstDshowInterface_Release(This) \ - (This)->lpVtbl -> Release(This) - -#define IGstDshowInterface_gst_set_media_type(This, mediatype) \ - (This)->lpVtbl -> gst_set_media_type(This, mediatype) - -#define IGstDshowInterface_gst_set_buffer_callback(This, push, data) \ - (This)->lpVtbl -> gst_set_buffer_callback(This, push, data) - -#define IGstDshowInterface_gst_push_buffer(This, buffer, start, stop, size, discount) \ - (This)->lpVtbl -> gst_push_buffer(This, buffer, start, stop, size, discount) - -#define IGstDshowInterface_gst_flush(This) \ - (This)->lpVtbl -> gst_flush(This) - -#define IGstDshowInterface_gst_set_sample_size(This, size) \ - (This)->lpVtbl -> gst_set_sample_size(This, size) - -#endif /* C style interface */ - -#endif /* __IGstDshowInterface_INTERFACE_DEFINED__ */ - -#endif /* __GST_DSHOW_INTERFACE_H__ */
\ No newline at end of file diff --git a/sys/dshowsrcwrapper/gstdshowsrcwrapper.cpp b/sys/dshowsrcwrapper/gstdshowsrcwrapper.cpp index 1e9a8aa2..9acfc344 100755 --- a/sys/dshowsrcwrapper/gstdshowsrcwrapper.cpp +++ b/sys/dshowsrcwrapper/gstdshowsrcwrapper.cpp @@ -26,28 +26,11 @@ #include "gstdshowaudiosrc.h" #include "gstdshowvideosrc.h" -const GUID CLSID_GstreamerSrcFilter - = - { 0x6a780808, 0x9725, 0x4d0b, {0x86, 0x95, 0xa4, 0xdd, 0x8d, 0x21, 0x7, - 0x73} }; - -const GUID IID_IGstSrcInterface = - { 0x542c0a24, 0x8bd1, 0x46cb, {0xaa, 0x57, 0x3e, 0x46, 0xd0, 0x6, 0xd2, - 0xf3} }; - static gboolean plugin_init (GstPlugin * plugin) { - /* register fake filters */ - HRESULT hr = gst_dshow_register_fakefilters (); - if (FAILED (hr)) { - g_warning ("failed to register directshow fakesink filter: 0x%x\n", hr); - return FALSE; - } - if (!gst_element_register (plugin, "dshowaudiosrc", - GST_RANK_NONE, - GST_TYPE_DSHOWAUDIOSRC) || + GST_RANK_NONE, GST_TYPE_DSHOWAUDIOSRC) || !gst_element_register (plugin, "dshowvideosrc", GST_RANK_NONE, GST_TYPE_DSHOWVIDEOSRC)) return FALSE; diff --git a/sys/dshowsrcwrapper/gstdshowvideosrc.cpp b/sys/dshowsrcwrapper/gstdshowvideosrc.cpp index 67e70d96..14654f60 100755 --- a/sys/dshowsrcwrapper/gstdshowvideosrc.cpp +++ b/sys/dshowsrcwrapper/gstdshowvideosrc.cpp @@ -111,7 +111,7 @@ static GstFlowReturn gst_dshowvideosrc_create (GstPushSrc * psrc, static GstCaps *gst_dshowvideosrc_getcaps_from_streamcaps (GstDshowVideoSrc * src, IPin * pin, IAMStreamConfig * streamcaps); static gboolean gst_dshowvideosrc_push_buffer (byte * buffer, long size, - byte * src_object, UINT64 start, UINT64 stop); + gpointer src_object, UINT64 start, UINT64 stop); static void gst_dshowvideosrc_init_interfaces (GType type) @@ -614,14 +614,8 @@ gst_dshowvideosrc_start (GstBaseSrc * bsrc) goto error; } - hres = CoCreateInstance (CLSID_DshowFakeSink, NULL, CLSCTX_INPROC, - IID_IBaseFilter, (LPVOID *) & src->dshow_fakesink); - if (hres != S_OK || !src->dshow_fakesink) { - GST_CAT_ERROR (dshowvideosrc_debug, - "Can't create an instance of our dshow fakesink filter (error=0x%x)", - hres); - goto error; - } + src->dshow_fakesink = new CDshowFakeSink; + src->dshow_fakesink->AddRef(); hres = src->filter_graph->AddFilter(src->video_cap_filter, L"capture"); if (hres != S_OK) { @@ -661,7 +655,6 @@ static gboolean gst_dshowvideosrc_set_caps (GstBaseSrc * bsrc, GstCaps * caps) { HRESULT hres; - IGstDshowInterface *srcinterface = NULL; IPin *input_pin = NULL; GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (bsrc); GstStructure *s = gst_caps_get_structure (caps, 0); @@ -689,22 +682,9 @@ gst_dshowvideosrc_set_caps (GstBaseSrc * bsrc, GstCaps * caps) if (type) { pin_mediatype = (GstCapturePinMediaType *) type->data; - hres = src->dshow_fakesink->QueryInterface( - IID_IGstDshowInterface, (LPVOID *) &srcinterface); - - if (hres != S_OK || !srcinterface) { - GST_CAT_ERROR (dshowvideosrc_debug, - "Can't get IGstDshowInterface interface from our dshow fakesink filter (error=%d)", - hres); - goto error; - } - - srcinterface->gst_set_media_type(pin_mediatype->mediatype); - srcinterface->gst_set_buffer_callback( - (push_buffer_func) gst_dshowvideosrc_push_buffer, (byte *) src); - - if (srcinterface) - srcinterface->Release(); + src->dshow_fakesink->gst_set_media_type (pin_mediatype->mediatype); + src->dshow_fakesink->gst_set_buffer_callback( + (push_buffer_func) gst_dshowvideosrc_push_buffer, src); gst_dshow_get_pin_from_filter (src->dshow_fakesink, PINDIR_INPUT, &input_pin); @@ -746,9 +726,6 @@ gst_dshowvideosrc_set_caps (GstBaseSrc * bsrc, GstCaps * caps) return TRUE; error: - if (srcinterface) - srcinterface->Release(); - return FALSE; } @@ -992,7 +969,7 @@ gst_dshowvideosrc_getcaps_from_streamcaps (GstDshowVideoSrc * src, IPin * pin, } static gboolean -gst_dshowvideosrc_push_buffer (byte * buffer, long size, byte * src_object, +gst_dshowvideosrc_push_buffer (byte * buffer, long size, gpointer src_object, UINT64 start, UINT64 stop) { GstDshowVideoSrc *src = GST_DSHOWVIDEOSRC (src_object); diff --git a/sys/dshowsrcwrapper/gstdshowvideosrc.h b/sys/dshowsrcwrapper/gstdshowvideosrc.h index b7bfbb19..bf94e61f 100755 --- a/sys/dshowsrcwrapper/gstdshowvideosrc.h +++ b/sys/dshowsrcwrapper/gstdshowvideosrc.h @@ -28,7 +28,7 @@ #include <gst/interfaces/propertyprobe.h> #include "gstdshow.h" -#include "gstdshowinterface.h" +#include "gstdshowfakesink.h" // 30323449-0000-0010-8000-00AA00389B71 MEDIASUBTYPE_I420 DEFINE_GUID(MEDIASUBTYPE_I420, 0x30323449, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); @@ -62,7 +62,7 @@ struct _GstDshowVideoSrc IBaseFilter *video_cap_filter; /* dshow sink filter */ - IBaseFilter *dshow_fakesink; + CDshowFakeSink *dshow_fakesink; /* graph manager interfaces */ IMediaFilter *media_filter; diff --git a/sys/dshowsrcwrapper/libgstdshow.def b/sys/dshowsrcwrapper/libgstdshow.def deleted file mode 100755 index ee8586c9..00000000 --- a/sys/dshowsrcwrapper/libgstdshow.def +++ /dev/null @@ -1,8 +0,0 @@ -EXPORTS - DllMain PRIVATE - DllGetClassObject PRIVATE - DllCanUnloadNow PRIVATE - DllRegisterServer PRIVATE - DllUnregisterServer PRIVATE - - diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index 1aa99d1a..1fc64c54 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -88,6 +88,7 @@ check_PROGRAMS = \ $(check_kate) \ elements/aacparse \ elements/amrparse \ + elements/asfmux \ elements/camerabin \ elements/legacyresample \ elements/qtmux \ diff --git a/tests/check/elements/asfmux.c b/tests/check/elements/asfmux.c new file mode 100644 index 00000000..4d3c7073 --- /dev/null +++ b/tests/check/elements/asfmux.c @@ -0,0 +1,221 @@ +/* GStreamer + * + * unit test for asfmux + * + * Copyright (C) <2008> Thiago Santos <thiagoss@embedded.ufcg.edu.br> + * + * 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 <unistd.h> + +#include <gst/check/gstcheck.h> + +/* For ease of programming we use globals to keep refs for our floating + * src and sink pads we create; otherwise we always have to do get_pad, + * get_peer, and then remove references in every test function */ +static GstPad *mysrcpad, *mysinkpad; + +#define AUDIO_CAPS_STRING "audio/x-wma, " \ + "channels = (int) 2, " \ + "rate = (int) 8000, " \ + "wmaversion = (int) 2, " \ + "block-align = (int) 14, " \ + "bitrate = (int) 64000" + +#define VIDEO_CAPS_STRING "video/x-wmv, " \ + "width = (int) 384, " \ + "height = (int) 288, " \ + "framerate = (fraction) 25/1, " \ + "wmvversion = (int) 2" + +static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-ms-asf")); +static GstStaticPadTemplate srcvideotemplate = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (VIDEO_CAPS_STRING)); +static GstStaticPadTemplate srcaudiotemplate = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (AUDIO_CAPS_STRING)); + +GstPad * +setup_src_pad (GstElement * element, + GstStaticPadTemplate * template, GstCaps * caps, gchar * sinkname) +{ + GstPad *srcpad, *sinkpad; + + GST_DEBUG_OBJECT (element, "setting up sending pad"); + /* sending pad */ + srcpad = gst_pad_new_from_static_template (template, "src"); + fail_if (srcpad == NULL, "Could not create a srcpad"); + ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 1); + + if (!(sinkpad = gst_element_get_static_pad (element, sinkname))) + sinkpad = gst_element_get_request_pad (element, sinkname); + fail_if (sinkpad == NULL, "Could not get sink pad from %s", + GST_ELEMENT_NAME (element)); + /* references are owned by: 1) us, 2) asfmux, 3) collect pads */ + ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3); + if (caps) + fail_unless (gst_pad_set_caps (srcpad, caps)); + fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK, + "Could not link source and %s sink pads", GST_ELEMENT_NAME (element)); + gst_object_unref (sinkpad); /* because we got it higher up */ + + /* references are owned by: 1) asfmux, 2) collect pads */ + ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2); + + return srcpad; +} + +void +teardown_src_pad (GstElement * element, gchar * sinkname) +{ + GstPad *srcpad, *sinkpad; + gchar *padname; + + /* clean up floating src pad */ + /* hm, asfmux uses _01 as suffixes for padnames */ + padname = g_strdup (sinkname); + memcpy (strchr (padname, '%'), "01", 2); + if (!(sinkpad = gst_element_get_static_pad (element, padname))) + sinkpad = gst_element_get_request_pad (element, padname); + g_free (padname); + + fail_if (sinkpad == NULL, "sinkpad is null"); + + /* pad refs held by 1) asfmux 2) collectpads and 3) us (through _get) */ + ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3); + fail_unless (gst_pad_is_linked (sinkpad)); + srcpad = gst_pad_get_peer (sinkpad); + + fail_if (srcpad == NULL, "Couldn't get srcpad"); + gst_pad_unlink (srcpad, sinkpad); + + /* after unlinking, pad refs still held by + * 1) asfmux and 2) collectpads and 3) us (through _get) */ + ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3); + gst_object_unref (sinkpad); + /* one more ref is held by element itself */ + + /* pad refs held by both creator and this function (through _get_peer) */ + ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 2); + gst_object_unref (srcpad); + gst_object_unref (srcpad); +} + +GstElement * +setup_asfmux (GstStaticPadTemplate * srctemplate, gchar * sinkname) +{ + GstElement *asfmux; + + GST_DEBUG ("setup_asfmux"); + asfmux = gst_check_setup_element ("asfmux"); + + mysrcpad = setup_src_pad (asfmux, srctemplate, NULL, sinkname); + mysinkpad = gst_check_setup_sink_pad (asfmux, &sinktemplate, NULL); + gst_pad_set_active (mysrcpad, TRUE); + gst_pad_set_active (mysinkpad, TRUE); + return asfmux; +} + +void +cleanup_asfmux (GstElement * asfmux, gchar * sinkname) +{ + GST_DEBUG ("cleanup_asfmux"); + gst_element_set_state (asfmux, GST_STATE_NULL); + gst_pad_set_active (mysrcpad, FALSE); + gst_pad_set_active (mysinkpad, FALSE); + teardown_src_pad (asfmux, sinkname); + gst_check_teardown_sink_pad (asfmux); + gst_check_teardown_element (asfmux); +} + +void +check_asfmux_pad (GstStaticPadTemplate * srctemplate, gchar * src_caps_string, + gchar * sinkname) +{ + GstElement *asfmux; + GstBuffer *inbuffer; + GstCaps *caps; + GstFlowReturn ret; + + asfmux = setup_asfmux (srctemplate, sinkname); + fail_unless (gst_element_set_state (asfmux, + GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, + "could not set to playing"); + + inbuffer = gst_buffer_new_and_alloc (1); + caps = gst_caps_from_string (src_caps_string); + gst_buffer_set_caps (inbuffer, caps); + gst_caps_unref (caps); + GST_BUFFER_TIMESTAMP (inbuffer) = 0; + ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1); + ret = gst_pad_push (mysrcpad, inbuffer); + fail_unless (ret == GST_FLOW_OK, "Pad push returned: %d", ret); + + cleanup_asfmux (asfmux, sinkname); + g_list_free (buffers); + buffers = NULL; +} + +GST_START_TEST (test_video_pad) +{ + check_asfmux_pad (&srcvideotemplate, VIDEO_CAPS_STRING, "video_%d"); +} + +GST_END_TEST; + +GST_START_TEST (test_audio_pad) +{ + check_asfmux_pad (&srcaudiotemplate, AUDIO_CAPS_STRING, "audio_%d"); +} + +GST_END_TEST; + +Suite * +asfmux_suite (void) +{ + Suite *s = suite_create ("asfmux"); + TCase *tc_chain = tcase_create ("general"); + tcase_add_test (tc_chain, test_video_pad); + tcase_add_test (tc_chain, test_audio_pad); + + suite_add_tcase (s, tc_chain); + + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = asfmux_suite (); + SRunner *sr = srunner_create (s); + + gst_check_init (&argc, &argv); + + srunner_run_all (sr, CK_NORMAL); + nf = srunner_ntests_failed (sr); + srunner_free (sr); + + return nf; +} diff --git a/win32/MANIFEST b/win32/MANIFEST index 647176e4..a0d954aa 100644 --- a/win32/MANIFEST +++ b/win32/MANIFEST @@ -2,15 +2,11 @@ win32/MANIFEST win32/common/config.h win32/common/config.h.in win32/vs6/gst_plugins_bad.dsw -win32/vs6/libgstdirectdraw.dsp win32/vs6/libgstdshow.dsp win32/vs6/libgstdshowdecwrapper.dsp -win32/vs6/libdshowsrcwrapper.dsp win32/vs6/libgstflv.dsp win32/vs6/libgstmpegvideoparse.dsp win32/vs6/libgstneon.dsp -win32/vs7/gst-plugins-bad.sln -win32/vs7/libgstdirectdraw.vcproj win32/vs8/gst-plugins-bad.sln win32/vs8/libgstdirectdraw.vcproj -win32/common/libgstdshow.def +win32/vs8/libdshowsrcwrapper.vcproj diff --git a/win32/vs6/gst_plugins_bad.dsw b/win32/vs6/gst_plugins_bad.dsw index 388fd822..6d0c9b5c 100755 --- a/win32/vs6/gst_plugins_bad.dsw +++ b/win32/vs6/gst_plugins_bad.dsw @@ -15,30 +15,6 @@ Package=<4> ###############################################################################
-Project: "libgstdshow"=".\libgstdshow.dsp" - Package Owner=<4>
-
-Package=<5>
-{{{
-}}}
-
-Package=<4>
-{{{
-}}}
-
-###############################################################################
-
-Project: "libgstdshowdecwrapper"=".\libgstdshowdecwrapper.dsp" - Package Owner=<4>
-
-Package=<5>
-{{{
-}}}
-
-Package=<4>
-{{{
-}}}
-
-###############################################################################
-
Project: "libgstflv"=".\libgstflv.dsp" - Package Owner=<4>
Package=<5>
diff --git a/win32/vs7/gst-plugins-bad.sln b/win32/vs7/gst-plugins-bad.sln deleted file mode 100644 index 2df4288f..00000000 --- a/win32/vs7/gst-plugins-bad.sln +++ /dev/null @@ -1,29 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 8.00 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libgstdirectsound", "libgstdirectsound.vcproj", "{566A2EB9-984C-4027-86DD-EDC7B390C679}" - ProjectSection(ProjectDependencies) = postProject - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libgstdirectdraw", "libgstdirectdraw.vcproj", "{1594A623-5529-4B86-BD4A-694CF0BDB5C4}" - ProjectSection(ProjectDependencies) = postProject - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfiguration) = preSolution - Debug = Debug - Release = Release - EndGlobalSection - GlobalSection(ProjectConfiguration) = postSolution - {566A2EB9-984C-4027-86DD-EDC7B390C679}.Debug.ActiveCfg = Debug|Win32 - {566A2EB9-984C-4027-86DD-EDC7B390C679}.Debug.Build.0 = Debug|Win32 - {566A2EB9-984C-4027-86DD-EDC7B390C679}.Release.ActiveCfg = Release|Win32 - {566A2EB9-984C-4027-86DD-EDC7B390C679}.Release.Build.0 = Release|Win32 - {1594A623-5529-4B86-BD4A-694CF0BDB5C4}.Debug.ActiveCfg = Debug|Win32 - {1594A623-5529-4B86-BD4A-694CF0BDB5C4}.Debug.Build.0 = Debug|Win32 - {1594A623-5529-4B86-BD4A-694CF0BDB5C4}.Release.ActiveCfg = Release|Win32 - {1594A623-5529-4B86-BD4A-694CF0BDB5C4}.Release.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - EndGlobalSection - GlobalSection(ExtensibilityAddIns) = postSolution - EndGlobalSection -EndGlobal diff --git a/win32/vs7/libgstdirectdraw.vcproj b/win32/vs7/libgstdirectdraw.vcproj deleted file mode 100644 index 553d0c08..00000000 --- a/win32/vs7/libgstdirectdraw.vcproj +++ /dev/null @@ -1,145 +0,0 @@ -<?xml version="1.0" encoding="windows-1251"?> -<VisualStudioProject - ProjectType="Visual C++" - Version="7.10" - Name="libgstdirectdraw" - ProjectGUID="{1594A623-5529-4B86-BD4A-694CF0BDB5C4}" - Keyword="Win32Proj"> - <Platforms> - <Platform - Name="Win32"/> - </Platforms> - <Configurations> - <Configuration - Name="Debug|Win32" - OutputDirectory="Debug" - IntermediateDirectory="Debug" - ConfigurationType="2" - CharacterSet="2"> - <Tool - Name="VCCLCompilerTool" - Optimization="0" - AdditionalIncludeDirectories="../../../gstreamer,../../../gst-plugins-base/gst-libs,../../../gstreamer/libs,../../../gstreamer/win32/common" - PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBGSTDIRECTDRAW_EXPORTS;HAVE_CONFIG_H" - MinimalRebuild="TRUE" - BasicRuntimeChecks="3" - RuntimeLibrary="1" - UsePrecompiledHeader="0" - WarningLevel="3" - Detect64BitPortabilityProblems="TRUE" - DebugInformationFormat="4"/> - <Tool - Name="VCCustomBuildTool"/> - <Tool - Name="VCLinkerTool" - AdditionalDependencies="ddraw.lib libgstvideo-0.10.lib libgstreamer-0.10.lib libgstbase-0.10.lib glib-2.0.lib gobject-2.0.lib" - OutputFile="$(OutDir)/libgstdirectdraw.dll" - LinkIncremental="2" - AdditionalLibraryDirectories="../../../gstreamer/win32/vs7/$(OutDir);../../../gst-plugins-base/win32/vs7/$(OutDir)" - GenerateDebugInformation="TRUE" - ProgramDatabaseFile="$(OutDir)/libgstdirectdraw.pdb" - SubSystem="2" - ImportLibrary="$(OutDir)/libgstdirectdraw.lib" - TargetMachine="1"/> - <Tool - Name="VCMIDLTool"/> - <Tool - Name="VCPostBuildEventTool" - CommandLine="copy /y "$(TargetPath)" c:\gstreamer\debug\lib\gstreamer-0.10"/> - <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="Release" - IntermediateDirectory="Release" - ConfigurationType="2" - CharacterSet="2"> - <Tool - Name="VCCLCompilerTool" - AdditionalIncludeDirectories="../../../gstreamer,../../../gst-plugins-base/gst-libs,../../../gstreamer/libs,../../../gstreamer/win32/common" - PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBGSTDIRECTDRAW_EXPORTS;HAVE_CONFIG_H" - RuntimeLibrary="0" - UsePrecompiledHeader="0" - WarningLevel="3" - Detect64BitPortabilityProblems="TRUE" - DebugInformationFormat="3"/> - <Tool - Name="VCCustomBuildTool"/> - <Tool - Name="VCLinkerTool" - AdditionalDependencies="ddraw.lib libgstvideo-0.10.lib libgstreamer-0.10.lib libgstbase-0.10.lib glib-2.0.lib gobject-2.0.lib" - OutputFile="$(OutDir)/libgstdirectdraw.dll" - LinkIncremental="1" - AdditionalLibraryDirectories="../../../gstreamer/win32/vs7/$(OutDir);../../../gst-plugins-base/win32/vs7/$(OutDir)" - GenerateDebugInformation="TRUE" - SubSystem="2" - OptimizeReferences="2" - EnableCOMDATFolding="2" - ImportLibrary="$(OutDir)/libgstdirectdraw.lib" - TargetMachine="1"/> - <Tool - Name="VCMIDLTool"/> - <Tool - Name="VCPostBuildEventTool" - CommandLine="copy /y "$(TargetPath)" c:\gstreamer\lib\gstreamer-0.10"/> - <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="..\..\sys\directdraw\gstdirectdrawplugin.c"> - </File> - <File - RelativePath="..\..\sys\directdraw\gstdirectdrawsink.c"> - </File> - </Filter> - <Filter - Name="Header Files" - Filter="h;hpp;hxx;hm;inl;inc;xsd" - UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"> - </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/win32/vs8/libdshowsrcwrapper.vcproj b/win32/vs8/libdshowsrcwrapper.vcproj index d18e66f8..a87b8603 100755 --- a/win32/vs8/libdshowsrcwrapper.vcproj +++ b/win32/vs8/libdshowsrcwrapper.vcproj @@ -49,7 +49,7 @@ Optimization="2"
InlineFunctionExpansion="1"
AdditionalIncludeDirectories="C:\msys\1.0\local\include;C:\msys\1.0\local\include\libxml2;"C:\msys\1.0\local\include\glib-2.0";"C:\msys\1.0\local\lib\glib-2.0\include";"C:\msys\1.0\local\include\gstreamer-0.10";..\common;"C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Samples\Multimedia\DirectShow\BaseClasses""
- PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;_USRDLL;DSHOWSRCWRAPPER_EXPORTS;HAVE_CONFIG_H;COBJMACROS;_WIN32_DCOM;_CRT_SECURE_NO_DEPRECATE"
+ PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;_USRDLL;DSHOWSRCWRAPPER_EXPORTS;HAVE_CONFIG_H;_CRT_SECURE_NO_DEPRECATE"
StringPooling="true"
RuntimeLibrary="2"
EnableFunctionLevelLinking="true"
@@ -78,7 +78,7 @@ LinkIncremental="1"
SuppressStartupBanner="true"
AdditionalLibraryDirectories="C:\msys\1.0\local\lib;"C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Lib";"C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Samples\Multimedia\DirectShow\BaseClasses\XP32_RETAIL""
- ModuleDefinitionFile="..\..\sys\dshowsrcwrapper\libgstdshow.def"
+ ModuleDefinitionFile=""
ProgramDatabaseFile=".\Release/libgstdshowsrcwrapper.pdb"
ImportLibrary=".\Release/libgstdshowsrcwrapper.lib"
TargetMachine="1"
@@ -146,7 +146,7 @@ Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="C:\msys\1.0\local\include;C:\msys\1.0\local\include\libxml2;"C:\msys\1.0\local\include\glib-2.0";"C:\msys\1.0\local\lib\glib-2.0\include";"C:\msys\1.0\local\include\gstreamer-0.10";..\common;"C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Samples\Multimedia\DirectShow\BaseClasses""
- PreprocessorDefinitions="_DEBUG;WIN32;_WINDOWS;_USRDLL;DSHOWSRCWRAPPER_EXPORTS;HAVE_CONFIG_H;COBJMACROS;_WIN32_DCOM;_CRT_SECURE_NO_DEPRECATE"
+ PreprocessorDefinitions="_DEBUG;WIN32;_WINDOWS;_USRDLL;DSHOWSRCWRAPPER_EXPORTS;HAVE_CONFIG_H;_CRT_SECURE_NO_DEPRECATE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
@@ -177,7 +177,7 @@ LinkIncremental="2"
SuppressStartupBanner="true"
AdditionalLibraryDirectories="C:\msys\1.0\local\lib;"C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Lib";"C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Samples\Multimedia\DirectShow\BaseClasses\XP32_DEBUG""
- ModuleDefinitionFile="..\..\sys\dshowsrcwrapper\libgstdshow.def"
+ ModuleDefinitionFile=""
GenerateDebugInformation="true"
ProgramDatabaseFile=".\Debug/libgstdshowsrcwrapper.pdb"
ImportLibrary=".\Debug/libgstdshowsrcwrapper.lib"
@@ -239,10 +239,6 @@ RelativePath="..\..\sys\dshowsrcwrapper\gstdshowvideosrc.cpp"
>
</File>
- <File
- RelativePath="..\..\sys\dshowsrcwrapper\libgstdshow.def"
- >
- </File>
</Filter>
<Filter
Name="Header Files"
@@ -261,10 +257,6 @@ >
</File>
<File
- RelativePath="..\..\sys\dshowsrcwrapper\gstdshowinterface.h"
- >
- </File>
- <File
RelativePath="..\..\sys\dshowsrcwrapper\gstdshowvideosrc.h"
>
</File>
diff --git a/win32/vs8/libgstdirectdraw.vcproj b/win32/vs8/libgstdirectdraw.vcproj index 31af1ca9..0e64b45f 100644..100755 --- a/win32/vs8/libgstdirectdraw.vcproj +++ b/win32/vs8/libgstdirectdraw.vcproj @@ -40,14 +40,14 @@ <Tool Name="VCCLCompilerTool" Optimization="0" - AdditionalIncludeDirectories="../../../gstreamer,../../../gst-plugins-base/gst-libs,../../../gstreamer/libs,../../../gstreamer/win32/common" + AdditionalIncludeDirectories="C:\msys\1.0\local\include;C:\msys\1.0\local\include\libxml2;"C:\msys\1.0\local\include\glib-2.0";"C:\msys\1.0\local\lib\glib-2.0\include";"C:\msys\1.0\local\include\gstreamer-0.10";..\common;"$(DXSDK_DIR)Include"" PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBGSTDIRECTDRAW_EXPORTS;HAVE_CONFIG_H" MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="3" UsePrecompiledHeader="0" WarningLevel="3" - Detect64BitPortabilityProblems="true" + Detect64BitPortabilityProblems="false" DebugInformationFormat="4" /> <Tool @@ -61,10 +61,10 @@ /> <Tool Name="VCLinkerTool" - AdditionalDependencies="ddraw.lib libgstvideo-0.10.lib libgstreamer-0.10.lib libgstbase-0.10.lib glib-2.0.lib gobject-2.0.lib user32.lib gdi32.lib" + AdditionalDependencies="ddraw.lib libgstinterfaces-0.10.lib libgstvideo-0.10.lib libgstreamer-0.10.lib libgstbase-0.10.lib glib-2.0.lib gobject-2.0.lib user32.lib gdi32.lib dxguid.lib" OutputFile="$(OutDir)/libgstdirectdraw.dll" LinkIncremental="2" - AdditionalLibraryDirectories="../../../gstreamer/win32/vs8/$(ConfigurationName);../../../gst-plugins-base/win32/vs8/$(ConfigurationName)" + AdditionalLibraryDirectories="C:\msys\1.0\local\lib;"$(DXSDK_DIR)Lib\x86"" GenerateDebugInformation="true" ProgramDatabaseFile="$(OutDir)/libgstdirectdraw.pdb" SubSystem="2" @@ -94,7 +94,7 @@ /> <Tool Name="VCPostBuildEventTool" - CommandLine="copy /y "$(TargetPath)" c:\gstreamer\debug\lib\gstreamer-0.10" + CommandLine="copy /Y Debug\libgstdirectdraw.dll c:\msys\1.0\local\lib\gstreamer-0.10" /> </Configuration> <Configuration @@ -122,12 +122,12 @@ /> <Tool Name="VCCLCompilerTool" - AdditionalIncludeDirectories="../../../gstreamer,../../../gst-plugins-base/gst-libs,../../../gstreamer/libs,../../../gstreamer/win32/common" + AdditionalIncludeDirectories="C:\msys\1.0\local\include;C:\msys\1.0\local\include\libxml2;"C:\msys\1.0\local\include\glib-2.0";"C:\msys\1.0\local\lib\glib-2.0\include";"C:\msys\1.0\local\include\gstreamer-0.10";..\common;"$(DXSDK_DIR)Include"" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBGSTDIRECTDRAW_EXPORTS;HAVE_CONFIG_H" RuntimeLibrary="0" UsePrecompiledHeader="0" WarningLevel="3" - Detect64BitPortabilityProblems="true" + Detect64BitPortabilityProblems="false" DebugInformationFormat="3" /> <Tool @@ -141,10 +141,10 @@ /> <Tool Name="VCLinkerTool" - AdditionalDependencies="ddraw.lib libgstvideo-0.10.lib libgstreamer-0.10.lib libgstbase-0.10.lib glib-2.0.lib gobject-2.0.lib user32.lib gdi32.lib" + AdditionalDependencies="ddraw.lib libgstinterfaces-0.10.lib libgstvideo-0.10.lib libgstreamer-0.10.lib libgstbase-0.10.lib glib-2.0.lib gobject-2.0.lib user32.lib gdi32.lib dxguid.lib" OutputFile="$(OutDir)/libgstdirectdraw.dll" LinkIncremental="1" - AdditionalLibraryDirectories="../../../gstreamer/win32/vs8/$(ConfigurationName);../../../gst-plugins-base/win32/vs8/$(ConfigurationName)" + AdditionalLibraryDirectories="C:\msys\1.0\local\lib;"$(DXSDK_DIR)Lib\x86"" GenerateDebugInformation="true" SubSystem="2" OptimizeReferences="2" @@ -175,7 +175,7 @@ /> <Tool Name="VCPostBuildEventTool" - CommandLine="copy /y "$(TargetPath)" c:\gstreamer\lib\gstreamer-0.10" + CommandLine="copy /Y Release\libgstdirectdraw.dll c:\msys\1.0\local\lib\gstreamer-0.10" /> </Configuration> </Configurations> @@ -201,6 +201,10 @@ Filter="h;hpp;hxx;hm;inl;inc;xsd" UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > + <File + RelativePath="..\..\sys\directdraw\gstdirectdrawsink.h" + > + </File> </Filter> <Filter Name="Resource Files" |