diff --git a/common b/common index a39eb835fb..11f0cd5a3f 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit a39eb835fb3be2a4c5a6a89b5ca5cc064e79b2e2 +Subproject commit 11f0cd5a3fba36f85cf3e434150bfe66b1bf08d4 diff --git a/docs/libs/Makefile.am b/docs/libs/Makefile.am index 4014d8b9b7..83e8039360 100644 --- a/docs/libs/Makefile.am +++ b/docs/libs/Makefile.am @@ -13,43 +13,17 @@ FORMATS=html html: html-build.stamp include $(top_srcdir)/common/upload-doc.mak -# generated basefiles -#basefiles = \ -## $(DOC_MODULE).types \ -# $(DOC_MODULE)-sections.txt \ -# $(DOC_MODULE)-docs.sgml - -# ugly hack to make -unused.sgml work -#unused-build.stamp: -# BUILDDIR=`pwd` && \ -# cd $(srcdir)/tmpl && \ -# ln -sf gstreamer-libs-unused.sgml \ -# $$BUILDDIR/tmpl/gstreamer-libs-@GST_MAJORMINOR@-unused.sgml -# touch unused-build.stamp - -# these rules are added to create parallel docs using GST_MAJORMINOR -#$(basefiles): gstreamer-libs-@GST_MAJORMINOR@%: gstreamer-libs% -# cp $< $@ - -#CLEANFILES = $(basefiles) - # The top-level SGML file. Change it if you want. DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml -# The directory containing the source code. Relative to $(top_srcdir). +# The directory containing the source code. # gtk-doc will search all .c & .h files beneath here for inline comments # documenting functions and macros. DOC_SOURCE_DIR=$(top_srcdir)/gst-libs/gst -DOC_BUILD_DIR=$(top_builddir)/gst-libs/gst # Extra options to supply to gtkdoc-scan. SCAN_OPTIONS=--deprecated-guards="GST_DISABLE_DEPRECATED" -# FIXME : -# there's something wrong with gstreamer-sections.txt not being in the dist -# maybe it doesn't resolve; we're adding it below for now -#EXTRA_DIST = gstreamer.types.in gstreamer.hierarchy $(DOC_MODULE)-sections.txt gstreamer-sections.txt $(DOC_MAIN_SGML_FILE) - # Extra options to supply to gtkdoc-mkdb. MKDB_OPTIONS=--sgml-mode --output-format=xml @@ -58,25 +32,8 @@ FIXXREF_OPTIONS=--extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html \ --extra-dir=$(GST_PREFIX)/share/gtk-doc/html # Used for dependencies. -HFILE_GLOB=$(DOC_SOURCE_DIR)/*/*.h -CFILE_GLOB=$(DOC_SOURCE_DIR)/*/*.c - -# this is a wingo addition -# thomasvs: another nice wingo addition would be an explanation on why -# this is useful ;) - -SCANOBJ_DEPS = \ - $(top_builddir)/gst-libs/gst/interfaces/libgstinterfaces-@GST_MAJORMINOR@.la \ - $(top_builddir)/gst-libs/gst/audio/libgstaudio-@GST_MAJORMINOR@.la \ - $(top_builddir)/gst-libs/gst/cdda/libgstcdda-@GST_MAJORMINOR@.la \ - $(top_builddir)/gst-libs/gst/fft/libgstfft-@GST_MAJORMINOR@.la \ - $(top_builddir)/gst-libs/gst/rtp/libgstrtp-@GST_MAJORMINOR@.la \ - $(top_builddir)/gst-libs/gst/rtsp/libgstrtsp-@GST_MAJORMINOR@.la \ - $(top_builddir)/gst-libs/gst/sdp/libgstsdp-@GST_MAJORMINOR@.la \ - $(top_builddir)/gst-libs/gst/tag/libgsttag-@GST_MAJORMINOR@.la \ - $(top_builddir)/gst-libs/gst/video/libgstvideo-@GST_MAJORMINOR@.la \ - $(top_builddir)/gst-libs/gst/app/libgstapp-@GST_MAJORMINOR@.la \ - $(top_builddir)/gst-libs/gst/pbutils/libgstpbutils-@GST_MAJORMINOR@.la +HFILE_GLOB=$(top_srcdir)/gst-libs/gst/*/*.h +CFILE_GLOB=$(top_srcdir)/gst-libs/gst/*/*.c # Header files to ignore when scanning. IGNORE_HFILES = pbutils-private.h gsttageditingprivate.h id3v2.h \ @@ -99,14 +56,24 @@ extra_files = # CFLAGS and LDFLAGS for compiling scan program. Only needed if your app/lib # contains GtkObjects/GObjects and you want to document signals and properties. GTKDOC_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) -DGST_USE_UNSTABLE_API -GTKDOC_LIBS = $(SCANOBJ_DEPS) $(GST_BASE_LIBS) +GTKDOC_LIBS = \ + $(top_builddir)/gst-libs/gst/interfaces/libgstinterfaces-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/audio/libgstaudio-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/cdda/libgstcdda-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/fft/libgstfft-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/rtp/libgstrtp-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/rtsp/libgstrtsp-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/sdp/libgstsdp-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/tag/libgsttag-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/video/libgstvideo-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/app/libgstapp-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/pbutils/libgstpbutils-@GST_MAJORMINOR@.la \ + $(GST_BASE_LIBS) GTKDOC_CC=$(LIBTOOL) --tag=CC --mode=compile $(CC) GTKDOC_LD=$(LIBTOOL) --tag=CC --mode=link $(CC) # If you need to override some of the declarations, place them in this file -# and uncomment this line. -#DOC_OVERRIDES = $(DOC_MODULE)-overrides.txt -DOC_OVERRIDES = +DOC_OVERRIDES = $(DOC_MODULE)-overrides.txt include $(top_srcdir)/common/gtk-doc.mak diff --git a/docs/plugins/Makefile.am b/docs/plugins/Makefile.am index f304621a16..60111f334b 100644 --- a/docs/plugins/Makefile.am +++ b/docs/plugins/Makefile.am @@ -13,33 +13,13 @@ FORMATS=html html: html-build.stamp include $(top_srcdir)/common/upload-doc.mak -# generated basefiles -#basefiles = \ -## $(DOC_MODULE).types \ -# $(DOC_MODULE)-sections.txt \ -# $(DOC_MODULE)-docs.sgml - -# ugly hack to make -unused.sgml work -#unused-build.stamp: -# BUILDDIR=`pwd` && \ -# cd $(srcdir)/tmpl && \ -# ln -sf gstreamer-libs-unused.sgml \ -# $$BUILDDIR/tmpl/gstreamer-libs-@GST_MAJORMINOR@-unused.sgml -# touch unused-build.stamp - -# these rules are added to create parallel docs using GST_MAJORMINOR -#$(basefiles): gstreamer-libs-@GST_MAJORMINOR@%: gstreamer-libs% -# cp $< $@ - -#CLEANFILES = $(basefiles) - # The top-level SGML file. Change it if you want. DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml -# The directory containing the source code. Relative to $(top_srcdir). +# The directory containing the source code. # gtk-doc will search all .c & .h files beneath here for inline comments # documenting functions and macros. -DOC_SOURCE_DIR = $(top_srcdir) +DOC_SOURCE_DIR = $(top_srcdir)/gst $(top_srcdir)/ext $(top_srcdir)/sys # Extra options to supply to gtkdoc-scan. SCAN_OPTIONS= @@ -54,14 +34,8 @@ FIXXREF_OPTIONS=--extra-dir=$(top_builddir)/docs/libs/html \ --extra-dir=$(datadir)/gtk-doc/html # Used for dependencies. -HFILE_GLOB=$(DOC_SOURCE_DIR)/*/*/*.h -CFILE_GLOB=$(DOC_SOURCE_DIR)/*/*/*.c - -# this is a wingo addition -# thomasvs: another nice wingo addition would be an explanation on why -# this is useful ;) - -SCANOBJ_DEPS = +HFILE_GLOB=$(top_srcdir)/gst/*/*.h $(top_srcdir)/ext/*/*.h $(top_srcdir)/sys/*/*.h +CFILE_GLOB=$(top_srcdir)/gst/*/*.c $(top_srcdir)/ext/*/*.c $(top_srcdir)/sys/*/*.c # Header files to ignore when scanning. IGNORE_HFILES = avcodec.h dsputil.h arch.h speex_resampler.h speex_resampler_wrapper.h fixed_arm4.h fixed_arm5e.h fixed_bfin.h fixed_debug.h fixed_generic.h resample_sse.h @@ -136,14 +110,12 @@ extra_files = # CFLAGS and LDFLAGS for compiling scan program. Only needed if your app/lib # contains GtkObjects/GObjects and you want to document signals and properties. GTKDOC_CFLAGS = $(GST_BASE_CFLAGS) -I$(top_builddir) -I$(top_builddir)/gst-libs -GTKDOC_LIBS = $(SCANOBJ_DEPS) $(GST_BASE_LIBS) +GTKDOC_LIBS = $(GST_BASE_LIBS) GTKDOC_CC=$(LIBTOOL) --tag=CC --mode=compile $(CC) GTKDOC_LD=$(LIBTOOL) --tag=CC --mode=link $(CC) # If you need to override some of the declarations, place them in this file -# and uncomment this line. -#DOC_OVERRIDES = $(DOC_MODULE)-overrides.txt -DOC_OVERRIDES = +DOC_OVERRIDES = $(DOC_MODULE)-overrides.txt include $(top_srcdir)/common/gtk-doc-plugins.mak diff --git a/ext/theora/gsttheoraenc.c b/ext/theora/gsttheoraenc.c index 9dc8387f30..c23c19715d 100644 --- a/ext/theora/gsttheoraenc.c +++ b/ext/theora/gsttheoraenc.c @@ -488,7 +488,13 @@ theora_enc_reset (GstTheoraEnc * enc) GST_OBJECT_LOCK (enc); enc->info.target_bitrate = enc->video_bitrate; - enc->info.quality = enc->video_quality; + if (enc->quality_changed) { + enc->info.quality = enc->video_quality; + } else { + if (enc->video_bitrate == 0) { + enc->info.quality = enc->video_quality; + } + } enc->bitrate_changed = FALSE; enc->quality_changed = FALSE; GST_OBJECT_UNLOCK (enc); diff --git a/gst-plugins-base.spec.in b/gst-plugins-base.spec.in index 9a9d4212fc..9a0f9d70f0 100644 --- a/gst-plugins-base.spec.in +++ b/gst-plugins-base.spec.in @@ -220,6 +220,9 @@ GStreamer Plugins Base library development and header files. %{_includedir}/gstreamer-%{majorminor}/gst/pbutils/gstpluginsbaseversion.h %{_includedir}/gstreamer-%{majorminor}/gst/tag/xmpwriter.h %{_includedir}/gstreamer-%{majorminor}/gst/audio/gstaudioiec61937.h +%{_includedir}/gstreamer-%{majorminor}/gst/audio/gstaudiodecoder.h +%{_includedir}/gstreamer-%{majorminor}/gst/audio/gstaudioencoder.h +%{_includedir}/gstreamer-%{majorminor}/gst/tag/gsttagmux.h %{_libdir}/libgstfft-%{majorminor}.so %{_libdir}/libgstrtsp-%{majorminor}.so @@ -280,6 +283,7 @@ GStreamer Plugins Base library development and header files. # gtk-doc documentation %doc %{_datadir}/gtk-doc/html/gst-plugins-base-libs-%{majorminor} %doc %{_datadir}/gtk-doc/html/gst-plugins-base-plugins-%{majorminor} +%doc %{_datadir}/gst-plugins-base/license-translations.dict %changelog * Sun Aug 07 2011 Thomas Vander Stichele diff --git a/gst/audiotestsrc/gstaudiotestsrc.h b/gst/audiotestsrc/gstaudiotestsrc.h index 29c4285331..ac31392523 100644 --- a/gst/audiotestsrc/gstaudiotestsrc.h +++ b/gst/audiotestsrc/gstaudiotestsrc.h @@ -53,6 +53,8 @@ G_BEGIN_DECLS * @GST_AUDIO_TEST_SRC_WAVE_TICKS: periodic ticks * @GST_AUDIO_TEST_SRC_WAVE_GAUSSIAN_WHITE_NOISE: white (zero mean) Gaussian noise; volume sets the standard deviation of the noise in units of the range of values of the sample type, e.g. volume=0.1 produces noise with a standard deviation of 0.1*32767=3277 with 16-bit integer samples, or 0.1*1.0=0.1 with floating-point samples. * @GST_AUDIO_TEST_SRC_WAVE_RED_NOISE: red (brownian) noise + * @GST_AUDIO_TEST_SRC_WAVE_BLUE_NOISE: spectraly inverted pink noise + * @GST_AUDIO_TEST_SRC_WAVE_VIOLET_NOISE: spectraly inverted red (brownian) noise * * Different types of supported sound waves. */ diff --git a/gst/playback/gstdecodebin2.c b/gst/playback/gstdecodebin2.c index f0bdb3e262..9a877bb9be 100644 --- a/gst/playback/gstdecodebin2.c +++ b/gst/playback/gstdecodebin2.c @@ -1339,13 +1339,14 @@ analyze_new_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad, GstDecodePad *dpad; GstElementFactory *factory; const gchar *classification; - gboolean is_parser_converter; + gboolean is_parser_converter = FALSE; GST_DEBUG_OBJECT (dbin, "Pad %s:%s caps:%" GST_PTR_FORMAT, GST_DEBUG_PAD_NAME (pad), caps); if (chain->elements - && src != ((GstDecodeElement *) chain->elements->data)->element) { + && src != ((GstDecodeElement *) chain->elements->data)->element + && src != ((GstDecodeElement *) chain->elements->data)->capsfilter) { GST_ERROR_OBJECT (dbin, "New pad from not the last element in this chain"); return; } @@ -1516,7 +1517,7 @@ analyze_new_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad, for (i = 0; i < factories->n_values; i++) { GstElementFactory *factory = g_value_get_object (g_value_array_get_nth (factories, i)); - GstCaps *tcaps; + GstCaps *tcaps, *intersection; const GList *tmps; GST_DEBUG ("Trying factory %s", @@ -1533,7 +1534,9 @@ analyze_new_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad, if (st->direction != GST_PAD_SINK || st->presence != GST_PAD_ALWAYS) continue; tcaps = gst_static_pad_template_get_caps (st); - gst_caps_merge (filter_caps, gst_caps_copy (tcaps)); + intersection = + gst_caps_intersect_full (tcaps, caps, GST_CAPS_INTERSECT_FIRST); + gst_caps_merge (filter_caps, intersection); gst_caps_unref (tcaps); } } @@ -1554,6 +1557,11 @@ analyze_new_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad, p = gst_element_get_static_pad (delem->capsfilter, "src"); gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (dpad), p); pad = p; + + if (!gst_caps_is_fixed (caps)) { + g_value_array_free (factories); + goto non_fixed; + } } /* 1.h else continue autoplugging something from the list. */ @@ -1664,6 +1672,12 @@ setup_caps_delay: g_signal_connect (G_OBJECT (pad), "notify::caps", G_CALLBACK (caps_notify_cb), chain); CHAIN_MUTEX_UNLOCK (chain); + + /* If we're here because we have a Parser/Converter + * we have to unref the pad */ + if (is_parser_converter) + gst_object_unref (pad); + return; } } @@ -1727,48 +1741,6 @@ connect_pad (GstDecodeBin * dbin, GstElement * src, GstDecodePad * dpad, /* Remove selected factory from the list. */ g_value_array_remove (factories, 0); - /* Check if the caps are really supported by the factory. The - * factory list is non-empty-subset filtered while caps - * are only accepted by a pad if they are a subset of the - * pad caps. - * - * FIXME: Only do this for fixed caps here. Non-fixed caps - * can happen if a Parser/Converter was autoplugged before - * this. We then assume that it will be able to convert to - * everything that the decoder would want. - * - * A subset check will fail here because the parser caps - * will be generic and while the decoder will only - * support a subset of the parser caps. - */ - if (gst_caps_is_fixed (caps)) { - const GList *templs; - gboolean skip = FALSE; - - templs = gst_element_factory_get_static_pad_templates (factory); - - while (templs) { - GstStaticPadTemplate *templ = (GstStaticPadTemplate *) templs->data; - - if (templ->direction == GST_PAD_SINK) { - GstCaps *templcaps = gst_static_caps_get (&templ->static_caps); - - if (!gst_caps_is_subset (caps, templcaps)) { - gst_caps_unref (templcaps); - skip = TRUE; - break; - } - - gst_caps_unref (templcaps); - } - templs = g_list_next (templs); - } - if (skip) { - gst_object_unref (factory); - continue; - } - } - /* If the factory is for a parser we first check if the factory * was already used for the current chain. If it was used already * we would otherwise create an infinite loop here because the diff --git a/gst/playback/gstplaysink.c b/gst/playback/gstplaysink.c index 86b0f2f73a..b653b760c5 100644 --- a/gst/playback/gstplaysink.c +++ b/gst/playback/gstplaysink.c @@ -2113,7 +2113,8 @@ gst_play_sink_reconfigure (GstPlaySink * playsink) GST_OBJECT_UNLOCK (playsink); /* figure out which components we need */ - if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) { + if (flags & GST_PLAY_FLAG_TEXT && playsink->video_pad_raw + && playsink->text_pad) { /* we have subtitles and we are requested to show it */ need_text = TRUE; } @@ -3027,14 +3028,14 @@ caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink) if (pad == playsink->audio_pad) { raw = is_raw_pad (pad); - reconfigure = (!!playsink->audio_pad_raw != !!raw) + reconfigure = (! !playsink->audio_pad_raw != ! !raw) && playsink->audiochain; GST_DEBUG_OBJECT (pad, "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw, reconfigure, caps); } else if (pad == playsink->video_pad) { raw = is_raw_pad (pad); - reconfigure = (!!playsink->video_pad_raw != !!raw) + reconfigure = (! !playsink->video_pad_raw != ! !raw) && playsink->videochain; GST_DEBUG_OBJECT (pad, "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw, diff --git a/tests/check/elements/audiorate.c b/tests/check/elements/audiorate.c index da45f8724f..8974ba7408 100644 --- a/tests/check/elements/audiorate.c +++ b/tests/check/elements/audiorate.c @@ -404,7 +404,8 @@ GST_START_TEST (test_large_discont) audiorate = gst_check_setup_element ("audiorate"); caps = gst_caps_new_simple ("audio/x-raw-float", "channels", G_TYPE_INT, 1, - "rate", G_TYPE_INT, 44100, "width", G_TYPE_INT, 32, NULL); + "rate", G_TYPE_INT, 44100, "width", G_TYPE_INT, 32, + "endianness", G_TYPE_INT, G_BYTE_ORDER, NULL); srcpad = gst_check_setup_src_pad (audiorate, &srctemplate, caps); sinkpad = gst_check_setup_sink_pad (audiorate, &sinktemplate, caps); diff --git a/tests/check/elements/decodebin2.c b/tests/check/elements/decodebin2.c new file mode 100644 index 0000000000..f015ef2cb4 --- /dev/null +++ b/tests/check/elements/decodebin2.c @@ -0,0 +1,658 @@ +/* GStreamer unit tests for decodebin2 + * + * Copyright (C) 2006 Tim-Philipp Müller + * Copyright (C) 2011 Hewlett-Packard Development Company, L.P. + * Author: Tim-Philipp Müller , Collabora Ltd. + * Sebastian Dröge , Collabora Ltd. + * + * 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 +#endif + +#include +#include +#include +#include + +static const gchar dummytext[] = + "Quick Brown Fox Jumps over a Lazy Frog Quick Brown " + "Fox Jumps over a Lazy Frog Quick Brown Fox Jumps over a Lazy Frog Quick " + "Brown Fox Jumps over a Lazy Frog Quick Brown Fox Jumps over a Lazy Frog " + "Quick Brown Fox Jumps over a Lazy Frog Quick Brown Fox Jumps over a Lazy " + "Frog Quick Brown Fox Jumps over a Lazy Frog Quick Brown Fox Jumps over a " + "Lazy Frog Quick Brown Fox Jumps over a Lazy Frog Quick Brown Fox Jumps " + "over a Lazy Frog Quick Brown Fox Jumps over a Lazy Frog Quick Brown Fox " + "jumps over a Lazy Frog Quick Brown Fox Jumps over a Lazy Frog Quick Brown " + "Fox Jumps over a Lazy Frog Quick Brown Fox Jumps over a Lazy Frog Quick " + "Brown Fox Jumps over a Lazy Frog Quick Brown Fox Jumps over a Lazy Frog " + "Quick Brown Fox Jumps over a Lazy Frog Quick Brown Fox Jumps over a Lazy " + "Frog Quick Brown Fox Jumps over a Lazy Frog Quick Brown Fox Jumps over a " + "Lazy Frog Quick Brown Fox Jumps over a Lazy Frog Quick Brown Fox Jumps " + "over a Lazy Frog Quick Brown Fox Jumps over a Lazy Frog Quick Brown Fox "; + +static void +src_handoff_cb (GstElement * src, GstBuffer * buf, GstPad * pad, gpointer data) +{ + GST_BUFFER_DATA (buf) = (guint8 *) dummytext; + GST_BUFFER_SIZE (buf) = sizeof (dummytext); + GST_BUFFER_OFFSET (buf) = 0; + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_READONLY); +} + +static void +decodebin_new_decoded_pad_cb (GstElement * decodebin, GstPad * pad, + gboolean last, gboolean * p_flag) +{ + /* we should not be reached */ + fail_unless (decodebin == NULL, "new-decoded-pad should not be emitted"); +} + +/* make sure that decodebin errors out instead of creating a new decoded pad + * if the entire stream is a plain text file */ +GST_START_TEST (test_text_plain_streams) +{ + GstElement *pipe, *src, *decodebin; + GstMessage *msg; + + pipe = gst_pipeline_new (NULL); + fail_unless (pipe != NULL, "failed to create pipeline"); + + src = gst_element_factory_make ("fakesrc", "src"); + fail_unless (src != NULL, "Failed to create fakesrc element"); + + g_object_set (src, "signal-handoffs", TRUE, NULL); + g_object_set (src, "num-buffers", 1, NULL); + g_object_set (src, "can-activate-pull", FALSE, NULL); + g_signal_connect (src, "handoff", G_CALLBACK (src_handoff_cb), NULL); + + decodebin = gst_element_factory_make ("decodebin2", "decodebin"); + fail_unless (decodebin != NULL, "Failed to create decodebin element"); + + g_signal_connect (decodebin, "new-decoded-pad", + G_CALLBACK (decodebin_new_decoded_pad_cb), NULL); + + fail_unless (gst_bin_add (GST_BIN (pipe), src)); + fail_unless (gst_bin_add (GST_BIN (pipe), decodebin)); + fail_unless (gst_element_link (src, decodebin), "can't link src<->decodebin"); + + fail_unless_equals_int (gst_element_set_state (pipe, GST_STATE_READY), + GST_STATE_CHANGE_SUCCESS); + /* it's push-based, so should be async */ + fail_unless_equals_int (gst_element_set_state (pipe, GST_STATE_PAUSED), + GST_STATE_CHANGE_ASYNC); + + /* it should error out at some point */ + msg = gst_bus_poll (GST_ELEMENT_BUS (pipe), GST_MESSAGE_ERROR, -1); + fail_unless (msg != NULL); + fail_unless (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR); + gst_message_unref (msg); + + gst_element_set_state (pipe, GST_STATE_NULL); + gst_object_unref (pipe); +} + +GST_END_TEST; + +static void +new_decoded_pad_plug_fakesink_cb (GstElement * decodebin, GstPad * srcpad, + gboolean last, GstElement * pipeline) +{ + GstElement *sink; + GstPad *sinkpad; + + GST_LOG ("Linking fakesink"); + + sink = gst_element_factory_make ("fakesink", "sink"); + fail_unless (sink != NULL, "Failed to create fakesink element"); + + gst_bin_add (GST_BIN (pipeline), sink); + + sinkpad = gst_element_get_static_pad (sink, "sink"); + fail_unless_equals_int (gst_pad_link (srcpad, sinkpad), GST_PAD_LINK_OK); + gst_object_unref (sinkpad); + + gst_element_set_state (sink, GST_STATE_PLAYING); +} + +GST_START_TEST (test_reuse_without_decoders) +{ + GstElement *pipe, *src, *decodebin, *sink; + + pipe = gst_pipeline_new (NULL); + fail_unless (pipe != NULL, "failed to create pipeline"); + + src = gst_element_factory_make ("audiotestsrc", "src"); + fail_unless (src != NULL, "Failed to create audiotestsrc element"); + + decodebin = gst_element_factory_make ("decodebin2", "decodebin"); + fail_unless (decodebin != NULL, "Failed to create decodebin element"); + + g_signal_connect (decodebin, "new-decoded-pad", + G_CALLBACK (new_decoded_pad_plug_fakesink_cb), pipe); + + fail_unless (gst_bin_add (GST_BIN (pipe), src)); + fail_unless (gst_bin_add (GST_BIN (pipe), decodebin)); + fail_unless (gst_element_link (src, decodebin), "can't link src<->decodebin"); + + fail_unless_equals_int (gst_element_set_state (pipe, GST_STATE_READY), + GST_STATE_CHANGE_SUCCESS); + /* it's push-based, so should be async */ + fail_unless_equals_int (gst_element_set_state (pipe, GST_STATE_PAUSED), + GST_STATE_CHANGE_ASYNC); + + /* wait for state change to complete */ + fail_unless_equals_int (gst_element_get_state (pipe, NULL, NULL, -1), + GST_STATE_CHANGE_SUCCESS); + + /* there shouldn't be any errors */ + fail_if (gst_bus_poll (GST_ELEMENT_BUS (pipe), GST_MESSAGE_ERROR, 0) != NULL); + + /* reset */ + gst_element_set_state (pipe, GST_STATE_READY); + + sink = gst_bin_get_by_name (GST_BIN (pipe), "sink"); + gst_bin_remove (GST_BIN (pipe), sink); + gst_element_set_state (sink, GST_STATE_NULL); + gst_object_unref (sink); + + GST_LOG ("second try"); + + fail_unless_equals_int (gst_element_set_state (pipe, GST_STATE_READY), + GST_STATE_CHANGE_SUCCESS); + /* it's push-based, so should be async */ + fail_unless_equals_int (gst_element_set_state (pipe, GST_STATE_PAUSED), + GST_STATE_CHANGE_ASYNC); + + /* wait for state change to complete */ + fail_unless_equals_int (gst_element_get_state (pipe, NULL, NULL, -1), + GST_STATE_CHANGE_SUCCESS); + + /* there shouldn't be any errors */ + fail_if (gst_bus_poll (GST_ELEMENT_BUS (pipe), GST_MESSAGE_ERROR, 0) != NULL); + + gst_element_set_state (pipe, GST_STATE_NULL); + gst_object_unref (pipe); +} + +GST_END_TEST; + +/* Fake mp3 parser for test */ +typedef GstBaseParse TestMpegAudioParse; +typedef GstBaseParseClass TestMpegAudioParseClass; + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/mpeg, mpegversion=1, layer=[1,3], parsed=(b)true") + ); + +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/mpeg, mpegversion=1, parsed=(bool) { false, true }") + ); + +static GType test_mpeg_audio_parse_get_type (void); +static gboolean test_mpeg_audio_parse_start (GstBaseParse * parse); +static gboolean test_mpeg_audio_parse_stop (GstBaseParse * parse); +static gboolean test_mpeg_audio_parse_check_valid_frame (GstBaseParse * parse, + GstBaseParseFrame * frame, guint * size, gint * skipsize); +static GstFlowReturn test_mpeg_audio_parse_parse_frame (GstBaseParse * parse, + GstBaseParseFrame * frame); + +GST_BOILERPLATE (TestMpegAudioParse, test_mpeg_audio_parse, GstBaseParse, + GST_TYPE_BASE_PARSE); + +static void +test_mpeg_audio_parse_base_init (gpointer klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + 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_simple (element_class, "MPEG1 Audio Parser", + "Codec/Parser/Audio", "Pretends to parse mpeg1 audio stream", + "Foo Bar "); +} + +static void +test_mpeg_audio_parse_class_init (TestMpegAudioParseClass * klass) +{ + GstBaseParseClass *parse_class = GST_BASE_PARSE_CLASS (klass); + + parse_class->start = test_mpeg_audio_parse_start; + parse_class->stop = test_mpeg_audio_parse_stop; + parse_class->check_valid_frame = test_mpeg_audio_parse_check_valid_frame; + parse_class->parse_frame = test_mpeg_audio_parse_parse_frame; +} + +static gint num_parse_instances = 0; + +static void +test_mpeg_audio_parse_init (TestMpegAudioParse * mp3parse, + TestMpegAudioParseClass * klass) +{ + /* catch decodebin plugging parsers in a loop early */ + fail_unless (++num_parse_instances < 10); +} + +static gboolean +test_mpeg_audio_parse_start (GstBaseParse * parse) +{ + gst_base_parse_set_min_frame_size (parse, 6); + return TRUE; +} + +static gboolean +test_mpeg_audio_parse_stop (GstBaseParse * parse) +{ + return TRUE; +} + +static gboolean +test_mpeg_audio_parse_check_valid_frame (GstBaseParse * parse, + GstBaseParseFrame * frame, guint * framesize, gint * skipsize) +{ + const guint8 *data = GST_BUFFER_DATA (frame->buffer); + + if ((GST_READ_UINT16_BE (data) & 0xffe0) == 0xffe0) { + /* this framesize is hard-coded for ../test.mp3 */ + *framesize = 1045; + return TRUE; + } else { + *skipsize = 1; + return FALSE; + } +} + +static GstFlowReturn +test_mpeg_audio_parse_parse_frame (GstBaseParse * parse, + GstBaseParseFrame * frame) +{ + if (GST_BUFFER_OFFSET (frame->buffer) == 0) { + GstCaps *caps; + + caps = gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, 1, + "mpegaudioversion", G_TYPE_INT, 1, "layer", G_TYPE_INT, 3, + "rate", G_TYPE_INT, 44100, "channels", G_TYPE_INT, 2, NULL); + gst_buffer_set_caps (frame->buffer, caps); + gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps); + gst_caps_unref (caps); + } + return GST_FLOW_OK; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "testmpegaudioparse", GST_RANK_NONE, + test_mpeg_audio_parse_get_type ()); +} + +GST_START_TEST (test_mp3_parser_loop) +{ + GstStateChangeReturn sret; + GstPluginFeature *feature; + GstMessage *msg; + GstElement *pipe, *src, *dec; + gchar *path; + + num_parse_instances = 0; + + gst_plugin_register_static (GST_VERSION_MAJOR, GST_VERSION_MINOR, + "fakemp3parse", "fakemp3parse", plugin_init, VERSION, "LGPL", + "gst-plugins-base", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); + + feature = gst_default_registry_find_feature ("testmpegaudioparse", + GST_TYPE_ELEMENT_FACTORY); + + gst_plugin_feature_set_rank (feature, GST_RANK_PRIMARY + 100); + + pipe = gst_pipeline_new (NULL); + + src = gst_element_factory_make ("filesrc", NULL); + fail_unless (src != NULL); + + path = g_build_filename (GST_TEST_FILES_PATH, "test.mp3", NULL); + g_object_set (src, "location", path, NULL); + g_free (path); + + dec = gst_element_factory_make ("decodebin2", NULL); + fail_unless (dec != NULL); + + gst_bin_add_many (GST_BIN (pipe), src, dec, NULL); + gst_element_link_many (src, dec, NULL); + + sret = gst_element_set_state (pipe, GST_STATE_PLAYING); + fail_unless_equals_int (sret, GST_STATE_CHANGE_ASYNC); + + /* wait for unlinked error */ + msg = gst_bus_timed_pop_filtered (GST_ELEMENT_BUS (pipe), + GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR); + gst_message_unref (msg); + + gst_element_set_state (pipe, GST_STATE_NULL); + gst_object_unref (pipe); + + /* make sure out parser got plugged at all though */ + fail_unless_equals_int (num_parse_instances, 1); + + /* don't want to interfere with any other of the other tests */ + gst_plugin_feature_set_rank (feature, GST_RANK_NONE); + gst_object_unref (feature); +} + +GST_END_TEST; + +/* Fake parser/decoder for parser_negotiation test */ +static GType gst_fake_h264_parser_get_type (void); +static GType gst_fake_h264_decoder_get_type (void); + +#undef parent_class +#define parent_class fake_h264_parser_parent_class +typedef struct _GstFakeH264Parser GstFakeH264Parser; +typedef GstBaseTransformClass GstFakeH264ParserClass; + +struct _GstFakeH264Parser +{ + GstBaseTransform parent; +}; + +GST_BOILERPLATE (GstFakeH264Parser, gst_fake_h264_parser, GstBaseTransform, + GST_TYPE_BASE_TRANSFORM); + +static void +gst_fake_h264_parser_base_init (gpointer klass) +{ + static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-h264")); + static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-h264, " + "stream-format=(string) { avc, byte-stream }")); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_templ)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_templ)); + gst_element_class_set_details_simple (element_class, + "FakeH264Parser", "Codec/Parser/Converter/Video", "yep", "me"); +} + +static GstFlowReturn +gst_fake_h264_parser_transform (GstBaseTransform * trans, GstBuffer * inbuf, + GstBuffer * outbuf) +{ + return GST_FLOW_OK; +} + +static GstCaps * +gst_fake_h264_parser_transform_caps (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * caps) +{ + if (direction == GST_PAD_SRC) + return gst_caps_from_string ("video/x-h264"); + else + return gst_caps_from_string ("video/x-h264, " + "stream-format=(string) { avc, byte-stream }"); +} + +static gboolean +gst_fake_h264_parser_get_unit_size (GstBaseTransform * trans, GstCaps * caps, + guint * size) +{ + *size = 1; + return TRUE; +} + +static gboolean +gst_fake_h264_parser_set_caps (GstBaseTransform * trans, GstCaps * incaps, + GstCaps * outcaps) +{ + GstStructure *s; + const gchar *stream_format; + + s = gst_caps_get_structure (incaps, 0); + fail_unless (gst_structure_has_name (s, "video/x-h264")); + + s = gst_caps_get_structure (outcaps, 0); + fail_unless (gst_structure_has_name (s, "video/x-h264")); + stream_format = gst_structure_get_string (s, "stream-format"); + fail_unless_equals_string ("byte-stream", stream_format); + + return TRUE; +} + +static GstFlowReturn +gst_fake_h264_parser_prepare_output_buffer (GstBaseTransform * trans, + GstBuffer * inbuf, gint size, GstCaps * caps, GstBuffer ** outbuf) +{ + *outbuf = gst_buffer_ref (inbuf); + return GST_FLOW_OK; +} + +static void +gst_fake_h264_parser_class_init (GstFakeH264ParserClass * klass) +{ + GstBaseTransformClass *basetrans_class = (GstBaseTransformClass *) klass; + + basetrans_class->transform = gst_fake_h264_parser_transform; + basetrans_class->transform_caps = gst_fake_h264_parser_transform_caps; + basetrans_class->get_unit_size = gst_fake_h264_parser_get_unit_size; + basetrans_class->set_caps = gst_fake_h264_parser_set_caps; + basetrans_class->prepare_output_buffer = + gst_fake_h264_parser_prepare_output_buffer; +} + +static void +gst_fake_h264_parser_init (GstFakeH264Parser * self, + GstFakeH264ParserClass * klass) +{ +} + +#undef parent_class +#define parent_class fake_h264_decoder_parent_class +typedef struct _GstFakeH264Decoder GstFakeH264Decoder; +typedef GstBaseTransformClass GstFakeH264DecoderClass; + +struct _GstFakeH264Decoder +{ + GstBaseTransform parent; +}; + +GST_BOILERPLATE (GstFakeH264Decoder, gst_fake_h264_decoder, GstBaseTransform, + GST_TYPE_BASE_TRANSFORM); + +static void +gst_fake_h264_decoder_base_init (gpointer klass) +{ + static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-h264, " "stream-format=(string) byte-stream")); + static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw-yuv")); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_templ)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_templ)); + gst_element_class_set_details_simple (element_class, + "FakeH264Decoder", "Codec/Decoder/Video", "yep", "me"); +} + +static GstFlowReturn +gst_fake_h264_decoder_transform (GstBaseTransform * trans, GstBuffer * inbuf, + GstBuffer * outbuf) +{ + return GST_FLOW_OK; +} + +static GstCaps * +gst_fake_h264_decoder_transform_caps (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * caps) +{ + if (direction == GST_PAD_SRC) + return gst_caps_from_string ("video/x-h264, " + "stream-format=(string) byte-stream"); + else + return gst_caps_from_string ("video/x-raw-yuv"); +} + +static gboolean +gst_fake_h264_decoder_get_unit_size (GstBaseTransform * trans, GstCaps * caps, + guint * size) +{ + *size = 1; + return TRUE; +} + +static gboolean +gst_fake_h264_decoder_set_caps (GstBaseTransform * trans, GstCaps * incaps, + GstCaps * outcaps) +{ + GstStructure *s; + const gchar *stream_format; + + s = gst_caps_get_structure (incaps, 0); + fail_unless (gst_structure_has_name (s, "video/x-h264")); + stream_format = gst_structure_get_string (s, "stream-format"); + fail_unless_equals_string ("byte-stream", stream_format); + + s = gst_caps_get_structure (outcaps, 0); + fail_unless (gst_structure_has_name (s, "video/x-raw-yuv")); + + return TRUE; +} + +static GstFlowReturn +gst_fake_h264_decoder_prepare_output_buffer (GstBaseTransform * trans, + GstBuffer * inbuf, gint size, GstCaps * caps, GstBuffer ** outbuf) +{ + *outbuf = gst_buffer_ref (inbuf); + return GST_FLOW_OK; +} + +static void +gst_fake_h264_decoder_class_init (GstFakeH264DecoderClass * klass) +{ + GstBaseTransformClass *basetrans_class = (GstBaseTransformClass *) klass; + + basetrans_class->transform = gst_fake_h264_decoder_transform; + basetrans_class->transform_caps = gst_fake_h264_decoder_transform_caps; + basetrans_class->get_unit_size = gst_fake_h264_decoder_get_unit_size; + basetrans_class->set_caps = gst_fake_h264_decoder_set_caps; + basetrans_class->prepare_output_buffer = + gst_fake_h264_decoder_prepare_output_buffer; +} + +static void +gst_fake_h264_decoder_init (GstFakeH264Decoder * self, + GstFakeH264DecoderClass * klass) +{ +} + +static void +parser_negotiation_pad_added_cb (GstElement * dec, GstPad * pad, + gpointer user_data) +{ + GstBin *pipe = user_data; + GstElement *sink; + GstPad *sinkpad; + + sink = gst_element_factory_make ("fakesink", NULL); + gst_bin_add (pipe, sink); + gst_element_sync_state_with_parent (sink); + sinkpad = gst_element_get_static_pad (sink, "sink"); + gst_pad_link (pad, sinkpad); + gst_object_unref (sinkpad); +} + +GST_START_TEST (test_parser_negotiation) +{ + GstStateChangeReturn sret; + GstMessage *msg; + GstCaps *caps; + GstElement *pipe, *src, *filter, *dec; + + gst_element_register (NULL, "fakeh264parse", GST_RANK_PRIMARY + 101, + gst_fake_h264_parser_get_type ()); + gst_element_register (NULL, "fakeh264dec", GST_RANK_PRIMARY + 100, + gst_fake_h264_decoder_get_type ()); + + pipe = gst_pipeline_new (NULL); + + src = gst_element_factory_make ("fakesrc", NULL); + fail_unless (src != NULL); + g_object_set (G_OBJECT (src), "num-buffers", 5, "sizetype", 2, "filltype", 2, + "can-activate-pull", FALSE, NULL); + + filter = gst_element_factory_make ("capsfilter", NULL); + fail_unless (filter != NULL); + caps = gst_caps_from_string ("video/x-h264"); + g_object_set (G_OBJECT (filter), "caps", caps, NULL); + gst_caps_unref (caps); + + dec = gst_element_factory_make ("decodebin2", NULL); + fail_unless (dec != NULL); + + g_signal_connect (dec, "pad-added", + G_CALLBACK (parser_negotiation_pad_added_cb), pipe); + + gst_bin_add_many (GST_BIN (pipe), src, filter, dec, NULL); + gst_element_link_many (src, filter, dec, NULL); + + sret = gst_element_set_state (pipe, GST_STATE_PLAYING); + fail_unless_equals_int (sret, GST_STATE_CHANGE_ASYNC); + + /* wait for EOS or error */ + msg = gst_bus_timed_pop_filtered (GST_ELEMENT_BUS (pipe), + GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR | GST_MESSAGE_EOS); + fail_unless (msg != NULL); + fail_unless (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS); + gst_message_unref (msg); + + gst_element_set_state (pipe, GST_STATE_NULL); + gst_object_unref (pipe); +} + +GST_END_TEST; + +static Suite * +decodebin2_suite (void) +{ + Suite *s = suite_create ("decodebin2"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_text_plain_streams); + tcase_add_test (tc_chain, test_reuse_without_decoders); + tcase_add_test (tc_chain, test_mp3_parser_loop); + tcase_add_test (tc_chain, test_parser_negotiation); + + return s; +} + +GST_CHECK_MAIN (decodebin2);