From 2d2e816aee4f5d3808e6a6c84bedfb11d7b3c278 Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Tue, 6 Sep 2011 21:24:46 +0200 Subject: [PATCH 01/47] Automatic update of common submodule From a39eb83 to 11f0cd5 --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index a39eb835fb..11f0cd5a3f 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit a39eb835fb3be2a4c5a6a89b5ca5cc064e79b2e2 +Subproject commit 11f0cd5a3fba36f85cf3e434150bfe66b1bf08d4 From 625e7a6143a0eefa2cf79550b6e66c44e4c19bf0 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Tue, 6 Sep 2011 15:59:49 +0200 Subject: [PATCH 02/47] aacparse: parse codec_data to determine number of samples per frame Fixes #656734. --- gst/audioparsers/gstaacparse.c | 12 ++++++++---- gst/audioparsers/gstaacparse.h | 14 +------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/gst/audioparsers/gstaacparse.c b/gst/audioparsers/gstaacparse.c index 5c50da852c..e42983d077 100644 --- a/gst/audioparsers/gstaacparse.c +++ b/gst/audioparsers/gstaacparse.c @@ -262,9 +262,11 @@ gst_aac_parse_sink_setcaps (GstBaseParse * parse, GstCaps * caps) aacparse->channels = (buffer[1] & 0x78) >> 3; aacparse->header_type = DSPAAC_HEADER_NONE; aacparse->mpegversion = 4; + aacparse->frame_samples = (buffer[1] & 4) ? 960 : 1024; - GST_DEBUG ("codec_data: object_type=%d, sample_rate=%d, channels=%d", - aacparse->object_type, aacparse->sample_rate, aacparse->channels); + GST_DEBUG ("codec_data: object_type=%d, sample_rate=%d, channels=%d, " + "samples=%d", aacparse->object_type, aacparse->sample_rate, + aacparse->channels, aacparse->frame_samples); /* arrange for metadata and get out of the way */ gst_aac_parse_set_src_caps (aacparse, caps); @@ -460,7 +462,8 @@ gst_aac_parse_detect_stream (GstAacParse * aacparse, gst_aac_parse_parse_adts_header (aacparse, data, &rate, &channels, &aacparse->object_type, &aacparse->mpegversion); - gst_base_parse_set_frame_rate (GST_BASE_PARSE (aacparse), rate, 1024, 2, 2); + gst_base_parse_set_frame_rate (GST_BASE_PARSE (aacparse), rate, + aacparse->frame_samples, 2, 2); GST_DEBUG ("ADTS: samplerate %d, channels %d, objtype %d, version %d", rate, channels, aacparse->object_type, aacparse->mpegversion); @@ -679,7 +682,7 @@ gst_aac_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame) } gst_base_parse_set_frame_rate (GST_BASE_PARSE (aacparse), - aacparse->sample_rate, 1024, 2, 2); + aacparse->sample_rate, aacparse->frame_samples, 2, 2); } return ret; @@ -701,6 +704,7 @@ gst_aac_parse_start (GstBaseParse * parse) aacparse = GST_AAC_PARSE (parse); GST_DEBUG ("start"); + aacparse->frame_samples = 1024; gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse), ADTS_MAX_SIZE); return TRUE; } diff --git a/gst/audioparsers/gstaacparse.h b/gst/audioparsers/gstaacparse.h index 4020d8fc75..1907c2e440 100644 --- a/gst/audioparsers/gstaacparse.h +++ b/gst/audioparsers/gstaacparse.h @@ -63,19 +63,6 @@ typedef struct _GstAacParseClass GstAacParseClass; /** * GstAacParse: - * @element: the parent element. - * @object_type: AAC object type of the stream. - * @bitrate: Current media bitrate. - * @sample_rate: Current media samplerate. - * @channels: Current media channel count. - * @frames_per_sec: FPS value of the current stream. - * @header_type: #GstAacHeaderType indicating the current stream type. - * @framecount: The amount of frames that has been processed this far. - * @bytecount: The amount of bytes that has been processed this far. - * @sync: Tells whether the parser is in sync (a.k.a. not searching for header) - * @eos: End-of-Stream indicator. Set when EOS event arrives. - * @duration: Duration of the current stream. - * @ts: Current stream timestamp. * * The opaque GstAacParse data structure. */ @@ -88,6 +75,7 @@ struct _GstAacParse { gint sample_rate; gint channels; gint mpegversion; + gint frame_samples; GstAacHeaderType header_type; }; From 786d35f53ff8788c70941b590d319c63193b33f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 7 Sep 2011 12:10:48 +0200 Subject: [PATCH 03/47] audioparsers: Improve src template caps Remove the parsed/framed fields and add all fields to the template caps that always exist. --- gst/audioparsers/gstaacparse.c | 3 +-- gst/audioparsers/gstac3parse.c | 10 +++++----- gst/audioparsers/gstdcaparse.c | 5 +++-- gst/audioparsers/gstflacparse.c | 2 +- gst/audioparsers/gstmpegaudioparse.c | 7 ++++--- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/gst/audioparsers/gstaacparse.c b/gst/audioparsers/gstaacparse.c index e42983d077..51410704e8 100644 --- a/gst/audioparsers/gstaacparse.c +++ b/gst/audioparsers/gstaacparse.c @@ -57,8 +57,7 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/mpeg, " - "framed = (boolean) false, " "mpegversion = (int) { 2, 4 };")); + GST_STATIC_CAPS ("audio/mpeg, mpegversion = (int) { 2, 4 };")); GST_DEBUG_CATEGORY_STATIC (aacparse_debug); #define GST_CAT_DEFAULT aacparse_debug diff --git a/gst/audioparsers/gstac3parse.c b/gst/audioparsers/gstac3parse.c index 80c1f4d613..3b0b9803d7 100644 --- a/gst/audioparsers/gstac3parse.c +++ b/gst/audioparsers/gstac3parse.c @@ -144,16 +144,16 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS ("audio/x-ac3, framed = (boolean) true, " - " channels = (int) [ 1, 6 ], rate = (int) [ 32000, 48000 ]; " + " channels = (int) [ 1, 6 ], rate = (int) [ 32000, 48000 ], " + " alignment = (string) { iec61937, frame}; " "audio/x-eac3, framed = (boolean) true, " - " channels = (int) [ 1, 6 ], rate = (int) [ 32000, 48000 ] ")); + " channels = (int) [ 1, 6 ], rate = (int) [ 32000, 48000 ], " + " alignment = (string) { iec61937, frame}; ")); static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-ac3, framed = (boolean) false; " - "audio/x-eac3, framed = (boolean) false; " - "audio/ac3, framed = (boolean) false ")); + GST_STATIC_CAPS ("audio/x-ac3; " "audio/x-eac3; " "audio/ac3")); static void gst_ac3_parse_finalize (GObject * object); diff --git a/gst/audioparsers/gstdcaparse.c b/gst/audioparsers/gstdcaparse.c index 2bf0e38828..2c020e7144 100644 --- a/gst/audioparsers/gstdcaparse.c +++ b/gst/audioparsers/gstdcaparse.c @@ -62,12 +62,13 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", " channels = (int) [ 1, 8 ]," " rate = (int) [ 8000, 192000 ]," " depth = (int) { 14, 16 }," - " endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }")); + " endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " + " block-size = (int) [ 1, MAX], " " frame-size = (int) [ 1, MAX]")); static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-dts, framed = (boolean) false")); + GST_STATIC_CAPS ("audio/x-dts")); static void gst_dca_parse_finalize (GObject * object); diff --git a/gst/audioparsers/gstflacparse.c b/gst/audioparsers/gstflacparse.c index 3011bf0aad..a58c76a4ef 100644 --- a/gst/audioparsers/gstflacparse.c +++ b/gst/audioparsers/gstflacparse.c @@ -181,7 +181,7 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-flac, framed = (boolean) false") + GST_STATIC_CAPS ("audio/x-flac") ); static void gst_flac_parse_finalize (GObject * object); diff --git a/gst/audioparsers/gstmpegaudioparse.c b/gst/audioparsers/gstmpegaudioparse.c index 94a8993fa7..d816fff438 100644 --- a/gst/audioparsers/gstmpegaudioparse.c +++ b/gst/audioparsers/gstmpegaudioparse.c @@ -76,14 +76,15 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_STATIC_CAPS ("audio/mpeg, " "mpegversion = (int) 1, " "layer = (int) [ 1, 3 ], " - "rate = (int) [ 8000, 48000 ], channels = (int) [ 1, 2 ]," - "parsed=(boolean) true") + "mpegaudioversion = (int) [ 1, 3], " + "rate = (int) [ 8000, 48000 ], " + "channels = (int) [ 1, 2 ], " "parsed=(boolean) true") ); static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/mpeg, mpegversion = (int) 1, parsed=(boolean)false") + GST_STATIC_CAPS ("audio/mpeg, mpegversion = (int) 1") ); static void gst_mpeg_audio_parse_finalize (GObject * object); From c29069fd11fa62e95827aea431df2cf0d9976ec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 7 Sep 2011 12:11:39 +0200 Subject: [PATCH 04/47] ac3parse: Add Converter to the classification because it can convert between different alignments This allows decodebin2 to let it negotiate properly. --- gst/audioparsers/gstac3parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/audioparsers/gstac3parse.c b/gst/audioparsers/gstac3parse.c index 3b0b9803d7..a1cb99789b 100644 --- a/gst/audioparsers/gstac3parse.c +++ b/gst/audioparsers/gstac3parse.c @@ -179,7 +179,7 @@ gst_ac3_parse_base_init (gpointer klass) gst_static_pad_template_get (&src_template)); gst_element_class_set_details_simple (element_class, - "AC3 audio stream parser", "Codec/Parser/Audio", + "AC3 audio stream parser", "Codec/Parser/Converter/Audio", "AC3 parser", "Tim-Philipp Müller "); } From 24d002e04d46ed510d5591563eb2a21574ff0c9a Mon Sep 17 00:00:00 2001 From: Konstantin Miller Date: Mon, 29 Aug 2011 14:12:22 +0200 Subject: [PATCH 05/47] souphttpsrc: Don't handle HTTP response 407 as error if proxy authentication data is available Fixes bug #657422. --- ext/soup/gstsouphttpsrc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ext/soup/gstsouphttpsrc.c b/ext/soup/gstsouphttpsrc.c index 8735f4f697..3011058850 100644 --- a/ext/soup/gstsouphttpsrc.c +++ b/ext/soup/gstsouphttpsrc.c @@ -734,6 +734,9 @@ gst_soup_http_src_got_headers_cb (SoupMessage * msg, GstSoupHTTPSrc * src) soup_message_headers_foreach (msg->response_headers, gst_soup_http_src_headers_foreach, src); + if (msg->status_code == 407 && src->proxy_id && src->proxy_pw) + return; + if (src->automatic_redirect && SOUP_STATUS_IS_REDIRECTION (msg->status_code)) { GST_DEBUG_OBJECT (src, "%u redirect to \"%s\"", msg->status_code, soup_message_headers_get (msg->response_headers, "Location")); From 69869f08391030bfd0fddcf20ccf5087e5ee7950 Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Wed, 7 Sep 2011 14:18:58 +0200 Subject: [PATCH 06/47] docs: cleanup makefiles Remove commented out parts that we don't need. Remove "the wingo addition" - no so useful after all. Narrow down file-globs for plugin docs. --- docs/plugins/Makefile.am | 39 ++++++++------------------------------- 1 file changed, 8 insertions(+), 31 deletions(-) diff --git a/docs/plugins/Makefile.am b/docs/plugins/Makefile.am index 0219ecc9d1..86453166dd 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= @@ -53,14 +33,11 @@ FIXXREF_OPTIONS=--extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html \ --extra-dir=$(GSTPB_PREFIX)/share/gtk-doc/html # Used for dependencies. -HFILE_GLOB=$(DOC_SOURCE_DIR)/*/*/*.h -CFILE_GLOB=$(DOC_SOURCE_DIR)/*/*/*.c $(DOC_SOURCE_DIR)/*/*/*.cc $(DOC_SOURCE_DIR)/*/*/*.m - -# 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 \ + $(top_srcdir)/ext/*/*.cc $(top_srcdir)/sys/*/*.m # Header files to ignore when scanning. IGNORE_HFILES = @@ -260,7 +237,7 @@ 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) -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) From 0fd139792115f2cd1b8d944305f2aa71fd4f1c24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 7 Sep 2011 14:26:01 +0200 Subject: [PATCH 07/47] qtmux: Use complete MPEG caps in the unit test --- tests/check/elements/qtmux.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/check/elements/qtmux.c b/tests/check/elements/qtmux.c index a70e34564d..997d3061c3 100644 --- a/tests/check/elements/qtmux.c +++ b/tests/check/elements/qtmux.c @@ -43,6 +43,7 @@ static GstPad *mysrcpad, *mysinkpad; "rate = (int) 48000" #define VIDEO_CAPS_STRING "video/mpeg, " \ "mpegversion = (int) 4, " \ + "systemstream = (boolean) false, " \ "width = (int) 384, " \ "height = (int) 288, " \ "framerate = (fraction) 25/1" From 8f1f6d1a57edde50ae446983a28bcce48c816c31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 7 Sep 2011 14:27:36 +0200 Subject: [PATCH 08/47] cmml: Use complete cmml caps in the unit test --- tests/check/elements/cmmldec.c | 2 +- tests/check/elements/cmmlenc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/check/elements/cmmldec.c b/tests/check/elements/cmmldec.c index 1ba8fb4a1b..a6b8559d5e 100644 --- a/tests/check/elements/cmmldec.c +++ b/tests/check/elements/cmmldec.c @@ -25,7 +25,7 @@ #include #define SINK_CAPS "text/x-cmml" -#define SRC_CAPS "text/x-cmml" +#define SRC_CAPS "text/x-cmml, encoded=(boolean)TRUE" #define IDENT_HEADER \ "CMML\x00\x00\x00\x00"\ diff --git a/tests/check/elements/cmmlenc.c b/tests/check/elements/cmmlenc.c index 4424a72a77..4b95d713ec 100644 --- a/tests/check/elements/cmmlenc.c +++ b/tests/check/elements/cmmlenc.c @@ -26,7 +26,7 @@ #include #define SINK_CAPS "text/x-cmml" -#define SRC_CAPS "text/x-cmml" +#define SRC_CAPS "text/x-cmml,encoded=(boolean)FALSE" #define IDENT_HEADER \ "CMML\x00\x00\x00\x00"\ From 782fc78d573b54c79cc0af435560049f879a36f0 Mon Sep 17 00:00:00 2001 From: Andoni Morales Alastruey Date: Tue, 15 Mar 2011 11:03:53 +0100 Subject: [PATCH 09/47] matroskamux: handle GstForceKeyUnit event ... by starting a new cluster after forwarding event. Fixes #644154. --- gst/matroska/matroska-mux.c | 26 +++++++++++++++++++++++--- gst/matroska/matroska-mux.h | 3 +++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/gst/matroska/matroska-mux.c b/gst/matroska/matroska-mux.c index ed98df1162..2398d78e5e 100644 --- a/gst/matroska/matroska-mux.c +++ b/gst/matroska/matroska-mux.c @@ -391,6 +391,8 @@ gst_matroska_mux_finalize (GObject * object) { GstMatroskaMux *mux = GST_MATROSKA_MUX (object); + gst_event_replace (&mux->force_key_unit_event, NULL); + gst_object_unref (mux->collect); gst_object_unref (mux->ebml_write); if (mux->writing_app) @@ -662,6 +664,17 @@ gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event) gst_event_unref (event); event = NULL; break; + case GST_EVENT_CUSTOM_DOWNSTREAM:{ + const GstStructure *structure; + + structure = gst_event_get_structure (event); + if (gst_structure_has_name (structure, "GstForceKeyUnit")) { + gst_event_replace (&mux->force_key_unit_event, NULL); + mux->force_key_unit_event = event; + event = NULL; + } + break; + } default: break; } @@ -2692,13 +2705,20 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad) } if (mux->cluster) { - /* start a new cluster at every keyframe or when we may be reaching the - * limit of the relative timestamp */ + /* start a new cluster at every keyframe, at every GstForceKeyUnit event, + * or when we may be reaching the limit of the relative timestamp */ if (mux->cluster_time + mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf) - || is_video_keyframe) { + || is_video_keyframe || mux->force_key_unit_event) { if (!mux->streamable) gst_ebml_write_master_finish (ebml, mux->cluster); + + /* Forward the GstForceKeyUnit event after finishing the cluster */ + if (mux->force_key_unit_event) { + gst_pad_push_event (mux->srcpad, mux->force_key_unit_event); + mux->force_key_unit_event = NULL; + } + mux->prev_cluster_size = ebml->pos - mux->cluster_pos; mux->cluster_pos = ebml->pos; gst_ebml_write_set_cache (ebml, 0x20); diff --git a/gst/matroska/matroska-mux.h b/gst/matroska/matroska-mux.h index e5074a7c08..73bdc09ac3 100644 --- a/gst/matroska/matroska-mux.h +++ b/gst/matroska/matroska-mux.h @@ -126,6 +126,9 @@ typedef struct _GstMatroskaMux { cluster_pos, prev_cluster_size; + /* GstForceKeyUnit event */ + GstEvent *force_key_unit_event; + } GstMatroskaMux; typedef struct _GstMatroskaMuxClass { From ed3adece777175edd785787f4d6b914f06d5913c Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Wed, 7 Sep 2011 08:54:17 -0300 Subject: [PATCH 10/47] qtmux: remove one G_UNLIKELY for user property Using G_UNLIKELY on user properties isn't nice, specially when that is the default option. --- gst/isomp4/gstqtmux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/isomp4/gstqtmux.c b/gst/isomp4/gstqtmux.c index 33ea5ca31f..0f5c46890e 100644 --- a/gst/isomp4/gstqtmux.c +++ b/gst/isomp4/gstqtmux.c @@ -2087,7 +2087,7 @@ gst_qt_mux_add_buffer (GstQTMux * qtmux, GstQTPad * pad, GstBuffer * buf) again: last_buf = pad->last_buf; - if (G_UNLIKELY (qtmux->dts_method == DTS_METHOD_REORDER)) { + if (qtmux->dts_method == DTS_METHOD_REORDER) { buf = gst_qt_mux_get_asc_buffer_ts (qtmux, pad, buf); if (!buf && !last_buf) { GST_DEBUG_OBJECT (qtmux, "no reordered buffer"); From 06c65418d7aeadb7db3880cf815b7d0ff8d36b83 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Thu, 8 Sep 2011 13:28:24 +0200 Subject: [PATCH 11/47] v4l2: use GST_RESOURCE_ERROR_BUSY if v4l2_ioctl fails with EBUSY https://bugzilla.gnome.org/show_bug.cgi?id=658543 --- sys/v4l2/gstv4l2object.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/sys/v4l2/gstv4l2object.c b/sys/v4l2/gstv4l2object.c index 24a4123f82..e361970c34 100644 --- a/sys/v4l2/gstv4l2object.c +++ b/sys/v4l2/gstv4l2object.c @@ -2175,11 +2175,20 @@ get_fmt_failed: } set_fmt_failed: { - GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS, - (_("Device '%s' cannot capture at %dx%d"), - v4l2object->videodev, width, height), - ("Call to S_FMT failed for %" GST_FOURCC_FORMAT " @ %dx%d: %s", - GST_FOURCC_ARGS (pixelformat), width, height, g_strerror (errno))); + if (errno == EBUSY) { + GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, BUSY, + (_("Device '%s' is busy"), v4l2object->videodev), + ("Call to S_FMT failed for %" GST_FOURCC_FORMAT " @ %dx%d: %s", + GST_FOURCC_ARGS (pixelformat), width, height, + g_strerror (errno))); + } else { + GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS, + (_("Device '%s' cannot capture at %dx%d"), + v4l2object->videodev, width, height), + ("Call to S_FMT failed for %" GST_FOURCC_FORMAT " @ %dx%d: %s", + GST_FOURCC_ARGS (pixelformat), width, height, + g_strerror (errno))); + } return FALSE; } invalid_dimensions: From ef1ad78eeecc37b187ac744d98e34f329573ec82 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Thu, 8 Sep 2011 15:02:05 +0200 Subject: [PATCH 12/47] matroskademux: tweak gap handling ... so as to avoid buffers before and after gap to have identical running time. --- gst/matroska/matroska-demux.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c index 416f30e29c..7fb4071d04 100644 --- a/gst/matroska/matroska-demux.c +++ b/gst/matroska/matroska-demux.c @@ -3364,7 +3364,7 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, /* handle gaps, e.g. non-zero start-time, or an cue index entry * that landed us with timestamps not quite intended */ GST_OBJECT_LOCK (demux); - if (GST_CLOCK_TIME_IS_VALID (demux->common.segment.last_stop) && + if (GST_CLOCK_TIME_IS_VALID (demux->last_stop_end) && demux->common.segment.rate > 0.0) { GstClockTimeDiff diff; GstEvent *event1, *event2; @@ -3372,7 +3372,7 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, /* only send newsegments with increasing start times, * otherwise if these go back and forth downstream (sinks) increase * accumulated time and running_time */ - diff = GST_CLOCK_DIFF (demux->common.segment.last_stop, lace_time); + diff = GST_CLOCK_DIFF (demux->last_stop_end, lace_time); if (diff > 2 * GST_SECOND && lace_time > demux->common.segment.start && (!GST_CLOCK_TIME_IS_VALID (demux->common.segment.stop) || lace_time < demux->common.segment.stop)) { @@ -3386,11 +3386,9 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, * accum time, hence running_time */ /* close ahead of gap */ event1 = gst_event_new_new_segment (TRUE, - demux->common.segment.rate, - demux->common.segment.format, - demux->common.segment.last_stop, - demux->common.segment.last_stop, - demux->common.segment.last_stop); + demux->common.segment.rate, demux->common.segment.format, + demux->last_stop_end, demux->last_stop_end, + demux->last_stop_end); /* skip gap */ event2 = gst_event_new_new_segment (FALSE, demux->common.segment.rate, From 95b5ece2c98da84bc51507b36bc5f23c3c4582c5 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Wed, 7 Sep 2011 14:13:03 +0200 Subject: [PATCH 13/47] rtspsrc: ensure some initial state variable setup ... which might otherwise be skipped if the PLAY command is issued before the OPEN command had a chance to actually be acted upon. Fixes #657376. --- gst/rtsp/gstrtspsrc.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/gst/rtsp/gstrtspsrc.c b/gst/rtsp/gstrtspsrc.c index 8442646654..731e0266af 100644 --- a/gst/rtsp/gstrtspsrc.c +++ b/gst/rtsp/gstrtspsrc.c @@ -4105,6 +4105,8 @@ gst_rtspsrc_loop_send_cmd (GstRTSPSrc * src, gint cmd, gboolean flush) /* start new request */ gst_rtspsrc_loop_start_cmd (src, cmd); + GST_DEBUG_OBJECT (src, "sending cmd %d", cmd); + GST_OBJECT_LOCK (src); old = src->loop_cmd; if (old != CMD_WAIT) { @@ -6454,10 +6456,6 @@ gst_rtspsrc_thread (GstRTSPSrc * src) switch (cmd) { case CMD_OPEN: - src->cur_protocols = src->protocols; - /* first attempt, don't ignore timeouts */ - src->ignore_timeout = FALSE; - src->open_error = FALSE; ret = gst_rtspsrc_open (src, TRUE); break; case CMD_PLAY: @@ -6577,6 +6575,11 @@ gst_rtspsrc_change_state (GstElement * element, GstStateChange transition) goto start_failed; break; case GST_STATE_CHANGE_READY_TO_PAUSED: + /* init some state */ + rtspsrc->cur_protocols = rtspsrc->protocols; + /* first attempt, don't ignore timeouts */ + rtspsrc->ignore_timeout = FALSE; + rtspsrc->open_error = FALSE; gst_rtspsrc_loop_send_cmd (rtspsrc, CMD_OPEN, FALSE); break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: From d17d13219c3293e0eb586ef54cb43187060dca92 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Fri, 9 Sep 2011 12:35:50 +0100 Subject: [PATCH 14/47] ac3parse: use bsid 9 and 10 to control sample rate See http://matroska.org/technical/specs/codecid/index.html The spec is silent about this though... https://bugzilla.gnome.org/show_bug.cgi?id=658546 --- gst/audioparsers/gstac3parse.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/gst/audioparsers/gstac3parse.c b/gst/audioparsers/gstac3parse.c index a1cb99789b..b2f97643c4 100644 --- a/gst/audioparsers/gstac3parse.c +++ b/gst/audioparsers/gstac3parse.c @@ -144,10 +144,10 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS ("audio/x-ac3, framed = (boolean) true, " - " channels = (int) [ 1, 6 ], rate = (int) [ 32000, 48000 ], " + " channels = (int) [ 1, 6 ], rate = (int) [ 8000, 48000 ], " " alignment = (string) { iec61937, frame}; " "audio/x-eac3, framed = (boolean) true, " - " channels = (int) [ 1, 6 ], rate = (int) [ 32000, 48000 ], " + " channels = (int) [ 1, 6 ], rate = (int) [ 8000, 48000 ], " " alignment = (string) { iec61937, frame}; ")); static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", @@ -300,7 +300,7 @@ gst_ac3_parse_frame_header_ac3 (GstAc3Parse * ac3parse, GstBuffer * buf, guint * sid) { GstBitReader bits = GST_BIT_READER_INIT_FROM_BUFFER (buf); - guint8 fscod, frmsizcod, bsid, acmod, lfe_on; + guint8 fscod, frmsizcod, bsid, acmod, lfe_on, rate_scale; GST_LOG_OBJECT (ac3parse, "parsing ac3"); @@ -337,10 +337,14 @@ gst_ac3_parse_frame_header_ac3 (GstAc3Parse * ac3parse, GstBuffer * buf, lfe_on = gst_bit_reader_get_bits_uint8_unchecked (&bits, 1); + /* 6/8->0, 9->1, 10->2, + see http://matroska.org/technical/specs/codecid/index.html */ + rate_scale = (CLAMP (bsid, 8, 10) - 8); + if (frame_size) *frame_size = frmsizcod_table[frmsizcod].frame_size[fscod] * 2; if (rate) - *rate = fscod_rates[fscod]; + *rate = fscod_rates[fscod] >> rate_scale; if (chans) *chans = acmod_chans[acmod] + lfe_on; if (blks) From aea09188dca1730c9d97063a522b334c360d3c08 Mon Sep 17 00:00:00 2001 From: Alessandro Decina Date: Mon, 12 Sep 2011 07:55:19 +0200 Subject: [PATCH 15/47] flvmux: don't release request pads going PAUSED->READY Don't release request pads but just reset them. This makes pipelines using flvmux reusable. --- gst/flv/gstflvmux.c | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/gst/flv/gstflvmux.c b/gst/flv/gstflvmux.c index f1612a475a..4b0e56b58e 100644 --- a/gst/flv/gstflvmux.c +++ b/gst/flv/gstflvmux.c @@ -114,6 +114,8 @@ static GstStateChangeReturn gst_flv_mux_change_state (GstElement * element, GstStateChange transition); static void gst_flv_mux_reset (GstElement * element); +static void gst_flv_mux_reset_pad (GstFlvMux * mux, GstFlvPad * pad, + gboolean video); typedef struct { @@ -218,10 +220,10 @@ gst_flv_mux_reset (GstElement * element) GstFlvMux *mux = GST_FLV_MUX (element); GSList *sl; - while ((sl = mux->collect->data) != NULL) { + for (sl = mux->collect->data; sl != NULL; sl = g_slist_next (sl)) { GstFlvPad *cpad = (GstFlvPad *) sl->data; - gst_element_release_request_pad (element, cpad->collect.pad); + gst_flv_mux_reset_pad (mux, cpad, cpad->video); } g_list_foreach (mux->index, (GFunc) gst_flv_mux_index_entry_free, NULL); @@ -472,6 +474,26 @@ gst_flv_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps) return ret; } +static void +gst_flv_mux_reset_pad (GstFlvMux * mux, GstFlvPad * cpad, gboolean video) +{ + cpad->video = video; + + if (cpad->audio_codec_data) + gst_buffer_unref (cpad->audio_codec_data); + cpad->audio_codec_data = NULL; + cpad->audio_codec = G_MAXUINT; + cpad->rate = G_MAXUINT; + cpad->width = G_MAXUINT; + cpad->channels = G_MAXUINT; + + if (cpad->video_codec_data) + gst_buffer_unref (cpad->video_codec_data); + cpad->video_codec_data = NULL; + cpad->video_codec = G_MAXUINT; + cpad->last_timestamp = 0; +} + static GstPad * gst_flv_mux_request_new_pad (GstElement * element, GstPadTemplate * templ, const gchar * pad_name) @@ -516,18 +538,9 @@ gst_flv_mux_request_new_pad (GstElement * element, cpad = (GstFlvPad *) gst_collect_pads_add_pad (mux->collect, pad, sizeof (GstFlvPad)); - cpad->video = video; - - cpad->audio_codec = G_MAXUINT; - cpad->rate = G_MAXUINT; - cpad->width = G_MAXUINT; - cpad->channels = G_MAXUINT; cpad->audio_codec_data = NULL; - - cpad->video_codec = G_MAXUINT; cpad->video_codec_data = NULL; - - cpad->last_timestamp = 0; + gst_flv_mux_reset_pad (mux, cpad, video); /* FIXME: hacked way to override/extend the event function of * GstCollectPads; because it sets its own event function giving the @@ -550,11 +563,7 @@ gst_flv_mux_release_pad (GstElement * element, GstPad * pad) GstFlvMux *mux = GST_FLV_MUX (GST_PAD_PARENT (pad)); GstFlvPad *cpad = (GstFlvPad *) gst_pad_get_element_private (pad); - if (cpad && cpad->audio_codec_data) - gst_buffer_unref (cpad->audio_codec_data); - if (cpad && cpad->video_codec_data) - gst_buffer_unref (cpad->video_codec_data); - + gst_flv_mux_reset_pad (mux, cpad, cpad->video); gst_collect_pads_remove_pad (mux->collect, pad); gst_element_remove_pad (element, pad); } From 261d11a6d7392f9231a4f32193d845ec26246581 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Fri, 9 Sep 2011 09:12:56 -0300 Subject: [PATCH 16/47] qtmux: Fix ctts generation for streams that don't start at 0 timestamps Subtract the first timestamp of a stream from all input buffers to get 0-based timestamps for creating a sane ctts table. Without this patch the ctts could have larger values than needed, causing the playback to have a delay at startup. As the first timestamp is only found after a few buffers are queued (due to possible reordered buffers), once we find the first timestamp we subtract it from all buffers on the queue, from that point on, all buffers have their timestamps subtract when they are collected. https://bugzilla.gnome.org/show_bug.cgi?id=658659 --- gst/isomp4/gstqtmux.c | 79 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 14 deletions(-) diff --git a/gst/isomp4/gstqtmux.c b/gst/isomp4/gstqtmux.c index 0f5c46890e..c6f2b4b64a 100644 --- a/gst/isomp4/gstqtmux.c +++ b/gst/isomp4/gstqtmux.c @@ -2029,6 +2029,39 @@ gst_qt_mux_push_ts (GstQTMux * qtmux, GstQTPad * pad, GstClockTime ts) pad->ts_n_entries++; } +static void +check_and_subtract_ts (GstQTMux * qtmux, GstClockTime * ts_a, GstClockTime ts_b) +{ + if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (*ts_a))) { + if (G_LIKELY (*ts_a > ts_b)) { + *ts_a -= ts_b; + } else { + *ts_a = 0; + GST_WARNING_OBJECT (qtmux, "Subtraction would result in negative value, " + "using 0 as result"); + } + } +} + +/* subtract ts from all buffers enqueued on the pad */ +static void +gst_qt_mux_subtract_ts (GstQTMux * qtmux, GstQTPad * pad, GstClockTime ts) +{ + gint i; + + for (i = 0; (i < QTMUX_NO_OF_TS) && (i < pad->ts_n_entries); i++) { + check_and_subtract_ts (qtmux, &pad->ts_entries[i], ts); + } + for (i = 0; i < G_N_ELEMENTS (pad->buf_entries); i++) { + if (pad->buf_entries[i]) { + check_and_subtract_ts (qtmux, &GST_BUFFER_TIMESTAMP (pad->buf_entries[i]), + ts); + check_and_subtract_ts (qtmux, + &GST_BUFFER_OFFSET_END (pad->buf_entries[i]), ts); + } + } +} + /* takes ownership of @buf */ static GstBuffer * gst_qt_mux_get_asc_buffer_ts (GstQTMux * qtmux, GstQTPad * pad, GstBuffer * buf) @@ -2085,6 +2118,13 @@ gst_qt_mux_add_buffer (GstQTMux * qtmux, GstQTPad * pad, GstBuffer * buf) buf = pad->prepare_buf_func (pad, buf, qtmux); } + if (G_LIKELY (buf != NULL && GST_CLOCK_TIME_IS_VALID (pad->first_ts))) { + buf = gst_buffer_make_metadata_writable (buf); + check_and_subtract_ts (qtmux, &GST_BUFFER_TIMESTAMP (buf), pad->first_ts); + } + /* when we obtain the first_ts we subtract from all stored buffers we have, + * after that we can subtract on input */ + again: last_buf = pad->last_buf; if (qtmux->dts_method == DTS_METHOD_REORDER) { @@ -2158,6 +2198,31 @@ again: goto no_order; } + /* if this is the first buffer, store the timestamp */ + if (G_UNLIKELY (pad->first_ts == GST_CLOCK_TIME_NONE) && last_buf) { + if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (last_buf))) { + pad->first_ts = GST_BUFFER_TIMESTAMP (last_buf); + } else { + GST_DEBUG_OBJECT (qtmux, "First buffer for pad %s has no timestamp, " + "using 0 as first timestamp", GST_PAD_NAME (pad->collect.pad)); + pad->first_ts = 0; + } + GST_DEBUG_OBJECT (qtmux, "Stored first timestamp for pad %s %" + GST_TIME_FORMAT, GST_PAD_NAME (pad->collect.pad), + GST_TIME_ARGS (pad->first_ts)); + + gst_qt_mux_subtract_ts (qtmux, pad, pad->first_ts); + + GST_BUFFER_TIMESTAMP (last_buf) = 0; + check_and_subtract_ts (qtmux, &GST_BUFFER_OFFSET_END (last_buf), + pad->first_ts); + if (buf) { + check_and_subtract_ts (qtmux, &GST_BUFFER_TIMESTAMP (buf), pad->first_ts); + check_and_subtract_ts (qtmux, &GST_BUFFER_OFFSET_END (buf), + pad->first_ts); + } + } + /* fall back to duration if last buffer or * out-of-order (determined previously), otherwise use input ts */ if (buf == NULL || @@ -2317,20 +2382,6 @@ again: qtmux->longest_chunk = duration; } - /* if this is the first buffer, store the timestamp */ - if (G_UNLIKELY (pad->first_ts == GST_CLOCK_TIME_NONE) && last_buf) { - if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (last_buf))) { - pad->first_ts = GST_BUFFER_TIMESTAMP (last_buf); - } else { - GST_DEBUG_OBJECT (qtmux, "First buffer for pad %s has no timestamp, " - "using 0 as first timestamp", GST_PAD_NAME (pad->collect.pad)); - pad->first_ts = 0; - } - GST_DEBUG_OBJECT (qtmux, "Stored first timestamp for pad %s %" - GST_TIME_FORMAT, GST_PAD_NAME (pad->collect.pad), - GST_TIME_ARGS (pad->first_ts)); - } - /* now we go and register this buffer/sample all over */ /* note that a new chunk is started each time (not fancy but works) */ if (qtmux->moov_recov_file) { From c78048bc25c6b0b7018a442bab24227452b6dd57 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Mon, 12 Sep 2011 09:21:47 -0300 Subject: [PATCH 17/47] tests: flvmux: Fix flvmux's tests after fix for request pads handling Now that flvmux doesn't release its request pads on PAUSED->READY the test doesn't need to re-request them for every reuse test start. --- tests/check/elements/flvmux.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/check/elements/flvmux.c b/tests/check/elements/flvmux.c index 6930ac71e7..fe10282898 100644 --- a/tests/check/elements/flvmux.c +++ b/tests/check/elements/flvmux.c @@ -50,6 +50,7 @@ static void mux_pcm_audio (guint num_buffers, guint repeat) { GstElement *src, *sink, *flvmux, *conv, *pipeline; + GstPad *sinkpad, *srcpad; gint counter; GST_LOG ("num_buffers = %u", num_buffers); @@ -83,25 +84,24 @@ mux_pcm_audio (guint num_buffers, guint repeat) fail_unless (gst_element_link (src, conv)); fail_unless (gst_element_link (flvmux, sink)); + /* now link the elements */ + sinkpad = gst_element_get_request_pad (flvmux, "audio"); + fail_unless (sinkpad != NULL, "Could not get audio request pad"); + + srcpad = gst_element_get_static_pad (conv, "src"); + fail_unless (srcpad != NULL, "Could not get audioconvert's source pad"); + + fail_unless_equals_int (gst_pad_link (srcpad, sinkpad), GST_PAD_LINK_OK); + + gst_object_unref (srcpad); + gst_object_unref (sinkpad); + do { GstStateChangeReturn state_ret; GstMessage *msg; - GstPad *sinkpad, *srcpad; GST_LOG ("repeat=%d", repeat); - /* now link the elements */ - sinkpad = gst_element_get_request_pad (flvmux, "audio"); - fail_unless (sinkpad != NULL, "Could not get audio request pad"); - - srcpad = gst_element_get_static_pad (conv, "src"); - fail_unless (srcpad != NULL, "Could not get audioconvert's source pad"); - - fail_unless_equals_int (gst_pad_link (srcpad, sinkpad), GST_PAD_LINK_OK); - - gst_object_unref (srcpad); - gst_object_unref (sinkpad); - counter = 0; state_ret = gst_element_set_state (pipeline, GST_STATE_PAUSED); From 682ae32f6f8a81e4c3cb164bb285c3d5f1069fa5 Mon Sep 17 00:00:00 2001 From: David Svensson Fors Date: Wed, 14 Sep 2011 14:46:00 +0200 Subject: [PATCH 18/47] matroskademux: configurable timestamp gap handling matroskademux performs segment tricks to skip gaps in streams, notably at start for non 0 based files. There may however be cases when full presentation (including intermediate gaps) is desired, so a property allows to configure as of which gap to act (or not at all). API: GstMatroskaDemux::max-gap-time Fixes #659009. --- gst/matroska/matroska-demux.c | 71 +++++++++++++++++++++++++++++++++-- gst/matroska/matroska-demux.h | 3 ++ 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c index 7fb4071d04..4b39dc9294 100644 --- a/gst/matroska/matroska-demux.c +++ b/gst/matroska/matroska-demux.c @@ -81,9 +81,12 @@ enum { ARG_0, ARG_METADATA, - ARG_STREAMINFO + ARG_STREAMINFO, + ARG_MAX_GAP_TIME }; +#define DEFAULT_MAX_GAP_TIME (2 * GST_SECOND) + static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, @@ -168,6 +171,12 @@ static void gst_matroska_demux_reset (GstElement * element); static gboolean perform_seek_to_offset (GstMatroskaDemux * demux, guint64 offset); +/* gobject functions */ +static void gst_matroska_demux_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_matroska_demux_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + GType gst_matroska_demux_get_type (void); GST_BOILERPLATE (GstMatroskaDemux, gst_matroska_demux, GstElement, GST_TYPE_ELEMENT); @@ -223,6 +232,15 @@ gst_matroska_demux_class_init (GstMatroskaDemuxClass * klass) gobject_class->finalize = gst_matroska_demux_finalize; + gobject_class->get_property = gst_matroska_demux_get_property; + gobject_class->set_property = gst_matroska_demux_set_property; + + g_object_class_install_property (gobject_class, ARG_MAX_GAP_TIME, + g_param_spec_uint64 ("max-gap-time", "Maximum gap time", + "The demuxer sends out newsegment events for skipping " + "gaps longer than this (0 = disabled).", 0, G_MAXUINT64, + DEFAULT_MAX_GAP_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_matroska_demux_change_state); gstelement_class->send_event = @@ -262,6 +280,9 @@ gst_matroska_demux_init (GstMatroskaDemux * demux, demux->common.adapter = gst_adapter_new (); + /* property defaults */ + demux->max_gap_time = DEFAULT_MAX_GAP_TIME; + /* finish off */ gst_matroska_demux_reset (GST_ELEMENT (demux)); } @@ -3364,7 +3385,8 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, /* handle gaps, e.g. non-zero start-time, or an cue index entry * that landed us with timestamps not quite intended */ GST_OBJECT_LOCK (demux); - if (GST_CLOCK_TIME_IS_VALID (demux->last_stop_end) && + if (demux->max_gap_time && + GST_CLOCK_TIME_IS_VALID (demux->last_stop_end) && demux->common.segment.rate > 0.0) { GstClockTimeDiff diff; GstEvent *event1, *event2; @@ -3373,7 +3395,8 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, * otherwise if these go back and forth downstream (sinks) increase * accumulated time and running_time */ diff = GST_CLOCK_DIFF (demux->last_stop_end, lace_time); - if (diff > 2 * GST_SECOND && lace_time > demux->common.segment.start + if (diff > demux->max_gap_time + && lace_time > demux->common.segment.start && (!GST_CLOCK_TIME_IS_VALID (demux->common.segment.stop) || lace_time < demux->common.segment.stop)) { GST_DEBUG_OBJECT (demux, @@ -5412,6 +5435,48 @@ gst_matroska_demux_change_state (GstElement * element, return ret; } +static void +gst_matroska_demux_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstMatroskaDemux *demux; + + g_return_if_fail (GST_IS_MATROSKA_DEMUX (object)); + demux = GST_MATROSKA_DEMUX (object); + + switch (prop_id) { + case ARG_MAX_GAP_TIME: + GST_OBJECT_LOCK (demux); + demux->max_gap_time = g_value_get_uint64 (value); + GST_OBJECT_UNLOCK (demux); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_matroska_demux_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstMatroskaDemux *demux; + + g_return_if_fail (GST_IS_MATROSKA_DEMUX (object)); + demux = GST_MATROSKA_DEMUX (object); + + switch (prop_id) { + case ARG_MAX_GAP_TIME: + GST_OBJECT_LOCK (demux); + g_value_set_uint64 (value, demux->max_gap_time); + GST_OBJECT_UNLOCK (demux); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + gboolean gst_matroska_demux_plugin_init (GstPlugin * plugin) { diff --git a/gst/matroska/matroska-demux.h b/gst/matroska/matroska-demux.h index c9ab2fc8e1..192189f13b 100644 --- a/gst/matroska/matroska-demux.h +++ b/gst/matroska/matroska-demux.h @@ -91,6 +91,9 @@ typedef struct _GstMatroskaDemux { /* reverse playback */ GArray *seek_index; gint seek_entry; + + /* gap handling */ + guint64 max_gap_time; } GstMatroskaDemux; typedef struct _GstMatroskaDemuxClass { From 352bab2ef74ce44a6b7f8b583ed549833e2315c2 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 14 Sep 2011 16:37:12 +0100 Subject: [PATCH 19/47] navseek: toggle pause/play on space bar A useful thing to have. https://bugzilla.gnome.org/show_bug.cgi?id=659065 --- gst/debugutils/gstnavseek.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/gst/debugutils/gstnavseek.c b/gst/debugutils/gstnavseek.c index c7b674dbdc..5dedf604e5 100644 --- a/gst/debugutils/gstnavseek.c +++ b/gst/debugutils/gstnavseek.c @@ -211,6 +211,23 @@ gst_navseek_segseek (GstNavSeek * navseek) gst_object_unref (peer_pad); } +static void +gst_navseek_toggle_play_pause (GstNavSeek * navseek) +{ + GstStateChangeReturn sret; + GstState current, pending, state; + + sret = gst_element_get_state (GST_ELEMENT (navseek), ¤t, &pending, 0); + if (sret == GST_STATE_CHANGE_FAILURE) + return; + + state = (pending != GST_STATE_VOID_PENDING) ? pending : current; + + gst_element_post_message (GST_ELEMENT (navseek), + gst_message_new_request_state (GST_OBJECT (navseek), + (state == GST_STATE_PLAYING) ? GST_STATE_PAUSED : GST_STATE_PLAYING)); +} + static gboolean gst_navseek_handle_src_event (GstPad * pad, GstEvent * event) { @@ -263,6 +280,8 @@ gst_navseek_handle_src_event (GstPad * pad, GstEvent * event) } else if (strcmp (key, "n") == 0) { /* normal speed */ gst_navseek_change_playback_rate (navseek, 1.0); + } else if (strcmp (key, "space") == 0) { + gst_navseek_toggle_play_pause (navseek); } } else { break; From 26ae2330357174e2d806d9953ddad0baa364188d Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Thu, 15 Sep 2011 15:53:47 +0100 Subject: [PATCH 20/47] matroskademux: fix stuttering A/V Someone got had by implicit promotion to unsigned in ops with a signed and an unsigned value. https://bugzilla.gnome.org/show_bug.cgi?id=659153 --- gst/matroska/matroska-demux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c index 4b39dc9294..fad741a26b 100644 --- a/gst/matroska/matroska-demux.c +++ b/gst/matroska/matroska-demux.c @@ -3395,7 +3395,7 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, * otherwise if these go back and forth downstream (sinks) increase * accumulated time and running_time */ diff = GST_CLOCK_DIFF (demux->last_stop_end, lace_time); - if (diff > demux->max_gap_time + if (diff > 0 && diff > demux->max_gap_time && lace_time > demux->common.segment.start && (!GST_CLOCK_TIME_IS_VALID (demux->common.segment.stop) || lace_time < demux->common.segment.stop)) { From 11b0a0effcf0e23d9abf8425f191b932188e3a74 Mon Sep 17 00:00:00 2001 From: Branko Subasic Date: Fri, 16 Sep 2011 15:03:23 +0200 Subject: [PATCH 21/47] matroskademux: Avoid sending EOS when in paused state Changed the ebml reader's gst_ebml_peek_id_length() function so that it returns the actual reason for why the peek failed, instead of (almost) always returning GST_FLOW_UNEXPECTED. This prevents the pulling task from sending EOS when doing a flushing seek. --- gst/matroska/ebml-read.c | 39 +++++++++++++---------------- gst/matroska/ebml-read.h | 2 +- gst/matroska/matroska-read-common.c | 12 ++++----- 3 files changed, 24 insertions(+), 29 deletions(-) diff --git a/gst/matroska/ebml-read.c b/gst/matroska/ebml-read.c index c2dfa522a4..f6bf1348c9 100644 --- a/gst/matroska/ebml-read.c +++ b/gst/matroska/ebml-read.c @@ -60,6 +60,7 @@ gst_ebml_peek_id_length (guint32 * _id, guint64 * _length, guint * _needed, gint len_mask = 0x80, read = 1, n = 1, num_ffs = 0; guint64 total; guint8 b; + GstFlowReturn ret; g_return_val_if_fail (_id != NULL, GST_FLOW_ERROR); g_return_val_if_fail (_length != NULL, GST_FLOW_ERROR); @@ -71,10 +72,9 @@ gst_ebml_peek_id_length (guint32 * _id, guint64 * _length, guint * _needed, /* read element id */ needed = 2; - buf = peek (ctx, needed); - if (!buf) - goto not_enough_data; - + ret = peek (ctx, needed, &buf); + if (ret != GST_FLOW_OK) + goto peek_error; b = GST_READ_UINT8 (buf); total = (guint64) b; while (read <= 4 && !(total & len_mask)) { @@ -86,10 +86,9 @@ gst_ebml_peek_id_length (guint32 * _id, guint64 * _length, guint * _needed, /* need id and at least something for subsequent length */ needed = read + 1; - buf = peek (ctx, needed); - if (!buf) - goto not_enough_data; - + ret = peek (ctx, needed, &buf); + if (ret != GST_FLOW_OK) + goto peek_error; while (n < read) { b = GST_READ_UINT8 (buf + n); total = (total << 8) | b; @@ -112,10 +111,9 @@ gst_ebml_peek_id_length (guint32 * _id, guint64 * _length, guint * _needed, num_ffs++; needed += read - 1; - buf = peek (ctx, needed); - if (!buf) - goto not_enough_data; - + ret = peek (ctx, needed, &buf); + if (ret != GST_FLOW_OK) + goto peek_error; buf += (needed - read); n = 1; while (n < read) { @@ -137,10 +135,11 @@ gst_ebml_peek_id_length (guint32 * _id, guint64 * _length, guint * _needed, return GST_FLOW_OK; /* ERRORS */ -not_enough_data: +peek_error: { + GST_WARNING_OBJECT (el, "peek failed, ret = %d", ret); *_needed = needed; - return GST_FLOW_UNEXPECTED; + return ret; } invalid_id: { @@ -190,15 +189,13 @@ gst_ebml_read_clear (GstEbmlRead * ebml) ebml->el = NULL; } -static const guint8 * -gst_ebml_read_peek (GstByteReader * br, guint peek) +static GstFlowReturn +gst_ebml_read_peek (GstByteReader * br, guint peek, const guint8 ** data) { - const guint8 *data = NULL; - - if (G_LIKELY (gst_byte_reader_peek_data (br, peek, &data))) - return data; + if (G_LIKELY (gst_byte_reader_peek_data (br, peek, data))) + return GST_FLOW_OK; else - return NULL; + return GST_FLOW_UNEXPECTED; } static GstFlowReturn diff --git a/gst/matroska/ebml-read.h b/gst/matroska/ebml-read.h index b40c31d352..9db38f54ca 100644 --- a/gst/matroska/ebml-read.h +++ b/gst/matroska/ebml-read.h @@ -59,7 +59,7 @@ typedef struct _GstEbmlRead { GArray *readers; } GstEbmlRead; -typedef const guint8 * (*GstPeekData) (gpointer * context, guint peek); +typedef GstFlowReturn (*GstPeekData) (gpointer * context, guint peek, const guint8 ** data); /* returns UNEXPECTED if not enough data */ GstFlowReturn gst_ebml_peek_id_length (guint32 * _id, guint64 * _length, diff --git a/gst/matroska/matroska-read-common.c b/gst/matroska/matroska-read-common.c index 32b513e12b..672dcc3fc6 100644 --- a/gst/matroska/matroska-read-common.c +++ b/gst/matroska/matroska-read-common.c @@ -1639,14 +1639,12 @@ gst_matroska_read_common_peek_bytes (GstMatroskaReadCommon * common, guint64 return GST_FLOW_OK; } -static const guint8 * -gst_matroska_read_common_peek_pull (GstMatroskaReadCommon * common, guint peek) +static GstFlowReturn +gst_matroska_read_common_peek_pull (GstMatroskaReadCommon * common, guint peek, + guint8 ** data) { - guint8 *data = NULL; - - gst_matroska_read_common_peek_bytes (common, common->offset, peek, NULL, - &data); - return data; + return gst_matroska_read_common_peek_bytes (common, common->offset, peek, + NULL, data); } GstFlowReturn From 8ca420f547119f0a5a4a35eb3675f0d2f6b7c085 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Tue, 29 Mar 2011 12:09:18 +0530 Subject: [PATCH 22/47] pulse: New pulseaudiosink element to handle format changes This introduces a new bin which wraps around pulsesink and depending on the formats supported by the sink, plugs in/out a decodebin2 as required. This allows users to switch sinks on the stream and adapts accordingly (for example, you could watch a movie in passthrough mode on your receiver which supports AC3 decode, then plug out and switch to a non-digital profile to continue uninterrupted on analog output). The bin is required because doing the same with playbin2/playsink will require API changes that cannot be made in 0.10. With 0.11/1.0, we should be able to ask for upstream caps renegotiation to deal with all this. https://bugzilla.gnome.org/show_bug.cgi?id=657179 --- ext/pulse/Makefile.am | 4 +- ext/pulse/plugin.c | 6 + ext/pulse/pulseaudiosink.c | 927 +++++++++++++++++++++++++++++++++++++ ext/pulse/pulsesink.c | 81 +--- ext/pulse/pulsesink.h | 89 ++++ ext/pulse/pulseutil.h | 4 + 6 files changed, 1053 insertions(+), 58 deletions(-) create mode 100644 ext/pulse/pulseaudiosink.c diff --git a/ext/pulse/Makefile.am b/ext/pulse/Makefile.am index 9c0d6b7171..2438f5efc3 100644 --- a/ext/pulse/Makefile.am +++ b/ext/pulse/Makefile.am @@ -7,12 +7,14 @@ libgstpulse_la_SOURCES = \ pulsemixertrack.c \ pulseprobe.c \ pulsesink.c \ + pulseaudiosink.c \ pulsesrc.c \ pulseutil.c libgstpulse_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(PULSE_CFLAGS) libgstpulse_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_MAJORMINOR) \ - -lgstinterfaces-$(GST_MAJORMINOR) $(GST_BASE_LIBS) $(GST_LIBS) $(PULSE_LIBS) + -lgstinterfaces-$(GST_MAJORMINOR) -lgstpbutils-$(GST_MAJORMINOR) \ + $(GST_BASE_LIBS) $(GST_LIBS) $(PULSE_LIBS) libgstpulse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstpulse_la_LIBTOOLFLAGS = --tag=disable-static diff --git a/ext/pulse/plugin.c b/ext/pulse/plugin.c index 3abc26fc96..6b2e6b4bf7 100644 --- a/ext/pulse/plugin.c +++ b/ext/pulse/plugin.c @@ -49,6 +49,12 @@ plugin_init (GstPlugin * plugin) GST_TYPE_PULSESRC)) return FALSE; +#ifdef HAVE_PULSE_1_0 + if (!gst_element_register (plugin, "pulseaudiosink", GST_RANK_PRIMARY + 11, + GST_TYPE_PULSE_AUDIO_SINK)) + return FALSE; +#endif + if (!gst_element_register (plugin, "pulsemixer", GST_RANK_NONE, GST_TYPE_PULSEMIXER)) return FALSE; diff --git a/ext/pulse/pulseaudiosink.c b/ext/pulse/pulseaudiosink.c new file mode 100644 index 0000000000..b7e1a07254 --- /dev/null +++ b/ext/pulse/pulseaudiosink.c @@ -0,0 +1,927 @@ +/*-*- Mode: C; c-basic-offset: 2 -*-*/ + +/* GStreamer pulseaudio plugin + * + * Copyright (c) 2011 Intel Corporation + * 2011 Collabora + * 2011 Arun Raghavan + * 2011 Sebastian Dröge + * + * gst-pulse is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * gst-pulse is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with gst-pulse; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA. + */ + +/** + * SECTION:element-pulseaudiosink + * @see_also: pulsesink, pulsesrc, pulsemixer + * + * This element outputs audio to a + * PulseAudio sound server via + * the @pulsesink element. It transparently takes care of passing compressed + * format as-is if the sink supports it, decoding if necessary, and changes + * to supported formats at runtime. + * + * + * Example pipelines + * |[ + * gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisdec ! pulseaudiosink + * ]| Decode and play an Ogg/Vorbis file. + * |[ + * gst-launch -v filesrc location=test.mp3 ! mp3parse ! pulseaudiosink stream-properties="props,media.title=test" + * ]| Play an MP3 file on a sink that supports decoding directly, plug in a + * decoder if/when required. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_PULSE_1_0 + +#include +#include + +#include +#include "pulsesink.h" + +GST_DEBUG_CATEGORY (pulseaudiosink_debug); +#define GST_CAT_DEFAULT (pulseaudiosink_debug) + +#define GST_PULSE_AUDIO_SINK_LOCK(obj) G_STMT_START { \ + GST_LOG_OBJECT (obj, \ + "locking from thread %p", \ + g_thread_self ()); \ + g_mutex_lock (GST_PULSE_AUDIO_SINK_CAST(obj)->lock); \ + GST_LOG_OBJECT (obj, \ + "locked from thread %p", \ + g_thread_self ()); \ +} G_STMT_END + +#define GST_PULSE_AUDIO_SINK_UNLOCK(obj) G_STMT_START { \ + GST_LOG_OBJECT (obj, \ + "unlocking from thread %p", \ + g_thread_self ()); \ + g_mutex_unlock (GST_PULSE_AUDIO_SINK_CAST(obj)->lock); \ +} G_STMT_END + +typedef struct +{ + GstBin parent; + GMutex *lock; + + GstPad *sinkpad; + GstPad *sink_proxypad; + GstPadEventFunction sinkpad_old_eventfunc; + GstPadEventFunction proxypad_old_eventfunc; + + GstPulseSink *psink; + GstElement *dbin2; + + GstSegment segment; + + guint event_probe_id; + gulong pad_added_id; + + gboolean format_lost; +} GstPulseAudioSink; + +typedef struct +{ + GstBinClass parent_class; + guint n_prop_own; + guint n_prop_total; +} GstPulseAudioSinkClass; + +static void gst_pulse_audio_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_pulse_audio_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_pulse_audio_sink_dispose (GObject * object); +static gboolean gst_pulse_audio_sink_src_event (GstPad * pad, GstEvent * event); +static gboolean gst_pulse_audio_sink_sink_event (GstPad * pad, + GstEvent * event); +static gboolean gst_pulse_audio_sink_sink_acceptcaps (GstPad * pad, + GstCaps * caps); +static gboolean gst_pulse_audio_sink_sink_setcaps (GstPad * pad, + GstCaps * caps); +static GstStateChangeReturn +gst_pulse_audio_sink_change_state (GstElement * element, + GstStateChange transition); + +static void +gst_pulse_audio_sink_do_init (GType type) +{ + GST_DEBUG_CATEGORY_INIT (pulseaudiosink_debug, "pulseaudiosink", 0, + "Bin that wraps pulsesink for handling compressed formats"); +} + +GST_BOILERPLATE_FULL (GstPulseAudioSink, gst_pulse_audio_sink, GstBin, + GST_TYPE_BIN, gst_pulse_audio_sink_do_init); + +static GstStaticPadTemplate sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS (PULSE_SINK_TEMPLATE_CAPS)); + +static void +gst_pulse_audio_sink_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_set_details_simple (element_class, + "Bin wrapping pulsesink", "Sink/Audio/Bin", + "Correctly handles sink changes when streaming compressed formats to " + "pulsesink", "Arun Raghavan "); +} + +static GParamSpec * +param_spec_copy (GParamSpec * spec) +{ + const char *name, *nick, *blurb; + GParamFlags flags; + + name = g_param_spec_get_name (spec); + nick = g_param_spec_get_nick (spec); + blurb = g_param_spec_get_blurb (spec); + flags = spec->flags; + + if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_BOOLEAN) { + return g_param_spec_boolean (name, nick, blurb, + G_PARAM_SPEC_BOOLEAN (spec)->default_value, flags); + } + + if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_BOXED) { + return g_param_spec_boxed (name, nick, blurb, spec->value_type, flags); + } + + if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_CHAR) { + GParamSpecChar *cspec = G_PARAM_SPEC_CHAR (spec); + return g_param_spec_char (name, nick, blurb, cspec->minimum, + cspec->maximum, cspec->default_value, flags); + } + + if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_DOUBLE) { + GParamSpecDouble *dspec = G_PARAM_SPEC_DOUBLE (spec); + return g_param_spec_double (name, nick, blurb, dspec->minimum, + dspec->maximum, dspec->default_value, flags); + } + + if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_ENUM) { + return g_param_spec_enum (name, nick, blurb, spec->value_type, + G_PARAM_SPEC_ENUM (spec)->default_value, flags); + } + + if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_FLAGS) { + return g_param_spec_flags (name, nick, blurb, spec->value_type, + G_PARAM_SPEC_ENUM (spec)->default_value, flags); + } + + if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_FLOAT) { + GParamSpecFloat *fspec = G_PARAM_SPEC_FLOAT (spec); + return g_param_spec_double (name, nick, blurb, fspec->minimum, + fspec->maximum, fspec->default_value, flags); + } + + if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_GTYPE) { + return g_param_spec_gtype (name, nick, blurb, + G_PARAM_SPEC_GTYPE (spec)->is_a_type, flags); + } + + if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_INT) { + GParamSpecInt *ispec = G_PARAM_SPEC_INT (spec); + return g_param_spec_int (name, nick, blurb, ispec->minimum, + ispec->maximum, ispec->default_value, flags); + } + + if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_INT64) { + GParamSpecInt64 *ispec = G_PARAM_SPEC_INT64 (spec); + return g_param_spec_int64 (name, nick, blurb, ispec->minimum, + ispec->maximum, ispec->default_value, flags); + } + + if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_LONG) { + GParamSpecLong *lspec = G_PARAM_SPEC_LONG (spec); + return g_param_spec_long (name, nick, blurb, lspec->minimum, + lspec->maximum, lspec->default_value, flags); + } + + if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_OBJECT) { + return g_param_spec_object (name, nick, blurb, spec->value_type, flags); + } + + if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_PARAM) { + return g_param_spec_param (name, nick, blurb, spec->value_type, flags); + } + + if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_POINTER) { + return g_param_spec_pointer (name, nick, blurb, flags); + } + + if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_STRING) { + return g_param_spec_string (name, nick, blurb, + G_PARAM_SPEC_STRING (spec)->default_value, flags); + } + + if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_UCHAR) { + GParamSpecUChar *cspec = G_PARAM_SPEC_UCHAR (spec); + return g_param_spec_uchar (name, nick, blurb, cspec->minimum, + cspec->maximum, cspec->default_value, flags); + } + + if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_UINT) { + GParamSpecUInt *ispec = G_PARAM_SPEC_UINT (spec); + return g_param_spec_uint (name, nick, blurb, ispec->minimum, + ispec->maximum, ispec->default_value, flags); + } + + if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_UINT64) { + GParamSpecUInt64 *ispec = G_PARAM_SPEC_UINT64 (spec); + return g_param_spec_uint64 (name, nick, blurb, ispec->minimum, + ispec->maximum, ispec->default_value, flags); + } + + if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_ULONG) { + GParamSpecULong *lspec = G_PARAM_SPEC_ULONG (spec); + return g_param_spec_ulong (name, nick, blurb, lspec->minimum, + lspec->maximum, lspec->default_value, flags); + } + + if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_UNICHAR) { + return g_param_spec_unichar (name, nick, blurb, + G_PARAM_SPEC_UNICHAR (spec)->default_value, flags); + } + + if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_VARIANT) { + GParamSpecVariant *vspec = G_PARAM_SPEC_VARIANT (spec); + return g_param_spec_variant (name, nick, blurb, vspec->type, + vspec->default_value, flags); + } + + if (G_PARAM_SPEC_TYPE (spec) == GST_TYPE_PARAM_MINI_OBJECT) { + return gst_param_spec_mini_object (name, nick, blurb, spec->value_type, + flags); + } + + g_warning ("Unknown param type %ld for '%s'", + (long) G_PARAM_SPEC_TYPE (spec), name); + g_assert_not_reached (); +} + +static void +gst_pulse_audio_sink_class_init (GstPulseAudioSinkClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstElementClass *element_class = (GstElementClass *) klass; + GstPulseSinkClass *psink_class = + GST_PULSESINK_CLASS (g_type_class_ref (GST_TYPE_PULSESINK)); + GParamSpec **specs; + guint n, i, j; + + gobject_class->get_property = gst_pulse_audio_sink_get_property; + gobject_class->set_property = gst_pulse_audio_sink_set_property; + gobject_class->dispose = gst_pulse_audio_sink_dispose; + element_class->change_state = + GST_DEBUG_FUNCPTR (gst_pulse_audio_sink_change_state); + + /* Find out how many properties we already have */ + specs = g_object_class_list_properties (gobject_class, &klass->n_prop_own); + g_free (specs); + + /* Proxy pulsesink's properties */ + specs = g_object_class_list_properties (G_OBJECT_CLASS (psink_class), &n); + for (i = 0, j = klass->n_prop_own; i < n; i++) { + if (g_object_class_find_property (gobject_class, + g_param_spec_get_name (specs[i]))) { + /* We already inherited this property from a parent, skip */ + j--; + } else { + g_object_class_install_property (gobject_class, i + j + 1, + param_spec_copy (specs[i])); + } + } + + klass->n_prop_total = i + j; + + g_free (specs); + g_type_class_unref (psink_class); +} + +static GstPad * +get_proxypad (GstPad * sinkpad) +{ + GstIterator *iter = NULL; + GstPad *proxypad = NULL; + + iter = gst_pad_iterate_internal_links (sinkpad); + if (iter) { + if (gst_iterator_next (iter, (gpointer) & proxypad) != GST_ITERATOR_OK) + proxypad = NULL; + gst_iterator_free (iter); + } + + return proxypad; +} + +static void +post_missing_element_message (GstPulseAudioSink * pbin, const gchar * name) +{ + GstMessage *msg; + + msg = gst_missing_element_message_new (GST_ELEMENT_CAST (pbin), name); + gst_element_post_message (GST_ELEMENT_CAST (pbin), msg); +} + +static void +notify_cb (GObject * selector, GParamSpec * pspec, GstPulseAudioSink * pbin) +{ + g_object_notify (G_OBJECT (pbin), g_param_spec_get_name (pspec)); +} + +static void +gst_pulse_audio_sink_init (GstPulseAudioSink * pbin, + GstPulseAudioSinkClass * klass) +{ + GstPad *pad = NULL; + GParamSpec **specs; + GString *prop; + guint i; + + pbin->lock = g_mutex_new (); + + gst_segment_init (&pbin->segment, GST_FORMAT_UNDEFINED); + + pbin->psink = GST_PULSESINK (gst_element_factory_make ("pulsesink", + "pulseaudiosink-sink")); + g_assert (pbin->psink != NULL); + + if (!gst_bin_add (GST_BIN (pbin), GST_ELEMENT (pbin->psink))) { + GST_ERROR_OBJECT (pbin, "Failed to add pulsesink to bin"); + goto error; + } + + pad = gst_element_get_static_pad (GST_ELEMENT (pbin->psink), "sink"); + pbin->sinkpad = gst_ghost_pad_new_from_template ("sink", pad, + gst_static_pad_template_get (&sink_template)); + + pbin->sinkpad_old_eventfunc = GST_PAD_EVENTFUNC (pbin->sinkpad); + gst_pad_set_event_function (pbin->sinkpad, + GST_DEBUG_FUNCPTR (gst_pulse_audio_sink_sink_event)); + gst_pad_set_setcaps_function (pbin->sinkpad, + GST_DEBUG_FUNCPTR (gst_pulse_audio_sink_sink_setcaps)); + gst_pad_set_acceptcaps_function (pbin->sinkpad, + GST_DEBUG_FUNCPTR (gst_pulse_audio_sink_sink_acceptcaps)); + + gst_element_add_pad (GST_ELEMENT (pbin), pbin->sinkpad); + + if (!(pbin->sink_proxypad = get_proxypad (pbin->sinkpad))) + GST_ERROR_OBJECT (pbin, "Failed to get proxypad of srcpad"); + else { + pbin->proxypad_old_eventfunc = GST_PAD_EVENTFUNC (pbin->sink_proxypad); + gst_pad_set_event_function (pbin->sink_proxypad, + GST_DEBUG_FUNCPTR (gst_pulse_audio_sink_src_event)); + } + + /* Now proxy all the notify::* signals */ + specs = g_object_class_list_properties (G_OBJECT_CLASS (klass), &i); + prop = g_string_sized_new (30); + + for (i--; i >= klass->n_prop_own; i--) { + g_string_printf (prop, "notify::%s", g_param_spec_get_name (specs[i])); + g_signal_connect (pbin->psink, prop->str, G_CALLBACK (notify_cb), pbin); + } + + g_string_free (prop, TRUE); + g_free (specs); + + pbin->format_lost = FALSE; + +out: + if (pad) + gst_object_unref (pad); + + return; + +error: + if (pbin->psink) + gst_object_unref (pbin->psink); + goto out; +} + +static void +gst_pulse_audio_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (object); + GstPulseAudioSinkClass *klass = + GST_PULSE_AUDIO_SINK_CLASS (G_OBJECT_GET_CLASS (object)); + + g_return_if_fail (prop_id <= klass->n_prop_total); + + g_object_set_property (G_OBJECT (pbin->psink), g_param_spec_get_name (pspec), + value); +} + +static void +gst_pulse_audio_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (object); + GstPulseAudioSinkClass *klass = + GST_PULSE_AUDIO_SINK_CLASS (G_OBJECT_GET_CLASS (object)); + + g_return_if_fail (prop_id <= klass->n_prop_total); + + g_object_get_property (G_OBJECT (pbin->psink), g_param_spec_get_name (pspec), + value); +} + +static void +gst_pulse_audio_sink_free_dbin2 (GstPulseAudioSink * pbin) +{ + g_signal_handler_disconnect (pbin->dbin2, pbin->pad_added_id); + gst_element_set_state (pbin->dbin2, GST_STATE_NULL); + + gst_bin_remove (GST_BIN (pbin), pbin->dbin2); + + pbin->dbin2 = NULL; +} + +static void +gst_pulse_audio_sink_dispose (GObject * object) +{ + GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (object); + + if (pbin->lock) { + g_mutex_free (pbin->lock); + pbin->lock = NULL; + } + + if (pbin->sink_proxypad) { + gst_object_unref (pbin->sink_proxypad); + pbin->sink_proxypad = NULL; + } + + if (pbin->dbin2) { + g_signal_handler_disconnect (pbin->dbin2, pbin->pad_added_id); + pbin->dbin2 = NULL; + } + + pbin->sinkpad = NULL; + pbin->psink = NULL; +} + +static gboolean +gst_pulse_audio_sink_update_sinkpad (GstPulseAudioSink * pbin, GstPad * sinkpad) +{ + gboolean ret; + + ret = gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (pbin->sinkpad), sinkpad); + + if (!ret) + GST_WARNING_OBJECT (pbin, "Could not update ghostpad target"); + + return ret; +} + +static void +distribute_running_time (GstElement * element, const GstSegment * segment) +{ + GstEvent *event; + GstPad *pad; + + pad = gst_element_get_static_pad (element, "sink"); + + /* FIXME: Some decoders collect newsegments and send them out at once, making + * them lose accumulator events (and thus making dbin2_event_probe() hard to + * do right if we're sending these as well. We can get away with not sending + * these at the moment, but this should be fixed! */ +#if 0 + if (segment->accum) { + event = gst_event_new_new_segment_full (FALSE, segment->rate, + segment->applied_rate, segment->format, 0, segment->accum, 0); + gst_pad_send_event (pad, event); + } +#endif + + event = gst_event_new_new_segment_full (FALSE, segment->rate, + segment->applied_rate, segment->format, + segment->start, segment->stop, segment->time); + gst_pad_send_event (pad, event); + + gst_object_unref (pad); +} + +static gboolean +dbin2_event_probe (GstPad * pad, GstMiniObject * obj, gpointer data) +{ + GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (data); + GstEvent *event = GST_EVENT (obj); + + if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) { + GST_DEBUG_OBJECT (pbin, "Got newsegment - dropping"); + gst_pad_remove_event_probe (pad, pbin->event_probe_id); + gst_object_unref (pbin); + return FALSE; + } + + return TRUE; +} + +static void +pad_added_cb (GstElement * dbin2, GstPad * pad, gpointer * data) +{ + GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (data); + GstPad *sinkpad = NULL; + + pbin = GST_PULSE_AUDIO_SINK (data); + sinkpad = gst_element_get_static_pad (GST_ELEMENT (pbin->psink), "sink"); + + GST_PULSE_AUDIO_SINK_LOCK (pbin); + if (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK) + GST_ERROR_OBJECT (pbin, "Failed to link decodebin2 to pulsesink"); + else + GST_DEBUG_OBJECT (pbin, "Linked new pad to pulsesink"); + GST_PULSE_AUDIO_SINK_UNLOCK (pbin); + + gst_object_unref (sinkpad); +} + +/* Called with pbin lock held */ +static void +gst_pulse_audio_sink_add_dbin2 (GstPulseAudioSink * pbin) +{ + GstPad *sinkpad = NULL; + + g_assert (pbin->dbin2 == NULL); + + pbin->dbin2 = gst_element_factory_make ("decodebin2", "pulseaudiosink-dbin2"); + + if (!pbin->dbin2) { + post_missing_element_message (pbin, "decodebin2"); + GST_ELEMENT_WARNING (pbin, CORE, MISSING_PLUGIN, + (_("Missing element '%s' - check your GStreamer installation."), + "decodebin2"), ("audio playback might fail")); + goto out; + } + + if (!gst_bin_add (GST_BIN (pbin), pbin->dbin2)) { + GST_ERROR_OBJECT (pbin, "Failed to add decodebin2 to bin"); + goto out; + } + + pbin->pad_added_id = g_signal_connect (pbin->dbin2, "pad-added", + G_CALLBACK (pad_added_cb), pbin); + + if (!gst_element_sync_state_with_parent (pbin->dbin2)) { + GST_ERROR_OBJECT (pbin, "Failed to set decodebin2 to parent state"); + goto out; + } + + /* Trap the newsegment events that we feed the decodebin and discard them */ + sinkpad = gst_element_get_static_pad (GST_ELEMENT (pbin->psink), "sink"); + pbin->event_probe_id = gst_pad_add_event_probe (sinkpad, + G_CALLBACK (dbin2_event_probe), gst_object_ref (pbin)); + gst_object_unref (sinkpad); + sinkpad = NULL; + + GST_DEBUG_OBJECT (pbin, "Distributing running time to decodebin"); + distribute_running_time (pbin->dbin2, &pbin->segment); + + sinkpad = gst_element_get_static_pad (pbin->dbin2, "sink"); + + gst_pulse_audio_sink_update_sinkpad (pbin, sinkpad); + +out: + if (sinkpad) + gst_object_unref (sinkpad); +} + +static void +update_eac3_alignment (GstPulseAudioSink * pbin) +{ + GstCaps *caps = gst_pad_peer_get_caps_reffed (pbin->sinkpad); + GstStructure *st; + + if (!caps) + return; + + st = gst_caps_get_structure (caps, 0); + + if (g_str_equal (gst_structure_get_name (st), "audio/x-eac3")) { + GstStructure *event_st = gst_structure_new ("ac3parse-set-alignment", + "alignment", G_TYPE_STRING, pbin->dbin2 ? "frame" : "iec61937", NULL); + + if (!gst_pad_push_event (pbin->sinkpad, + gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, event_st))) + GST_WARNING_OBJECT (pbin->sinkpad, "Could not update alignment"); + } + + gst_caps_unref (caps); +} + +static void +proxypad_blocked_cb (GstPad * pad, gboolean blocked, gpointer data) +{ + GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (data); + GstCaps *caps; + GstPad *sinkpad = NULL; + + if (!blocked) { + /* Unblocked, don't need to do anything */ + GST_DEBUG_OBJECT (pbin, "unblocked"); + return; + } + + GST_DEBUG_OBJECT (pbin, "blocked"); + + GST_PULSE_AUDIO_SINK_LOCK (pbin); + + if (!pbin->format_lost) { + sinkpad = gst_element_get_static_pad (GST_ELEMENT (pbin->psink), "sink"); + caps = gst_pad_get_caps_reffed (pad); + + if (gst_pad_accept_caps (sinkpad, caps)) { + if (pbin->dbin2) { + GST_DEBUG_OBJECT (pbin, "Removing decodebin"); + gst_pulse_audio_sink_free_dbin2 (pbin); + gst_pulse_audio_sink_update_sinkpad (pbin, sinkpad); + } else + GST_DEBUG_OBJECT (pbin, "Doing nothing"); + + gst_caps_unref (caps); + gst_object_unref (sinkpad); + goto done; + } + /* pulsesink doesn't accept the incoming caps, so add a decodebin + * (potentially after removing the existing once, since decodebin2 can't + * renegotiate). */ + } else { + /* Format lost, proceed to try plugging a decodebin */ + pbin->format_lost = FALSE; + } + + if (pbin->dbin2 != NULL) { + /* decodebin2 doesn't support reconfiguration, so throw this one away and + * create a new one. */ + gst_pulse_audio_sink_free_dbin2 (pbin); + } + + GST_DEBUG_OBJECT (pbin, "Adding decodebin"); + gst_pulse_audio_sink_add_dbin2 (pbin); + +done: + update_eac3_alignment (pbin); + + gst_pad_set_blocked_async_full (pad, FALSE, proxypad_blocked_cb, + gst_object_ref (pbin), (GDestroyNotify) gst_object_unref); + + GST_PULSE_AUDIO_SINK_UNLOCK (pbin); +} + +static gboolean +gst_pulse_audio_sink_src_event (GstPad * pad, GstEvent * event) +{ + GstPulseAudioSink *pbin = NULL; + GstPad *ghostpad = NULL; + gboolean ret = FALSE; + + ghostpad = GST_PAD_CAST (gst_pad_get_parent (pad)); + if (G_UNLIKELY (!ghostpad)) { + GST_WARNING_OBJECT (pad, "Could not get ghostpad"); + goto out; + } + + pbin = GST_PULSE_AUDIO_SINK (gst_pad_get_parent (ghostpad)); + if (G_UNLIKELY (!pbin)) { + GST_WARNING_OBJECT (pad, "Could not get pulseaudiosink"); + goto out; + } + + if (G_UNLIKELY (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_UPSTREAM) && + (gst_event_has_name (event, "pulse-format-lost") || + gst_event_has_name (event, "pulse-sink-changed"))) { + g_return_val_if_fail (pad->mode != GST_ACTIVATE_PULL, FALSE); + + GST_PULSE_AUDIO_SINK_LOCK (pbin); + if (gst_event_has_name (event, "pulse-format-lost")) + pbin->format_lost = TRUE; + + if (!gst_pad_is_blocked (pad)) + gst_pad_set_blocked_async_full (pad, TRUE, proxypad_blocked_cb, + gst_object_ref (pbin), (GDestroyNotify) gst_object_unref); + GST_PULSE_AUDIO_SINK_UNLOCK (pbin); + + ret = TRUE; + } else if (pbin->proxypad_old_eventfunc) { + ret = pbin->proxypad_old_eventfunc (pad, event); + event = NULL; + } + +out: + if (ghostpad) + gst_object_unref (ghostpad); + if (pbin) + gst_object_unref (pbin); + if (event) + gst_event_unref (event); + + return ret; +} + +static gboolean +gst_pulse_audio_sink_sink_event (GstPad * pad, GstEvent * event) +{ + GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (gst_pad_get_parent (pad)); + gboolean ret; + + ret = pbin->sinkpad_old_eventfunc (pad, gst_event_ref (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_NEWSEGMENT: + { + GstFormat format; + gdouble rate, arate; + gint64 start, stop, time; + gboolean update; + + GST_PULSE_AUDIO_SINK_LOCK (pbin); + gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, + &start, &stop, &time); + + GST_DEBUG_OBJECT (pbin, + "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)); + + if (format == GST_FORMAT_TIME) { + /* Store the values for feeding to sub-elements */ + gst_segment_set_newsegment_full (&pbin->segment, update, + rate, arate, format, start, stop, time); + } else { + GST_WARNING_OBJECT (pbin, "Got a non-TIME format segment"); + gst_segment_init (&pbin->segment, GST_FORMAT_TIME); + } + GST_PULSE_AUDIO_SINK_UNLOCK (pbin); + + break; + } + + case GST_EVENT_FLUSH_STOP: + GST_PULSE_AUDIO_SINK_LOCK (pbin); + gst_segment_init (&pbin->segment, GST_FORMAT_UNDEFINED); + GST_PULSE_AUDIO_SINK_UNLOCK (pbin); + break; + + default: + break; + } + + gst_object_unref (pbin); + gst_event_unref (event); + + return ret; +} + +/* The bin's acceptcaps should be exactly equivalent to a pulsesink that is + * connected to a sink that supports all the formats in template caps. This + * means that upstream will have to have everything possibly upto a parser + * plugged and we plugin a decoder whenever required. */ +static gboolean +gst_pulse_audio_sink_sink_acceptcaps (GstPad * pad, GstCaps * caps) +{ + GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (gst_pad_get_parent (pad)); + GstRingBufferSpec spec = { 0 }; + const GstStructure *st; + GstCaps *pad_caps = NULL; + gboolean ret = FALSE; + + pad_caps = gst_pad_get_caps_reffed (pad); + if (!pad_caps || !gst_caps_can_intersect (pad_caps, caps)) + goto out; + + /* If we've not got fixed caps, creating a stream might fail, so let's just + * return from here with default acceptcaps behaviour */ + if (!gst_caps_is_fixed (caps)) + goto out; + + spec.latency_time = GST_BASE_AUDIO_SINK (pbin->psink)->latency_time; + if (!gst_ring_buffer_parse_caps (&spec, caps)) + goto out; + + /* Make sure non-raw input is framed (one frame per buffer) and can be + * payloaded */ + st = gst_caps_get_structure (caps, 0); + + if (!g_str_has_prefix (gst_structure_get_name (st), "audio/x-raw")) { + gboolean framed = FALSE, parsed = FALSE; + + gst_structure_get_boolean (st, "framed", &framed); + gst_structure_get_boolean (st, "parsed", &parsed); + if ((!framed && !parsed) || gst_audio_iec61937_frame_size (&spec) <= 0) + goto out; + } + + ret = TRUE; + +out: + if (pad_caps) + gst_caps_unref (pad_caps); + + gst_object_unref (pbin); + + return ret; +} + +static gboolean +gst_pulse_audio_sink_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (gst_pad_get_parent (pad)); + gboolean ret = TRUE; + + GST_PULSE_AUDIO_SINK_LOCK (pbin); + + if (!gst_pad_is_blocked (pbin->sinkpad)) + gst_pad_set_blocked_async_full (pbin->sink_proxypad, TRUE, + proxypad_blocked_cb, gst_object_ref (pbin), + (GDestroyNotify) gst_object_unref); + + GST_PULSE_AUDIO_SINK_UNLOCK (pbin); + + gst_object_unref (pbin); + + return ret; +} + +static GstStateChangeReturn +gst_pulse_audio_sink_change_state (GstElement * element, + GstStateChange transition) +{ + GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (element); + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + + /* Nothing to do for upward transitions */ + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + GST_PULSE_AUDIO_SINK_LOCK (pbin); + if (gst_pad_is_blocked (pbin->sinkpad)) { + gst_pad_set_blocked_async_full (pbin->sink_proxypad, FALSE, + proxypad_blocked_cb, gst_object_ref (pbin), + (GDestroyNotify) gst_object_unref); + } + GST_PULSE_AUDIO_SINK_UNLOCK (pbin); + break; + + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret != GST_STATE_CHANGE_SUCCESS) { + GST_DEBUG_OBJECT (pbin, "Base class returned %d on state change", ret); + goto out; + } + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + GST_PULSE_AUDIO_SINK_LOCK (pbin); + gst_segment_init (&pbin->segment, GST_FORMAT_UNDEFINED); + + if (pbin->dbin2) { + GstPad *pad = gst_element_get_static_pad (GST_ELEMENT (pbin->psink), + "sink"); + + gst_pulse_audio_sink_free_dbin2 (pbin); + gst_pulse_audio_sink_update_sinkpad (pbin, pad); + + gst_object_unref (pad); + + } + GST_PULSE_AUDIO_SINK_UNLOCK (pbin); + + break; + + default: + break; + } + +out: + return ret; +} + +#endif /* HAVE_PULSE_1_0 */ diff --git a/ext/pulse/pulsesink.c b/ext/pulse/pulsesink.c index 51f60beb88..33dd6763ea 100644 --- a/ext/pulse/pulsesink.c +++ b/ext/pulse/pulsesink.c @@ -420,6 +420,27 @@ gst_pulsering_context_subscribe_cb (pa_context * c, if (idx != pa_stream_get_index (pbuf->stream)) continue; +#ifdef HAVE_PULSE_1_0 + if (psink->device && pa_format_info_is_pcm (pbuf->format) && + !g_str_equal (psink->device, + pa_stream_get_device_name (pbuf->stream))) { + /* Underlying sink changed. And this is not a passthrough stream. Let's + * see if someone upstream wants to try to renegotiate. */ + GstEvent *renego; + + g_free (psink->device); + psink->device = g_strdup (pa_stream_get_device_name (pbuf->stream)); + + GST_INFO_OBJECT (psink, "emitting sink-changed"); + + renego = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, + gst_structure_new ("pulse-sink-changed", NULL)); + + if (!gst_pad_push_event (GST_BASE_SINK (psink)->sinkpad, renego)) + GST_DEBUG_OBJECT (psink, "Emitted sink-changed - nobody was listening"); + } +#endif + /* Actually this event is also triggered when other properties of * the stream change that are unrelated to the volume. However it is * probably cheaper to signal the change here and check for the @@ -1719,12 +1740,6 @@ static GstStateChangeReturn gst_pulsesink_change_state (GstElement * element, static void gst_pulsesink_init_interfaces (GType type); -#if (G_BYTE_ORDER == G_LITTLE_ENDIAN) -# define ENDIANNESS "LITTLE_ENDIAN, BIG_ENDIAN" -#else -# define ENDIANNESS "BIG_ENDIAN, LITTLE_ENDIAN" -#endif - GST_IMPLEMENT_PULSEPROBE_METHODS (GstPulseSink, gst_pulsesink); #define _do_init(type) \ @@ -1784,57 +1799,7 @@ gst_pulsesink_base_init (gpointer g_class) static GstStaticPadTemplate pad_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-raw-int, " - "endianness = (int) { " ENDIANNESS " }, " - "signed = (boolean) TRUE, " - "width = (int) 16, " - "depth = (int) 16, " - "rate = (int) [ 1, MAX ], " - "channels = (int) [ 1, 32 ];" - "audio/x-raw-float, " - "endianness = (int) { " ENDIANNESS " }, " - "width = (int) 32, " - "rate = (int) [ 1, MAX ], " - "channels = (int) [ 1, 32 ];" - "audio/x-raw-int, " - "endianness = (int) { " ENDIANNESS " }, " - "signed = (boolean) TRUE, " - "width = (int) 32, " - "depth = (int) 32, " - "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 32 ];" - "audio/x-raw-int, " - "endianness = (int) { " ENDIANNESS " }, " - "signed = (boolean) TRUE, " - "width = (int) 24, " - "depth = (int) 24, " - "rate = (int) [ 1, MAX ], " - "channels = (int) [ 1, 32 ];" - "audio/x-raw-int, " - "endianness = (int) { " ENDIANNESS " }, " - "signed = (boolean) TRUE, " - "width = (int) 32, " - "depth = (int) 24, " - "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 32 ];" - "audio/x-raw-int, " - "signed = (boolean) FALSE, " - "width = (int) 8, " - "depth = (int) 8, " - "rate = (int) [ 1, MAX ], " - "channels = (int) [ 1, 32 ];" - "audio/x-alaw, " - "rate = (int) [ 1, MAX], " - "channels = (int) [ 1, 32 ];" - "audio/x-mulaw, " - "rate = (int) [ 1, MAX], " "channels = (int) [ 1, 32 ];" -#ifdef HAVE_PULSE_1_0 - "audio/x-ac3, framed = (boolean) true;" - "audio/x-eac3, framed = (boolean) true; " - "audio/x-dts, framed = (boolean) true, " - " block_size = (int) { 512, 1024, 2048 }; " - "audio/mpeg, mpegversion = (int)1, " - " mpegaudioversion = (int) [ 1, 2 ], parsed = (boolean) true; " -#endif - )); + GST_STATIC_CAPS (PULSE_SINK_TEMPLATE_CAPS)); GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); @@ -2075,6 +2040,8 @@ done: } #ifdef HAVE_PULSE_1_0 +/* NOTE: If you're making changes here, see if pulseaudiosink acceptcaps also + * needs to be changed accordingly. */ static gboolean gst_pulsesink_pad_acceptcaps (GstPad * pad, GstCaps * caps) { diff --git a/ext/pulse/pulsesink.h b/ext/pulse/pulsesink.h index ad8831f393..340b4819fb 100644 --- a/ext/pulse/pulsesink.h +++ b/ext/pulse/pulsesink.h @@ -24,6 +24,10 @@ #ifndef __GST_PULSESINK_H__ #define __GST_PULSESINK_H__ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include @@ -88,6 +92,91 @@ struct _GstPulseSinkClass GType gst_pulsesink_get_type (void); +#if (G_BYTE_ORDER == G_LITTLE_ENDIAN) +# define ENDIANNESS "LITTLE_ENDIAN, BIG_ENDIAN" +#else +# define ENDIANNESS "BIG_ENDIAN, LITTLE_ENDIAN" +#endif + +#define _PULSE_SINK_CAPS_COMMON \ + "audio/x-raw-int, " \ + "endianness = (int) { " ENDIANNESS " }, " \ + "signed = (boolean) TRUE, " \ + "width = (int) 16, " \ + "depth = (int) 16, " \ + "rate = (int) [ 1, MAX ], " \ + "channels = (int) [ 1, 32 ];" \ + "audio/x-raw-float, " \ + "endianness = (int) { " ENDIANNESS " }, " \ + "width = (int) 32, " \ + "rate = (int) [ 1, MAX ], " \ + "channels = (int) [ 1, 32 ];" \ + "audio/x-raw-int, " \ + "endianness = (int) { " ENDIANNESS " }, " \ + "signed = (boolean) TRUE, " \ + "width = (int) 32, " \ + "depth = (int) 32, " \ + "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 32 ];" \ + "audio/x-raw-int, " \ + "signed = (boolean) FALSE, " \ + "width = (int) 8, " \ + "depth = (int) 8, " \ + "rate = (int) [ 1, MAX ], " \ + "channels = (int) [ 1, 32 ];" \ + "audio/x-alaw, " \ + "rate = (int) [ 1, MAX], " \ + "channels = (int) [ 1, 32 ];" \ + "audio/x-mulaw, " \ + "rate = (int) [ 1, MAX], " "channels = (int) [ 1, 32 ];" \ + "audio/x-raw-int, " \ + "endianness = (int) { " ENDIANNESS " }, " \ + "signed = (boolean) TRUE, " \ + "width = (int) 24, " \ + "depth = (int) 24, " \ + "rate = (int) [ 1, MAX ], " \ + "channels = (int) [ 1, 32 ];" \ + "audio/x-raw-int, " \ + "endianness = (int) { " ENDIANNESS " }, " \ + "signed = (boolean) TRUE, " \ + "width = (int) 32, " \ + "depth = (int) 24, " \ + "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 32 ];" + +#ifdef HAVE_PULSE_1_0 +#define _PULSE_SINK_CAPS_1_0 \ + "audio/x-ac3, framed = (boolean) true;" \ + "audio/x-eac3, framed = (boolean) true; " \ + "audio/x-dts, framed = (boolean) true, " \ + "block-size = (int) { 512, 1024, 2048 }; " \ + "audio/mpeg, mpegversion = (int) 1, " \ + "mpegaudioversion = (int) [ 1, 2 ], parsed = (boolean) true;" +#else +#define _PULSE_SINK_CAPS_1_0 "" +#endif + +#define PULSE_SINK_TEMPLATE_CAPS \ + _PULSE_SINK_CAPS_COMMON \ + _PULSE_SINK_CAPS_1_0 + +#ifdef HAVE_PULSE_1_0 + +#define GST_TYPE_PULSE_AUDIO_SINK \ + (gst_pulse_audio_sink_get_type()) +#define GST_PULSE_AUDIO_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PULSE_AUDIO_SINK,GstPulseAudioSink)) +#define GST_PULSE_AUDIO_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PULSE_AUDIO_SINK,GstPulseAudioSinkClass)) +#define GST_IS_PULSE_AUDIO_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PULSE_AUDIO_SINK)) +#define GST_IS_PULSE_AUDIO_SINK_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PULSE_AUDIO_SINK)) +#define GST_PULSE_AUDIO_SINK_CAST(obj) \ + ((GstPulseAudioSink *)(obj)) + +GType gst_pulse_audio_sink_get_type (void); + +#endif /* HAVE_PULSE_1_0 */ + G_END_DECLS #endif /* __GST_PULSESINK_H__ */ diff --git a/ext/pulse/pulseutil.h b/ext/pulse/pulseutil.h index 91c8502e39..4adfeb1ed4 100644 --- a/ext/pulse/pulseutil.h +++ b/ext/pulse/pulseutil.h @@ -22,6 +22,10 @@ #ifndef __GST_PULSEUTIL_H__ #define __GST_PULSEUTIL_H__ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include From 7e4574e9684f17f0071e267327c854e8610893f9 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 19 Sep 2011 09:37:58 +0200 Subject: [PATCH 23/47] speexenc: do not use invalid buffer timestamps --- ext/speex/gstspeexenc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/speex/gstspeexenc.c b/ext/speex/gstspeexenc.c index 6ace2da124..98fb2bbc7a 100644 --- a/ext/speex/gstspeexenc.c +++ b/ext/speex/gstspeexenc.c @@ -1050,6 +1050,7 @@ gst_speex_enc_chain (GstPad * pad, GstBuffer * buf) /* Check if we have a continous stream, if not drop some samples or the buffer or * insert some silence samples */ if (enc->next_ts != GST_CLOCK_TIME_NONE && + GST_BUFFER_TIMESTAMP_IS_VALID (buf) && GST_BUFFER_TIMESTAMP (buf) < enc->next_ts) { guint64 diff = enc->next_ts - GST_BUFFER_TIMESTAMP (buf); guint64 diff_bytes; From 3319737e5c1e9b7e9347e3468ed5ecdcb7d3553d Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 5 Sep 2011 10:11:18 +0100 Subject: [PATCH 24/47] udpsrc: error out when no protocol is specified in the uri It is certainly better than to crash. https://bugzilla.gnome.org/show_bug.cgi?id=658178 --- gst/udp/gstudpnetutils.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/gst/udp/gstudpnetutils.c b/gst/udp/gstudpnetutils.c index f488fcc418..bdbffbceca 100644 --- a/gst/udp/gstudpnetutils.c +++ b/gst/udp/gstudpnetutils.c @@ -374,7 +374,10 @@ gst_udp_parse_uri (const gchar * uristr, GstUDPUri * uri) gchar *location, *location_end; gchar *colptr; + /* consider no protocol to be udp:// */ protocol = gst_uri_get_protocol (uristr); + if (!protocol) + goto no_protocol; if (strcmp (protocol, "udp") != 0) goto wrong_protocol; g_free (protocol); @@ -425,6 +428,11 @@ gst_udp_parse_uri (const gchar * uristr, GstUDPUri * uri) return 0; /* ERRORS */ +no_protocol: + { + GST_ERROR ("error parsing uri %s: no protocol", uristr); + return -1; + } wrong_protocol: { GST_ERROR ("error parsing uri %s: wrong protocol (%s != udp)", uristr, From 056e9188b13644aa1ed81216860c290f6e8ab864 Mon Sep 17 00:00:00 2001 From: Lasse Laukkanen Date: Fri, 16 Sep 2011 16:53:22 +0300 Subject: [PATCH 25/47] isomp4: Fix allowing zero duration tracks https://bugzilla.gnome.org/show_bug.cgi?id=637486 --- gst/isomp4/gstqtmux.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/gst/isomp4/gstqtmux.c b/gst/isomp4/gstqtmux.c index c6f2b4b64a..55b3f6bcf4 100644 --- a/gst/isomp4/gstqtmux.c +++ b/gst/isomp4/gstqtmux.c @@ -1738,15 +1738,25 @@ gst_qt_mux_stop_file (GstQTMux * qtmux) GstCollectData *cdata = (GstCollectData *) walk->data; GstQTPad *qtpad = (GstQTPad *) cdata; - /* send last buffer */ + /* avoid add_buffer complaining if not negotiated + * in which case no buffers either, so skipping */ + if (!qtpad->fourcc) { + GST_DEBUG_OBJECT (qtmux, "Pad %s has never had buffers", + GST_PAD_NAME (qtpad->collect.pad)); + continue; + } + + /* send last buffer; also flushes possibly queued buffers/ts */ GST_DEBUG_OBJECT (qtmux, "Sending the last buffer for pad %s", GST_PAD_NAME (qtpad->collect.pad)); ret = gst_qt_mux_add_buffer (qtmux, qtpad, NULL); - if (ret != GST_FLOW_OK) + if (ret != GST_FLOW_OK) { GST_WARNING_OBJECT (qtmux, "Failed to send last buffer for %s, " "flow return: %s", GST_PAD_NAME (qtpad->collect.pad), gst_flow_get_name (ret)); + } + /* having flushed above, can check for buffers now */ if (!GST_CLOCK_TIME_IS_VALID (qtpad->first_ts)) { GST_DEBUG_OBJECT (qtmux, "Pad %s has no buffers", GST_PAD_NAME (qtpad->collect.pad)); From 3e33a7a09fb46d582e22aaea0865901fa66f4b7c Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Mon, 1 Aug 2011 08:32:24 +0200 Subject: [PATCH 26/47] rtspsrc: configure rtcp interval if provided ... in PLAY response. --- gst/rtsp/gstrtspsrc.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/gst/rtsp/gstrtspsrc.c b/gst/rtsp/gstrtspsrc.c index 731e0266af..8ea7ebca9a 100644 --- a/gst/rtsp/gstrtspsrc.c +++ b/gst/rtsp/gstrtspsrc.c @@ -5955,6 +5955,34 @@ gst_rtspsrc_parse_rtpinfo (GstRTSPSrc * src, gchar * rtpinfo) return TRUE; } +static void +gst_rtspsrc_handle_rtcp_interval (GstRTSPSrc * src, gchar * rtcp) +{ + guint64 interval; + GList *walk; + + interval = strtoul (rtcp, NULL, 10); + GST_DEBUG_OBJECT (src, "rtcp interval: %" G_GUINT64_FORMAT " ms", interval); + + if (!interval) + return; + + interval *= GST_MSECOND; + + for (walk = src->streams; walk; walk = g_list_next (walk)) { + GstRTSPStream *stream = (GstRTSPStream *) walk->data; + + /* already (optionally) retrieved this when configuring manager */ + if (stream->session) { + GObject *rtpsession = stream->session; + + GST_DEBUG_OBJECT (src, "configure rtcp interval in session %p", + rtpsession); + g_object_set (rtpsession, "rtcp-min-interval", interval, NULL); + } + } +} + static gdouble gst_rtspsrc_get_float (const gchar * dstr) { @@ -6163,6 +6191,12 @@ gst_rtspsrc_play (GstRTSPSrc * src, GstSegment * segment, gboolean async) &hval, hval_idx++) == GST_RTSP_OK) gst_rtspsrc_parse_rtpinfo (src, hval); + /* some servers indicate RTCP parameters in PLAY response, + * rather than properly in SDP */ + if (gst_rtsp_message_get_header (&response, GST_RTSP_HDR_RTCP_INTERVAL, + &hval, 0) == GST_RTSP_OK) + gst_rtspsrc_handle_rtcp_interval (src, hval); + gst_rtsp_message_unset (&response); /* early exit when we did aggregate control */ From afd26f0078934e5fc67bb160af86cb0f0b856c48 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Mon, 1 Aug 2011 08:35:01 +0200 Subject: [PATCH 27/47] rtpsession: trigger reconsideration if rtcp interval set --- gst/rtpmanager/rtpsession.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gst/rtpmanager/rtpsession.c b/gst/rtpmanager/rtpsession.c index e1743f749e..757fefe55d 100644 --- a/gst/rtpmanager/rtpsession.c +++ b/gst/rtpmanager/rtpsession.c @@ -664,6 +664,12 @@ rtp_session_set_property (GObject * object, guint prop_id, case PROP_RTCP_MIN_INTERVAL: rtp_stats_set_min_interval (&sess->stats, (gdouble) g_value_get_uint64 (value) / GST_SECOND); + /* trigger reconsideration */ + RTP_SESSION_LOCK (sess); + sess->next_rtcp_check_time = 0; + RTP_SESSION_UNLOCK (sess); + if (sess->callbacks.reconsider) + sess->callbacks.reconsider (sess, sess->reconsider_user_data); break; case PROP_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD: sess->rtcp_immediate_feedback_threshold = g_value_get_uint (value); From f29c2539349546abe596d82d90c66f58626c1f8b Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Mon, 8 Aug 2011 12:09:41 +0200 Subject: [PATCH 28/47] rtpbin: allow configurable rtcp stream syncing interval ... rather than necessarily syncing at each RTCP SR. --- gst/rtpmanager/gstrtpbin.c | 36 ++++++++++++++++++++++++++++++++++++ gst/rtpmanager/gstrtpbin.h | 1 + 2 files changed, 37 insertions(+) diff --git a/gst/rtpmanager/gstrtpbin.c b/gst/rtpmanager/gstrtpbin.c index e8d659ff0c..e9b3164b64 100644 --- a/gst/rtpmanager/gstrtpbin.c +++ b/gst/rtpmanager/gstrtpbin.c @@ -213,6 +213,9 @@ struct _GstRtpBinPrivate gint shutdown; gboolean autoremove; + + /* UNIX (ntp) time of last SR sync used */ + guint64 last_unix; }; /* signals and args */ @@ -245,6 +248,7 @@ enum #define DEFAULT_AUTOREMOVE FALSE #define DEFAULT_BUFFER_MODE RTP_JITTER_BUFFER_MODE_SLAVE #define DEFAULT_USE_PIPELINE_CLOCK FALSE +#define DEFAULT_RTCP_SYNC_INTERVAL 0 enum { @@ -254,6 +258,7 @@ enum PROP_DO_LOST, PROP_IGNORE_PT, PROP_NTP_SYNC, + PROP_RTCP_SYNC_INTERVAL, PROP_AUTOREMOVE, PROP_BUFFER_MODE, PROP_USE_PIPELINE_CLOCK, @@ -1103,6 +1108,16 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len, GST_DEBUG_OBJECT (bin, "client %p min delta %" G_GINT64_FORMAT, client, min); + /* bail out if we adjusted recently enough */ + if (all_sync && (last_unix - bin->priv->last_unix) < + bin->rtcp_sync_interval * GST_MSECOND) { + GST_DEBUG_OBJECT (bin, "discarding RTCP sender packet for sync; " + "previous sender info too recent " + "(previous UNIX %" G_GUINT64_FORMAT ")", bin->priv->last_unix); + return; + } + bin->priv->last_unix = last_unix; + /* calculate offsets for each stream */ for (walk = client->streams; walk; walk = g_slist_next (walk)) { GstRtpBinStream *ostream = (GstRtpBinStream *) walk->data; @@ -1677,6 +1692,19 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) "Synchronize received streams to the NTP clock", DEFAULT_NTP_SYNC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * GstRtpBin::rtcp-sync-interval: + * + * Determines how often to sync streams using RTCP data. + * + * Since: 0.10.31 + */ + g_object_class_install_property (gobject_class, PROP_RTCP_SYNC_INTERVAL, + g_param_spec_uint ("rtcp-sync-interval", "RTCP Sync Interval", + "RTCP SR interval synchronization (ms) (0 = always)", + 0, G_MAXUINT, DEFAULT_RTCP_SYNC_INTERVAL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_rtp_bin_change_state); gstelement_class->request_new_pad = GST_DEBUG_FUNCPTR (gst_rtp_bin_request_new_pad); @@ -1706,6 +1734,7 @@ gst_rtp_bin_init (GstRtpBin * rtpbin, GstRtpBinClass * klass) rtpbin->do_lost = DEFAULT_DO_LOST; rtpbin->ignore_pt = DEFAULT_IGNORE_PT; rtpbin->ntp_sync = DEFAULT_NTP_SYNC; + rtpbin->rtcp_sync_interval = DEFAULT_RTCP_SYNC_INTERVAL; rtpbin->priv->autoremove = DEFAULT_AUTOREMOVE; rtpbin->buffer_mode = DEFAULT_BUFFER_MODE; rtpbin->use_pipeline_clock = DEFAULT_USE_PIPELINE_CLOCK; @@ -1821,6 +1850,9 @@ gst_rtp_bin_set_property (GObject * object, guint prop_id, case PROP_NTP_SYNC: rtpbin->ntp_sync = g_value_get_boolean (value); break; + case PROP_RTCP_SYNC_INTERVAL: + rtpbin->rtcp_sync_interval = g_value_get_uint (value); + break; case PROP_IGNORE_PT: rtpbin->ignore_pt = g_value_get_boolean (value); break; @@ -1883,6 +1915,9 @@ gst_rtp_bin_get_property (GObject * object, guint prop_id, case PROP_NTP_SYNC: g_value_set_boolean (value, rtpbin->ntp_sync); break; + case PROP_RTCP_SYNC_INTERVAL: + g_value_set_uint (value, rtpbin->rtcp_sync_interval); + break; case PROP_AUTOREMOVE: g_value_set_boolean (value, rtpbin->priv->autoremove); break; @@ -2108,6 +2143,7 @@ gst_rtp_bin_change_state (GstElement * element, GstStateChange transition) case GST_STATE_CHANGE_NULL_TO_READY: break; case GST_STATE_CHANGE_READY_TO_PAUSED: + priv->last_unix = 0; GST_LOG_OBJECT (rtpbin, "clearing shutdown flag"); g_atomic_int_set (&priv->shutdown, 0); break; diff --git a/gst/rtpmanager/gstrtpbin.h b/gst/rtpmanager/gstrtpbin.h index 74aaac2453..7cc845c160 100644 --- a/gst/rtpmanager/gstrtpbin.h +++ b/gst/rtpmanager/gstrtpbin.h @@ -50,6 +50,7 @@ struct _GstRtpBin { gboolean do_lost; gboolean ignore_pt; gboolean ntp_sync; + guint rtcp_sync_interval; RTPJitterBufferMode buffer_mode; gboolean buffering; gboolean use_pipeline_clock; From 4b7301e4d13472e111b63945f002db1dbe1ff4fc Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Mon, 8 Aug 2011 12:11:24 +0200 Subject: [PATCH 29/47] rtpjitterbuffer: also provide clock-base to sync signal --- gst/rtpmanager/gstrtpjitterbuffer.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/gst/rtpmanager/gstrtpjitterbuffer.c b/gst/rtpmanager/gstrtpjitterbuffer.c index e11869985e..3a67d5eee3 100644 --- a/gst/rtpmanager/gstrtpjitterbuffer.c +++ b/gst/rtpmanager/gstrtpjitterbuffer.c @@ -1970,6 +1970,7 @@ gst_rtp_jitter_buffer_chain_rtcp (GstPad * pad, GstBuffer * buffer) guint64 ext_rtptime, diff; guint32 rtptime; gboolean drop = FALSE; + guint64 clock_base; jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad)); @@ -2003,9 +2004,12 @@ gst_rtp_jitter_buffer_chain_rtcp (GstPad * pad, GstBuffer * buffer) rtp_jitter_buffer_get_sync (priv->jbuf, &base_rtptime, &base_time, &clock_rate, &last_rtptime); + clock_base = priv->clock_base; + GST_DEBUG_OBJECT (jitterbuffer, "ext SR %" G_GUINT64_FORMAT ", base %" - G_GUINT64_FORMAT ", clock-rate %" G_GUINT32_FORMAT, - ext_rtptime, base_rtptime, clock_rate); + G_GUINT64_FORMAT ", clock-rate %" G_GUINT32_FORMAT + ", clock-base %" G_GUINT64_FORMAT, + ext_rtptime, base_rtptime, clock_rate, clock_base); if (base_rtptime == -1 || clock_rate == -1 || base_time == -1) { GST_DEBUG_OBJECT (jitterbuffer, "dropping, no RTP values"); @@ -2040,6 +2044,7 @@ gst_rtp_jitter_buffer_chain_rtcp (GstPad * pad, GstBuffer * buffer) "base-rtptime", G_TYPE_UINT64, base_rtptime, "base-time", G_TYPE_UINT64, base_time, "clock-rate", G_TYPE_UINT, clock_rate, + "clock-base", G_TYPE_UINT64, clock_base, "sr-ext-rtptime", G_TYPE_UINT64, ext_rtptime, "sr-buffer", GST_TYPE_BUFFER, buffer, NULL); From 9c95072048feee72fdd00e05b5b2d77f939ce9d5 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Mon, 8 Aug 2011 12:15:20 +0200 Subject: [PATCH 30/47] rtpbin: alternative inter-stream syncing methods ... at least if not syncing to NPT time: * either sync using RTCP SR data (as currently) * only perform the above once using initial RTCP SR packets * discard RTCP and sync by equating provided stream's clock-base rtptime, as provided by jitterbuffer (typically obtained from RTP-Info in RTSP). --- gst/rtpmanager/gstrtpbin.c | 171 ++++++++++++++++++++++++++++++++++--- gst/rtpmanager/gstrtpbin.h | 1 + 2 files changed, 161 insertions(+), 11 deletions(-) diff --git a/gst/rtpmanager/gstrtpbin.c b/gst/rtpmanager/gstrtpbin.c index e9b3164b64..c3ca387f9d 100644 --- a/gst/rtpmanager/gstrtpbin.c +++ b/gst/rtpmanager/gstrtpbin.c @@ -248,6 +248,7 @@ enum #define DEFAULT_AUTOREMOVE FALSE #define DEFAULT_BUFFER_MODE RTP_JITTER_BUFFER_MODE_SLAVE #define DEFAULT_USE_PIPELINE_CLOCK FALSE +#define DEFAULT_RTCP_SYNC GST_RTP_BIN_RTCP_SYNC_ALWAYS #define DEFAULT_RTCP_SYNC_INTERVAL 0 enum @@ -258,6 +259,7 @@ enum PROP_DO_LOST, PROP_IGNORE_PT, PROP_NTP_SYNC, + PROP_RTCP_SYNC, PROP_RTCP_SYNC_INTERVAL, PROP_AUTOREMOVE, PROP_BUFFER_MODE, @@ -265,6 +267,31 @@ enum PROP_LAST }; +enum +{ + GST_RTP_BIN_RTCP_SYNC_ALWAYS, + GST_RTP_BIN_RTCP_SYNC_INITIAL, + GST_RTP_BIN_RTCP_SYNC_RTP +}; + +#define GST_RTP_BIN_RTCP_SYNC_TYPE (gst_rtp_bin_rtcp_sync_get_type()) +static GType +gst_rtp_bin_rtcp_sync_get_type (void) +{ + static GType rtcp_sync_type = 0; + static const GEnumValue rtcp_sync_types[] = { + {GST_RTP_BIN_RTCP_SYNC_ALWAYS, "always", "always"}, + {GST_RTP_BIN_RTCP_SYNC_INITIAL, "initial", "initial"}, + {GST_RTP_BIN_RTCP_SYNC_RTP, "rtp-info", "rtp-info"}, + {0, NULL, NULL}, + }; + + if (!rtcp_sync_type) { + rtcp_sync_type = g_enum_register_static ("GstRTCPSync", rtcp_sync_types); + } + return rtcp_sync_type; +} + /* helper objects */ typedef struct _GstRtpBinSession GstRtpBinSession; typedef struct _GstRtpBinStream GstRtpBinStream; @@ -315,6 +342,9 @@ struct _GstRtpBinStream gboolean have_sync; /* mapping to local RTP and NTP time */ gint64 rt_delta; + gint64 rtp_delta; + /* base rtptime in gst time */ + gint64 clock_base; }; #define GST_RTP_SESSION_LOCK(sess) g_mutex_lock ((sess)->lock) @@ -780,6 +810,8 @@ gst_rtp_bin_reset_sync (GstRtpBin * rtpbin) * lip-sync */ stream->have_sync = FALSE; stream->rt_delta = 0; + stream->rtp_delta = 0; + stream->clock_base = -100 * GST_SECOND; } } GST_RTP_BIN_UNLOCK (rtpbin); @@ -984,7 +1016,8 @@ stream_set_ts_offset (GstRtpBin * bin, GstRtpBinStream * stream, static void gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len, guint8 * data, guint64 ntptime, guint64 last_extrtptime, - guint64 base_rtptime, guint64 base_time, guint clock_rate) + guint64 base_rtptime, guint64 base_time, guint clock_rate, + gint64 rtp_clock_base) { GstRtpBinClient *client; gboolean created; @@ -1027,8 +1060,9 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len, GST_DEBUG_OBJECT (bin, "base %" G_GUINT64_FORMAT ", extrtptime %" G_GUINT64_FORMAT - ", local RTP %" G_GUINT64_FORMAT ", clock-rate %d", base_rtptime, - last_extrtptime, local_rtp, clock_rate); + ", local RTP %" G_GUINT64_FORMAT ", clock-rate %d, " + "clock-base %" G_GINT64_FORMAT, base_rtptime, + last_extrtptime, local_rtp, clock_rate, rtp_clock_base); /* calculate local RTP time in gstreamer timestamp, we essentially perform the * same conversion that a jitterbuffer would use to convert an rtp timestamp @@ -1075,8 +1109,10 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len, stream->rt_delta = rtdiff - ntpdiff; stream_set_ts_offset (bin, stream, stream->rt_delta); - } else if (client->nstreams > 1) { - gint64 min; + } else { + gint64 min, rtp_min, clock_base = stream->clock_base; + gboolean all_sync, use_rtp; + gboolean rtcp_sync = g_atomic_int_get (&bin->rtcp_sync); /* calculate delta between server and receiver. last_unix is created by * converting the ntptime in the last SR packet to a gstreamer timestamp. This @@ -1094,19 +1130,104 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len, * latencies). * The stream that has the smallest diff is selected as the reference stream, * all other streams will have a positive offset to this difference. */ - min = G_MAXINT64; + + /* some alternative setting allow ignoring RTCP as much as possible, + * for servers generating bogus ntp timeline */ + min = rtp_min = G_MAXINT64; + use_rtp = FALSE; + if (rtcp_sync == GST_RTP_BIN_RTCP_SYNC_RTP) { + guint64 ext_base; + + use_rtp = TRUE; + /* signed version for convienience */ + clock_base = base_rtptime; + /* deal with possible wrap-around */ + ext_base = base_rtptime; + rtp_clock_base = gst_rtp_buffer_ext_timestamp (&ext_base, rtp_clock_base); + /* sanity check; base rtp and provided clock_base should be close */ + if (rtp_clock_base >= clock_base) { + if (rtp_clock_base - clock_base < 10 * clock_rate) { + rtp_clock_base = base_time + + gst_util_uint64_scale_int (rtp_clock_base - clock_base, + GST_SECOND, clock_rate); + } else { + use_rtp = FALSE; + } + } else { + if (clock_base - rtp_clock_base < 10 * clock_rate) { + rtp_clock_base = base_time - + gst_util_uint64_scale_int (clock_base - rtp_clock_base, + GST_SECOND, clock_rate); + } else { + use_rtp = FALSE; + } + } + /* warn and bail for clarity out if no sane values */ + if (!use_rtp) { + GST_WARNING_OBJECT (bin, "unable to sync to provided rtptime"); + return; + } + /* store to track changes */ + clock_base = rtp_clock_base; + /* generate a fake as before, + * now equating rtptime obtained from RTP-Info, + * where the large time represent the otherwise irrelevant npt/ntp time */ + stream->rtp_delta = (GST_SECOND << 28) - rtp_clock_base; + } + for (walk = client->streams; walk; walk = g_slist_next (walk)) { GstRtpBinStream *ostream = (GstRtpBinStream *) walk->data; - if (!ostream->have_sync) + if (!ostream->have_sync) { + all_sync = FALSE; continue; + } + + /* change in current stream's base from previously init'ed value + * leads to reset of all stream's base */ + if (stream != ostream && stream->clock_base >= 0 && + (stream->clock_base != clock_base)) { + GST_DEBUG_OBJECT (bin, "reset upon clock base change"); + ostream->clock_base = -100 * GST_SECOND; + ostream->rtp_delta = 0; + } if (ostream->rt_delta < min) min = ostream->rt_delta; + if (ostream->rtp_delta < rtp_min) + rtp_min = ostream->rtp_delta; } - GST_DEBUG_OBJECT (bin, "client %p min delta %" G_GINT64_FORMAT, client, - min); + /* arrange to re-sync for each stream upon significant change, + * e.g. post-seek */ + all_sync = (stream->clock_base == clock_base); + stream->clock_base = clock_base; + + /* may need init performed above later on, but nothing more to do now */ + if (client->nstreams <= 1) + return; + + GST_DEBUG_OBJECT (bin, "client %p min delta %" G_GINT64_FORMAT + " all sync %d", client, min, all_sync); + GST_DEBUG_OBJECT (bin, "rtcp sync mode %d, use_rtp %d", rtcp_sync, use_rtp); + + switch (rtcp_sync) { + case GST_RTP_BIN_RTCP_SYNC_RTP: + if (!use_rtp) + break; + GST_DEBUG_OBJECT (bin, "using rtp generated reports; " + "client %p min rtp delta %" G_GINT64_FORMAT, client, rtp_min); + /* fall-through */ + case GST_RTP_BIN_RTCP_SYNC_INITIAL: + /* if all have been synced already, do not bother further */ + if (all_sync) { + GST_DEBUG_OBJECT (bin, "all streams already synced; done"); + return; + } + break; + default: + break; + } /* bail out if we adjusted recently enough */ if (all_sync && (last_unix - bin->priv->last_unix) < @@ -1131,7 +1252,10 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len, /* calculate offset to our reference stream, this should always give a * positive number. */ - ts_offset = ostream->rt_delta - min; + if (use_rtp) + ts_offset = ostream->rtp_delta - rtp_min; + else + ts_offset = ostream->rt_delta - min; stream_set_ts_offset (bin, ostream, ts_offset); } @@ -1164,6 +1288,7 @@ gst_rtp_bin_handle_sync (GstElement * jitterbuffer, GstStructure * s, guint64 base_rtptime; guint64 base_time; guint clock_rate; + guint64 clock_base; guint64 extrtptime; GstBuffer *buffer; @@ -1179,6 +1304,7 @@ gst_rtp_bin_handle_sync (GstElement * jitterbuffer, GstStructure * s, g_value_get_uint64 (gst_structure_get_value (s, "base-rtptime")); base_time = g_value_get_uint64 (gst_structure_get_value (s, "base-time")); clock_rate = g_value_get_uint (gst_structure_get_value (s, "clock-rate")); + clock_base = g_value_get_uint64 (gst_structure_get_value (s, "clock-base")); extrtptime = g_value_get_uint64 (gst_structure_get_value (s, "sr-ext-rtptime")); buffer = gst_value_get_buffer (gst_structure_get_value (s, "sr-buffer")); @@ -1231,7 +1357,8 @@ gst_rtp_bin_handle_sync (GstElement * jitterbuffer, GstStructure * s, GST_RTP_BIN_LOCK (bin); /* associate the stream to CNAME */ gst_rtp_bin_associate (bin, stream, len, data, - ntptime, extrtptime, base_rtptime, base_time, clock_rate); + ntptime, extrtptime, base_rtptime, base_time, clock_rate, + clock_base); GST_RTP_BIN_UNLOCK (bin); } } @@ -1275,7 +1402,9 @@ create_stream (GstRtpBinSession * session, guint32 ssrc) stream->have_sync = FALSE; stream->rt_delta = 0; + stream->rtp_delta = 0; stream->percent = 100; + stream->clock_base = -100 * GST_SECOND; session->streams = g_slist_prepend (session->streams, stream); /* provide clock_rate to the jitterbuffer when needed */ @@ -1692,6 +1821,19 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass) "Synchronize received streams to the NTP clock", DEFAULT_NTP_SYNC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * GstRtpBin::rtcp-sync: + * + * If not synchronizing (directly) to the NTP clock, determines how to sync + * the various streams. + * + * Since: 0.10.31 + */ + g_object_class_install_property (gobject_class, PROP_RTCP_SYNC, + g_param_spec_enum ("rtcp-sync", "RTCP Sync", + "Use of RTCP SR in synchronization", GST_RTP_BIN_RTCP_SYNC_TYPE, + DEFAULT_RTCP_SYNC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** * GstRtpBin::rtcp-sync-interval: * @@ -1734,6 +1876,7 @@ gst_rtp_bin_init (GstRtpBin * rtpbin, GstRtpBinClass * klass) rtpbin->do_lost = DEFAULT_DO_LOST; rtpbin->ignore_pt = DEFAULT_IGNORE_PT; rtpbin->ntp_sync = DEFAULT_NTP_SYNC; + rtpbin->rtcp_sync = DEFAULT_RTCP_SYNC; rtpbin->rtcp_sync_interval = DEFAULT_RTCP_SYNC_INTERVAL; rtpbin->priv->autoremove = DEFAULT_AUTOREMOVE; rtpbin->buffer_mode = DEFAULT_BUFFER_MODE; @@ -1850,6 +1993,9 @@ gst_rtp_bin_set_property (GObject * object, guint prop_id, case PROP_NTP_SYNC: rtpbin->ntp_sync = g_value_get_boolean (value); break; + case PROP_RTCP_SYNC: + g_atomic_int_set (&rtpbin->rtcp_sync, g_value_get_enum (value)); + break; case PROP_RTCP_SYNC_INTERVAL: rtpbin->rtcp_sync_interval = g_value_get_uint (value); break; @@ -1915,6 +2061,9 @@ gst_rtp_bin_get_property (GObject * object, guint prop_id, case PROP_NTP_SYNC: g_value_set_boolean (value, rtpbin->ntp_sync); break; + case PROP_RTCP_SYNC: + g_value_set_enum (value, g_atomic_int_get (&rtpbin->rtcp_sync)); + break; case PROP_RTCP_SYNC_INTERVAL: g_value_set_uint (value, rtpbin->rtcp_sync_interval); break; diff --git a/gst/rtpmanager/gstrtpbin.h b/gst/rtpmanager/gstrtpbin.h index 7cc845c160..a9157871d3 100644 --- a/gst/rtpmanager/gstrtpbin.h +++ b/gst/rtpmanager/gstrtpbin.h @@ -50,6 +50,7 @@ struct _GstRtpBin { gboolean do_lost; gboolean ignore_pt; gboolean ntp_sync; + gint rtcp_sync; guint rtcp_sync_interval; RTPJitterBufferMode buffer_mode; gboolean buffering; From 8599801cae088b536dd1d366682c08601d708109 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Mon, 8 Aug 2011 12:48:50 +0200 Subject: [PATCH 31/47] rtspsrc: switch to rtp time based syncing when guessed appropriate --- gst/rtsp/gstrtspsrc.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/gst/rtsp/gstrtspsrc.c b/gst/rtsp/gstrtspsrc.c index 8ea7ebca9a..78dd882ae1 100644 --- a/gst/rtsp/gstrtspsrc.c +++ b/gst/rtsp/gstrtspsrc.c @@ -128,6 +128,13 @@ enum LAST_SIGNAL }; +enum _GstRtspSrcRtcpSyncMode +{ + RTCP_SYNC_ALWAYS, + RTCP_SYNC_INITIAL, + RTCP_SYNC_RTP +}; + enum _GstRtspSrcBufferMode { BUFFER_MODE_NONE, @@ -5981,6 +5988,19 @@ gst_rtspsrc_handle_rtcp_interval (GstRTSPSrc * src, gchar * rtcp) g_object_set (rtpsession, "rtcp-min-interval", interval, NULL); } } + + /* now it happens that (Xenon) server sending this may also provide bogus + * RTCP SR sync data (i.e. with quite some jitter), so never mind those + * and just use RTP-Info to sync */ + if (src->manager) { + GObjectClass *klass; + + klass = G_OBJECT_GET_CLASS (G_OBJECT (src->manager)); + if (g_object_class_find_property (klass, "rtcp-sync")) { + GST_DEBUG_OBJECT (src, "configuring rtp sync method"); + g_object_set (src->manager, "rtcp-sync", RTCP_SYNC_RTP, NULL); + } + } } static gdouble From 1e17e10f75e312b640cb32d715290c6b59f0c8a8 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Thu, 18 Aug 2011 14:12:21 +0200 Subject: [PATCH 32/47] rtpjitterbuffer: check some more for possible rtp timestamp discontinuity ... when operating in non slave mode, and reset if detected. This should avoid some (large) bogus outgoing timestamp due to jumps in rtp time, as result of PAUSE/PLAY or seek or ... --- gst/rtpmanager/rtpjitterbuffer.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/gst/rtpmanager/rtpjitterbuffer.c b/gst/rtpmanager/rtpjitterbuffer.c index f6382fc614..f1c8e49fe3 100644 --- a/gst/rtpmanager/rtpjitterbuffer.c +++ b/gst/rtpmanager/rtpjitterbuffer.c @@ -634,6 +634,22 @@ rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, GstBuffer * buf, } rtptime = gst_rtp_buffer_get_timestamp (buf); + /* rtp time jumps are checked for during skew calculation, but bypassed + * in other mode, so mind those here and reset jb if needed */ + if (jbuf->mode != RTP_JITTER_BUFFER_MODE_SLAVE && + jbuf->base_time != -1 && jbuf->last_rtptime != -1) { + GstClockTime ext_rtptime = jbuf->ext_rtptime; + + ext_rtptime = gst_rtp_buffer_ext_timestamp (&ext_rtptime, rtptime); + if (ext_rtptime > jbuf->last_rtptime + 3 * clock_rate || + ext_rtptime + 3 * clock_rate < jbuf->last_rtptime) { + /* reset even if we don't have valid incoming time; + * still better than producing possibly very bogus output timestamp */ + GST_WARNING ("rtp delta too big, reset skew"); + rtp_jitter_buffer_reset_skew (jbuf); + } + } + switch (jbuf->mode) { case RTP_JITTER_BUFFER_MODE_NONE: case RTP_JITTER_BUFFER_MODE_BUFFER: From 915db2602903194984f2c9aa48985946bbca68f5 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Thu, 1 Sep 2011 14:47:48 +0200 Subject: [PATCH 33/47] rtpjitterbuffer: only reset skew on gap if input ts available --- gst/rtpmanager/rtpjitterbuffer.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/gst/rtpmanager/rtpjitterbuffer.c b/gst/rtpmanager/rtpjitterbuffer.c index f1c8e49fe3..5db5da49f1 100644 --- a/gst/rtpmanager/rtpjitterbuffer.c +++ b/gst/rtpmanager/rtpjitterbuffer.c @@ -635,8 +635,12 @@ rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, GstBuffer * buf, rtptime = gst_rtp_buffer_get_timestamp (buf); /* rtp time jumps are checked for during skew calculation, but bypassed - * in other mode, so mind those here and reset jb if needed */ - if (jbuf->mode != RTP_JITTER_BUFFER_MODE_SLAVE && + * in other mode, so mind those here and reset jb if needed. + * Only reset if valid input time, which is likely for UDP input + * where we expect this might happen due to async thread effects + * (in seek and state change cycles), but not so much for TCP input */ + if (GST_CLOCK_TIME_IS_VALID (time) && + jbuf->mode != RTP_JITTER_BUFFER_MODE_SLAVE && jbuf->base_time != -1 && jbuf->last_rtptime != -1) { GstClockTime ext_rtptime = jbuf->ext_rtptime; From 81fc784163657e08ab564f86cf176189c010e52b Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Sun, 21 Aug 2011 21:58:38 +0200 Subject: [PATCH 34/47] rtspsrc: do not set elements to PLAYING when doing seek in PAUSED --- gst/rtsp/gstrtspsrc.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/gst/rtsp/gstrtspsrc.c b/gst/rtsp/gstrtspsrc.c index 78dd882ae1..0a682d8839 100644 --- a/gst/rtsp/gstrtspsrc.c +++ b/gst/rtsp/gstrtspsrc.c @@ -1680,7 +1680,7 @@ cleanup: } static void -gst_rtspsrc_flush (GstRTSPSrc * src, gboolean flush) +gst_rtspsrc_flush (GstRTSPSrc * src, gboolean flush, gboolean playing) { GstEvent *event; gint cmd, i; @@ -1696,9 +1696,12 @@ gst_rtspsrc_flush (GstRTSPSrc * src, gboolean flush) state = GST_STATE_PAUSED; } else { event = gst_event_new_flush_stop (); - GST_DEBUG_OBJECT (src, "stop flush"); + GST_DEBUG_OBJECT (src, "stop flush; playing %d", playing); cmd = CMD_LOOP; - state = GST_STATE_PLAYING; + if (playing) + state = GST_STATE_PLAYING; + else + state = GST_STATE_PAUSED; clock = gst_element_get_clock (GST_ELEMENT_CAST (src)); if (clock) { base_time = gst_clock_get_time (clock); @@ -1847,7 +1850,7 @@ gst_rtspsrc_perform_seek (GstRTSPSrc * src, GstEvent * event) * blocking in preroll). */ if (flush) { GST_DEBUG_OBJECT (src, "starting flush"); - gst_rtspsrc_flush (src, TRUE); + gst_rtspsrc_flush (src, TRUE, FALSE); } else { if (src->task) { gst_task_pause (src->task); @@ -1896,7 +1899,7 @@ gst_rtspsrc_perform_seek (GstRTSPSrc * src, GstEvent * event) if (flush) { /* if we started flush, we stop now */ GST_DEBUG_OBJECT (src, "stopping flush"); - gst_rtspsrc_flush (src, FALSE); + gst_rtspsrc_flush (src, FALSE, playing); } else if (src->running) { /* re-engage loop */ gst_rtspsrc_loop_send_cmd (src, CMD_LOOP, FALSE); From adfe7d0467abccc64778e497e9416367e48b17f4 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Wed, 24 Aug 2011 14:34:23 +0200 Subject: [PATCH 35/47] rtpjitterbuffer: some more reset when clearing pt map ... which in particular caters for some more reset following a possible rtsp PLAY. --- gst/rtpmanager/gstrtpjitterbuffer.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gst/rtpmanager/gstrtpjitterbuffer.c b/gst/rtpmanager/gstrtpjitterbuffer.c index 3a67d5eee3..7a76853253 100644 --- a/gst/rtpmanager/gstrtpjitterbuffer.c +++ b/gst/rtpmanager/gstrtpjitterbuffer.c @@ -671,6 +671,11 @@ gst_rtp_jitter_buffer_clear_pt_map (GstRtpJitterBuffer * jitterbuffer) JBUF_LOCK (priv); priv->clock_rate = -1; + /* do not clear current content, but refresh state for new arrival */ + GST_DEBUG_OBJECT (jitterbuffer, "reset jitterbuffer"); + rtp_jitter_buffer_reset_skew (priv->jbuf); + priv->last_popped_seqnum = -1; + priv->next_seqnum = -1; JBUF_UNLOCK (priv); } From 77ebd33991df40c239827f5666f2bbfc558518c4 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Wed, 24 Aug 2011 14:37:52 +0200 Subject: [PATCH 36/47] rtpjitterbuffer/rtpbin: relax dropping rtcp packets ... to at least having it trigger a/v synchronization, possibly without using provided values which are still not considered sane (as previously dropped). --- gst/rtpmanager/gstrtpbin.c | 13 +++++++++++++ gst/rtpmanager/gstrtpjitterbuffer.c | 8 ++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/gst/rtpmanager/gstrtpbin.c b/gst/rtpmanager/gstrtpbin.c index c3ca387f9d..beac56ced2 100644 --- a/gst/rtpmanager/gstrtpbin.c +++ b/gst/rtpmanager/gstrtpbin.c @@ -1052,6 +1052,19 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len, stream->ssrc, client, client->cname); } + if (!GST_CLOCK_TIME_IS_VALID (last_extrtptime)) { + GST_DEBUG_OBJECT (bin, "invalidated sync data"); + if (bin->rtcp_sync == GST_RTP_BIN_RTCP_SYNC_RTP) { + /* we don't need that data, so carry on, + * but make some values look saner */ + last_extrtptime = base_rtptime; + } else { + /* nothing we can do with this data in this case */ + GST_DEBUG_OBJECT (bin, "bailing out"); + return; + } + } + /* Take the extended rtptime we found in the SR packet and map it to the * local rtptime. The local rtp time is used to construct timestamps on the * buffers so we will calculate what running_time corresponds to the RTP diff --git a/gst/rtpmanager/gstrtpjitterbuffer.c b/gst/rtpmanager/gstrtpjitterbuffer.c index 7a76853253..3e1b546bfc 100644 --- a/gst/rtpmanager/gstrtpjitterbuffer.c +++ b/gst/rtpmanager/gstrtpjitterbuffer.c @@ -2032,8 +2032,12 @@ gst_rtp_jitter_buffer_chain_rtcp (GstPad * pad, GstBuffer * buffer) diff = ext_rtptime - last_rtptime; /* if bigger than 1 second, we drop it */ if (diff > clock_rate) { - GST_DEBUG_OBJECT (jitterbuffer, "dropping, too far ahead"); - drop = TRUE; + GST_DEBUG_OBJECT (jitterbuffer, "too far ahead"); + /* should drop this, but some RTSP servers end up with bogus + * way too ahead RTCP packet when repeated PAUSE/PLAY, + * so still trigger rptbin sync but invalidate RTCP data + * (sync might use other methods) */ + ext_rtptime = -1; } GST_DEBUG_OBJECT (jitterbuffer, "ext last %" G_GUINT64_FORMAT ", diff %" G_GUINT64_FORMAT, last_rtptime, diff); From f65d4c83000788c2621fc5bced0d65e000924751 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Thu, 25 Aug 2011 12:40:52 +0200 Subject: [PATCH 37/47] rtpsession: avoid timing out source too quickly ... following a PAUSE/PLAY cycle, particularly applicable when operating with a short RTCP interval (possibly forced so server-side). --- gst/rtpmanager/gstrtpsession.c | 4 ++++ gst/rtpmanager/rtpsession.c | 18 +++++++++++------- gst/rtpmanager/rtpsession.h | 1 + 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/gst/rtpmanager/gstrtpsession.c b/gst/rtpmanager/gstrtpsession.c index ebeb3fd9bc..030a9e4bc1 100644 --- a/gst/rtpmanager/gstrtpsession.c +++ b/gst/rtpmanager/gstrtpsession.c @@ -837,6 +837,10 @@ rtcp_thread (GstRtpSession * rtpsession) session = rtpsession->priv->session; + GST_DEBUG_OBJECT (rtpsession, "starting at %" GST_TIME_FORMAT, + GST_TIME_ARGS (current_time)); + session->start_time = current_time; + while (!rtpsession->priv->stop_thread) { GstClockReturn res; diff --git a/gst/rtpmanager/rtpsession.c b/gst/rtpmanager/rtpsession.c index 757fefe55d..d5b7f13bf4 100644 --- a/gst/rtpmanager/rtpsession.c +++ b/gst/rtpmanager/rtpsession.c @@ -2748,6 +2748,7 @@ session_cleanup (const gchar * key, RTPSource * source, ReportData * data) gboolean is_sender, is_active; RTPSession *sess = data->sess; GstClockTime interval; + GstClockTime btime; is_sender = RTP_SOURCE_IS_SENDER (source); is_active = RTP_SOURCE_IS_ACTIVE (source); @@ -2766,11 +2767,13 @@ session_cleanup (const gchar * key, RTPSource * source, ReportData * data) } /* sources that were inactive for more than 5 times the deterministic reporting * interval get timed out. the min timeout is 5 seconds. */ - if (data->current_time > source->last_activity) { + /* mind old time that might pre-date last time going to PLAYING */ + btime = MAX (source->last_activity, sess->start_time); + if (data->current_time > btime) { interval = MAX (data->interval * 5, 5 * GST_SECOND); - if (data->current_time - source->last_activity > interval) { + if (data->current_time - btime > interval) { GST_DEBUG ("removing timeout source %08x, last %" GST_TIME_FORMAT, - source->ssrc, GST_TIME_ARGS (source->last_activity)); + source->ssrc, GST_TIME_ARGS (btime)); remove = TRUE; } } @@ -2779,12 +2782,13 @@ session_cleanup (const gchar * key, RTPSource * source, ReportData * data) /* senders that did not send for a long time become a receiver, this also * holds for our own source. */ if (is_sender) { - if (data->current_time > source->last_rtp_activity) { + /* mind old time that might pre-date last time going to PLAYING */ + btime = MAX (source->last_rtp_activity, sess->start_time); + if (data->current_time > btime) { interval = MAX (data->interval * 2, 5 * GST_SECOND); - if (data->current_time - source->last_rtp_activity > interval) { + if (data->current_time - btime > interval) { GST_DEBUG ("sender source %08x timed out and became receiver, last %" - GST_TIME_FORMAT, source->ssrc, - GST_TIME_ARGS (source->last_rtp_activity)); + GST_TIME_FORMAT, source->ssrc, GST_TIME_ARGS (btime)); source->is_sender = FALSE; sess->stats.sender_sources--; sendertimeout = TRUE; diff --git a/gst/rtpmanager/rtpsession.h b/gst/rtpmanager/rtpsession.h index 30b74bb5db..6b827f60a1 100644 --- a/gst/rtpmanager/rtpsession.h +++ b/gst/rtpmanager/rtpsession.h @@ -209,6 +209,7 @@ struct _RTPSession { GstClockTime next_rtcp_check_time; GstClockTime last_rtcp_send_time; + GstClockTime start_time; gboolean first_rtcp; gboolean allow_early; From e2179cbb7493de38ddd5ce78f944b26f423bb351 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Fri, 2 Sep 2011 13:41:41 +0200 Subject: [PATCH 38/47] rtpsession: avoid source premature timing out Use slightly adjusted sender interval to determine sender timeout rather than our own sender side interval (which may have been forced small). --- gst/rtpmanager/rtpsession.c | 31 ++++++++++++++++++++++++++++--- gst/rtpmanager/rtpsource.c | 3 +++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/gst/rtpmanager/rtpsession.c b/gst/rtpmanager/rtpsession.c index d5b7f13bf4..fe73e99e7d 100644 --- a/gst/rtpmanager/rtpsession.c +++ b/gst/rtpmanager/rtpsession.c @@ -543,6 +543,8 @@ rtp_session_init (RTPSession * sess) sess->source->internal = TRUE; sess->stats.active_sources++; INIT_AVG (sess->stats.avg_rtcp_packet_size, 100); + sess->source->stats.prev_rtcptime = 0; + sess->source->stats.last_rtcptime = 1; rtp_stats_set_min_interval (&sess->stats, (gdouble) DEFAULT_RTCP_MIN_INTERVAL / GST_SECOND); @@ -2747,12 +2749,35 @@ session_cleanup (const gchar * key, RTPSource * source, ReportData * data) gboolean sendertimeout = FALSE; gboolean is_sender, is_active; RTPSession *sess = data->sess; - GstClockTime interval; + GstClockTime interval, binterval; GstClockTime btime; is_sender = RTP_SOURCE_IS_SENDER (source); is_active = RTP_SOURCE_IS_ACTIVE (source); + /* our own rtcp interval may have been forced low by secondary configuration, + * while sender side may still operate with higher interval, + * so do not just take our interval to decide on timing out sender, + * but take (if data->interval <= 5 * GST_SECOND): + * interval = CLAMP (sender_interval, data->interval, 5 * GST_SECOND) + * where sender_interval is difference between last 2 received RTCP reports + */ + if (data->interval >= 5 * GST_SECOND || (source == sess->source)) { + binterval = data->interval; + } else { + GST_LOG ("prev_rtcp %" GST_TIME_FORMAT ", last_rtcp %" GST_TIME_FORMAT, + GST_TIME_ARGS (source->stats.prev_rtcptime), + GST_TIME_ARGS (source->stats.last_rtcptime)); + /* if not received enough yet, fallback to larger default */ + if (source->stats.last_rtcptime > source->stats.prev_rtcptime) + binterval = source->stats.last_rtcptime - source->stats.prev_rtcptime; + else + binterval = 5 * GST_SECOND; + binterval = CLAMP (binterval, data->interval, 5 * GST_SECOND); + } + GST_LOG ("timeout base interval %" GST_TIME_FORMAT, + GST_TIME_ARGS (binterval)); + /* check for our own source, we don't want to delete our own source. */ if (!(source == sess->source)) { if (source->received_bye) { @@ -2770,7 +2795,7 @@ session_cleanup (const gchar * key, RTPSource * source, ReportData * data) /* mind old time that might pre-date last time going to PLAYING */ btime = MAX (source->last_activity, sess->start_time); if (data->current_time > btime) { - interval = MAX (data->interval * 5, 5 * GST_SECOND); + interval = MAX (binterval * 5, 5 * GST_SECOND); if (data->current_time - btime > interval) { GST_DEBUG ("removing timeout source %08x, last %" GST_TIME_FORMAT, source->ssrc, GST_TIME_ARGS (btime)); @@ -2785,7 +2810,7 @@ session_cleanup (const gchar * key, RTPSource * source, ReportData * data) /* mind old time that might pre-date last time going to PLAYING */ btime = MAX (source->last_rtp_activity, sess->start_time); if (data->current_time > btime) { - interval = MAX (data->interval * 2, 5 * GST_SECOND); + interval = MAX (binterval * 2, 5 * GST_SECOND); if (data->current_time - btime > interval) { GST_DEBUG ("sender source %08x timed out and became receiver, last %" GST_TIME_FORMAT, source->ssrc, GST_TIME_ARGS (btime)); diff --git a/gst/rtpmanager/rtpsource.c b/gst/rtpmanager/rtpsource.c index fb9e039553..f1ee4acd0b 100644 --- a/gst/rtpmanager/rtpsource.c +++ b/gst/rtpmanager/rtpsource.c @@ -1345,6 +1345,9 @@ rtp_source_process_sr (RTPSource * src, GstClockTime time, guint64 ntptime, /* make current */ src->stats.curr_sr = curridx; + + src->stats.prev_rtcptime = src->stats.last_rtcptime; + src->stats.last_rtcptime = time; } /** From d959bb604129fce5dd819e8d2d34f149a417d2f9 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Mon, 19 Sep 2011 12:11:32 +0200 Subject: [PATCH 39/47] qtdemux: parse embedded ID32 tags --- gst/isomp4/qtdemux.c | 49 ++++++++++++++++++++++++++++++++++++- gst/isomp4/qtdemux_fourcc.h | 3 +++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/gst/isomp4/qtdemux.c b/gst/isomp4/qtdemux.c index 39d2e4f746..9270304bd3 100644 --- a/gst/isomp4/qtdemux.c +++ b/gst/isomp4/qtdemux.c @@ -8361,6 +8361,42 @@ unknown_tag: } } +static void +qtdemux_tag_add_id32 (GstQTDemux * demux, const char *tag, + const char *tag_bis, GNode * node) +{ + guint8 *data; + GstBuffer *buf; + guint len; + GstTagList *taglist = NULL; + + GST_LOG_OBJECT (demux, "parsing ID32"); + + data = node->data; + len = GST_READ_UINT32_BE (data); + + /* need at least full box and language tag */ + if (len < 12 + 2) + return; + + buf = gst_buffer_new (); + GST_BUFFER_DATA (buf) = data + 14; + GST_BUFFER_SIZE (buf) = len - 14; + + taglist = gst_tag_list_from_id3v2_tag (buf); + if (taglist) { + GST_LOG_OBJECT (demux, "parsing ok"); + gst_tag_list_insert (demux->tag_list, taglist, GST_TAG_MERGE_KEEP); + } else { + GST_LOG_OBJECT (demux, "parsing failed"); + } + + if (taglist) + gst_tag_list_free (taglist); + + gst_buffer_unref (buf); +} + typedef void (*GstQTDemuxAddTagFunc) (GstQTDemux * demux, const char *tag, const char *tag_bis, GNode * node); @@ -8430,7 +8466,9 @@ static const struct * http://atomicparsley.sourceforge.net/mpeg-4files.html and * bug #614471 */ - FOURCC_____, "", NULL, qtdemux_tag_add_revdns} + FOURCC_____, "", NULL, qtdemux_tag_add_revdns}, { + /* see http://www.mp4ra.org/specs.html for ID32 in meta box */ + FOURCC_ID32, "", NULL, qtdemux_tag_add_id32} }; static void @@ -8846,6 +8884,15 @@ qtdemux_parse_tree (GstQTDemux * qtdemux) GST_LOG_OBJECT (qtdemux, "No udta node found."); } + /* maybe also some tags in meta box */ + udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_meta); + if (udta) { + GST_DEBUG_OBJECT (qtdemux, "Parsing meta box for tags."); + qtdemux_parse_udta (qtdemux, udta); + } else { + GST_LOG_OBJECT (qtdemux, "No meta node found."); + } + qtdemux->tag_list = qtdemux_add_container_format (qtdemux, qtdemux->tag_list); return TRUE; diff --git a/gst/isomp4/qtdemux_fourcc.h b/gst/isomp4/qtdemux_fourcc.h index 29ad155a3f..6666a94e55 100644 --- a/gst/isomp4/qtdemux_fourcc.h +++ b/gst/isomp4/qtdemux_fourcc.h @@ -189,6 +189,9 @@ G_BEGIN_DECLS #define FOURCC_albm GST_MAKE_FOURCC('a','l','b','m') #define FOURCC_yrrc GST_MAKE_FOURCC('y','r','r','c') +/* misc tag stuff */ +#define FOURCC_ID32 GST_MAKE_FOURCC('I', 'D','3','2') + /* ISO Motion JPEG 2000 fourcc */ #define FOURCC_mjp2 GST_MAKE_FOURCC('m','j','p','2') #define FOURCC_jp2h GST_MAKE_FOURCC('j','p','2','h') From 931020158e7d727d00dc8dd260d1a956f80d2126 Mon Sep 17 00:00:00 2001 From: Ha Nguyen Date: Mon, 19 Sep 2011 12:15:11 +0200 Subject: [PATCH 40/47] rtpbin: Fix a leaked clock for each buffering message Fixes bug #659237. --- gst/rtpmanager/gstrtpbin.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gst/rtpmanager/gstrtpbin.c b/gst/rtpmanager/gstrtpbin.c index beac56ced2..a5dcc15a05 100644 --- a/gst/rtpmanager/gstrtpbin.c +++ b/gst/rtpmanager/gstrtpbin.c @@ -2217,6 +2217,7 @@ gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message) now = gst_clock_get_time (clock); base_time = gst_element_get_base_time (GST_ELEMENT_CAST (bin)); running_time = now - base_time; + gst_object_unref (clock); } GST_DEBUG_OBJECT (bin, "running time now %" GST_TIME_FORMAT, From b6b072e948de49cafc2ed2f8ab9270acfe9db1d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Tue, 2 Aug 2011 17:39:44 +0100 Subject: [PATCH 41/47] tests: add unit test to make sure encodebin picks mp4mux for variant=iso https://bugzilla.gnome.org/show_bug.cgi?id=651496 --- tests/check/elements/qtmux.c | 135 ++++++++++++++++++++++++++++++++++- 1 file changed, 133 insertions(+), 2 deletions(-) diff --git a/tests/check/elements/qtmux.c b/tests/check/elements/qtmux.c index 997d3061c3..9a8ede4b3b 100644 --- a/tests/check/elements/qtmux.c +++ b/tests/check/elements/qtmux.c @@ -518,7 +518,7 @@ create_qtmux_profile (const gchar * variant) return cprof; } -GST_START_TEST (test_encodebin) +GST_START_TEST (test_encodebin_qtmux) { GstEncodingContainerProfile *cprof; GstElement *enc; @@ -554,6 +554,136 @@ GST_START_TEST (test_encodebin) GST_END_TEST; +/* Fake mp3 encoder for test */ +typedef GstElement TestMp3Enc; +typedef GstElementClass TestMp3EncClass; + +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]") + ); + +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-int") + ); + +static GType test_mp3_enc_get_type (void); + +GST_BOILERPLATE (TestMp3Enc, test_mp3_enc, GstElement, GST_TYPE_ELEMENT); + +static void +test_mp3_enc_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 Encoder", + "Codec/Encoder/Audio", "Pretends to encode mp3", "Foo Bar "); +} + +static void +test_mp3_enc_class_init (TestMp3EncClass * klass) +{ + /* doesn't actually need to do anything for this test */ +} + +static void +test_mp3_enc_init (TestMp3Enc * mp3enc, TestMp3EncClass * klass) +{ + GstPad *pad; + + pad = gst_pad_new_from_static_template (&sink_template, "sink"); + gst_element_add_pad (mp3enc, pad); + + pad = gst_pad_new_from_static_template (&src_template, "src"); + gst_element_add_pad (mp3enc, pad); +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "testmp3enc", GST_RANK_NONE, + test_mp3_enc_get_type ()); +} + +static GstEncodingContainerProfile * +create_mp4mux_profile (void) +{ + GstEncodingContainerProfile *cprof; + GstCaps *caps; + + caps = gst_caps_new_simple ("video/quicktime", + "variant", G_TYPE_STRING, "iso", NULL); + + cprof = gst_encoding_container_profile_new ("Name", "blah", caps, NULL); + gst_caps_unref (caps); + + caps = gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, 1, + "layer", G_TYPE_INT, 3, "channels", G_TYPE_INT, 2, "rate", G_TYPE_INT, + 44100, NULL); + gst_encoding_container_profile_add_profile (cprof, + GST_ENCODING_PROFILE (gst_encoding_audio_profile_new (caps, NULL, NULL, + 1))); + gst_caps_unref (caps); + + return cprof; +} + +GST_START_TEST (test_encodebin_mp4mux) +{ + GstEncodingContainerProfile *cprof; + GstPluginFeature *feature; + GstElement *enc, *mux; + GstPad *pad; + + /* need a fake mp3 encoder because mp4 only accepts encoded formats */ + gst_plugin_register_static (GST_VERSION_MAJOR, GST_VERSION_MINOR, + "fakemp3enc", "fakemp3enc", plugin_init, VERSION, "LGPL", + "gst-plugins-good", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); + + feature = gst_default_registry_find_feature ("testmp3enc", + GST_TYPE_ELEMENT_FACTORY); + gst_plugin_feature_set_rank (feature, GST_RANK_PRIMARY + 100); + + enc = gst_element_factory_make ("encodebin", NULL); + if (enc == NULL) + return; + + /* Make sure encodebin finds mp4mux even though qtmux outputs a superset */ + cprof = create_mp4mux_profile (); + g_object_set (enc, "profile", cprof, NULL); + gst_encoding_profile_unref (cprof); + + /* should have created a pad after setting the profile */ + pad = gst_element_get_static_pad (enc, "audio_0"); + fail_unless (pad != NULL); + gst_object_unref (pad); + + mux = gst_bin_get_by_interface (GST_BIN (enc), GST_TYPE_TAG_SETTER); + fail_unless (mux != NULL); + { + GstElementFactory *f = gst_element_get_factory (mux); + + /* make sure we got mp4mux for variant=iso */ + GST_INFO ("muxer: %s", G_OBJECT_TYPE_NAME (mux)); + fail_unless_equals_string (GST_PLUGIN_FEATURE_NAME (f), "mp4mux"); + } + gst_object_unref (mux); + gst_object_unref (enc); + + gst_plugin_feature_set_rank (feature, GST_RANK_NONE); + gst_object_unref (feature); +} + +GST_END_TEST; + static Suite * qtmux_suite (void) { @@ -583,7 +713,8 @@ qtmux_suite (void) tcase_add_test (tc_chain, test_audio_pad_frag_asc_streamable); tcase_add_test (tc_chain, test_reuse); - tcase_add_test (tc_chain, test_encodebin); + tcase_add_test (tc_chain, test_encodebin_qtmux); + tcase_add_test (tc_chain, test_encodebin_mp4mux); return s; } From 82927d6bddf4212fb0a2dc99dbb7cd89be941592 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Tue, 20 Sep 2011 12:11:47 +0100 Subject: [PATCH 42/47] ximagesrc: add xid and xname properties to allow capturing a particular window A particular window may be selected using the new xid (X-Window XID, eg a pointer) and xname (window title) properties. If both are specified, the XID is used in preference, falling back to xname if not found. Default (if none of xid and xname are specified, or if no such window is found) is to capture the root window. https://bugzilla.gnome.org/show_bug.cgi?id=546932 --- sys/ximage/gstximagesrc.c | 136 ++++++++++++++++++++++++++++++++++++-- sys/ximage/gstximagesrc.h | 4 ++ 2 files changed, 135 insertions(+), 5 deletions(-) diff --git a/sys/ximage/gstximagesrc.c b/sys/ximage/gstximagesrc.c index 94d4bcd1f4..b09c1abd59 100644 --- a/sys/ximage/gstximagesrc.c +++ b/sys/ximage/gstximagesrc.c @@ -71,6 +71,8 @@ enum PROP_ENDX, PROP_ENDY, PROP_REMOTE, + PROP_XID, + PROP_XNAME, }; GST_BOILERPLATE (GstXImageSrc, gst_ximage_src, GstPushSrc, GST_TYPE_PUSH_SRC); @@ -105,6 +107,35 @@ gst_ximage_src_return_buf (GstXImageSrc * ximagesrc, } } +static Window +gst_ximage_src_find_window (GstXImageSrc * src, Window root, const char *name) +{ + Window *children; + Window window = 0, root_return, parent_return; + unsigned int nchildren; + char *tmpname; + int n, status; + + status = XFetchName (src->xcontext->disp, root, &tmpname); + if (status && !strcmp (name, tmpname)) + return root; + + status = + XQueryTree (src->xcontext->disp, root, &root_return, &parent_return, + &children, &nchildren); + if (!status || !children) + return (Window) 0; + + for (n = 0; n < nchildren; ++n) { + window = gst_ximage_src_find_window (src, children[n], name); + if (window != 0) + break; + } + + XFree (children); + return window; +} + static gboolean gst_ximage_src_open_display (GstXImageSrc * s, const gchar * name) { @@ -125,8 +156,49 @@ gst_ximage_src_open_display (GstXImageSrc * s, const gchar * name) s->width = s->xcontext->width; s->height = s->xcontext->height; - /* Always capture root window, for now */ s->xwindow = s->xcontext->root; + if (s->xid != 0 || s->xname) { + int status; + XWindowAttributes attrs; + Window window; + + if (s->xid != 0) { + status = XGetWindowAttributes (s->xcontext->disp, s->xid, &attrs); + if (status) { + GST_DEBUG_OBJECT (s, "Found window XID %p", s->xid); + s->xwindow = s->xid; + goto window_found; + } else { + GST_WARNING_OBJECT (s, "Failed to get window %p attributes", s->xid); + } + } + + if (s->xname) { + GST_DEBUG_OBJECT (s, "Looking for window %s", s->xname); + window = gst_ximage_src_find_window (s, s->xcontext->root, s->xname); + if (window != 0) { + GST_DEBUG_OBJECT (s, "Found window named %s as %p, ", s->xname, window); + status = XGetWindowAttributes (s->xcontext->disp, window, &attrs); + if (status) { + s->xwindow = window; + goto window_found; + } else { + GST_WARNING_OBJECT (s, "Failed to get window %p attributes", window); + } + } + } + + GST_INFO_OBJECT (s, "Using root window"); + goto use_root_window; + + window_found: + g_assert (s->xwindow != 0); + s->width = attrs.width; + s->height = attrs.height; + GST_INFO_OBJECT (s, "Using default window %p, size of %dx%d", s->xwindow, + s->width, s->height); + } +use_root_window: #ifdef HAVE_XFIXES /* check if xfixes supported */ @@ -592,7 +664,8 @@ gst_ximage_src_ximage_get (GstXImageSrc * ximagesrc) } else #endif /* HAVE_XSHM */ { - GST_DEBUG_OBJECT (ximagesrc, "Retrieving screen using XGetImage"); + GST_DEBUG_OBJECT (ximagesrc, + "Retrieving screen using XGetImage, window %p", ximagesrc->xwindow); if (ximagesrc->remote) { XGetSubImage (ximagesrc->xcontext->disp, ximagesrc->xwindow, ximagesrc->startx, ximagesrc->starty, ximagesrc->width, @@ -836,6 +909,21 @@ gst_ximage_src_set_property (GObject * object, guint prop_id, case PROP_REMOTE: src->remote = g_value_get_boolean (value); break; + case PROP_XID: + if (src->xcontext != NULL) { + g_warning ("ximagesrc window ID must be set before opening display"); + break; + } + src->xid = g_value_get_uint64 (value); + break; + case PROP_XNAME: + if (src->xcontext != NULL) { + g_warning ("ximagesrc window name must be set before opening display"); + break; + } + g_free (src->xname); + src->xname = g_strdup (g_value_get_string (value)); + break; default: break; } @@ -879,6 +967,12 @@ gst_ximage_src_get_property (GObject * object, guint prop_id, GValue * value, case PROP_REMOTE: g_value_set_boolean (value, src->remote); break; + case PROP_XID: + g_value_set_uint64 (value, src->xid); + break; + case PROP_XNAME: + g_value_set_string (value, src->xname); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -931,6 +1025,7 @@ gst_ximage_src_finalize (GObject * object) if (src->xcontext) ximageutil_xcontext_clear (src->xcontext); + g_free (src->xname); g_mutex_free (src->pool_lock); g_mutex_free (src->x_lock); @@ -955,9 +1050,16 @@ gst_ximage_src_get_caps (GstBaseSrc * bs) (s)->srcpad)); xcontext = s->xcontext; - - width = xcontext->width; - height = xcontext->height; + width = s->xcontext->width; + height = s->xcontext->height; + if (s->xwindow != 0) { + XWindowAttributes attrs; + int status = XGetWindowAttributes (s->xcontext->disp, s->xwindow, &attrs); + if (status) { + width = attrs.width; + height = attrs.height; + } + } /* property comments say 0 means right/bottom, means we can't capture the top left pixel alone */ @@ -1138,6 +1240,30 @@ gst_ximage_src_class_init (GstXImageSrcClass * klass) "Whether the display is remote", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * GstXImageSrc:xid + * + * The XID of the window to capture. 0 for the root window (default). + * + * Since: 0.10.31 + **/ + g_object_class_install_property (gc, PROP_XID, + g_param_spec_uint64 ("xid", "Window XID", + "Window XID to capture from", 0, G_MAXUINT64, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GstXImageSrc:xname + * + * The name of the window to capture, if any. + * + * Since: 0.10.31 + **/ + g_object_class_install_property (gc, PROP_XNAME, + g_param_spec_string ("xname", "Window name", + "Window name to capture from", NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + parent_class = g_type_class_peek_parent (klass); push_class->create = gst_ximage_src_create; diff --git a/sys/ximage/gstximagesrc.h b/sys/ximage/gstximagesrc.h index f436df68fb..e58513c1ec 100644 --- a/sys/ximage/gstximagesrc.h +++ b/sys/ximage/gstximagesrc.h @@ -56,6 +56,10 @@ struct _GstXImageSrc gchar *display_name; guint screen_num; + /* Window selection */ + guint64 xid; + gchar *xname; + /* Desired output framerate */ gint fps_n; gint fps_d; From fd757890eb03da3a6c5898be47e6181f80d71969 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Tue, 20 Sep 2011 13:38:53 +0200 Subject: [PATCH 43/47] rtph264depay: improve downstream flow return feedback to upstream ... although basertpdepay does not really make it easy/possible to do so all the way. --- gst/rtp/gstrtph264depay.c | 45 ++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/gst/rtp/gstrtph264depay.c b/gst/rtp/gstrtph264depay.c index fb0697797d..cc49f56a09 100644 --- a/gst/rtp/gstrtph264depay.c +++ b/gst/rtp/gstrtph264depay.c @@ -506,7 +506,7 @@ gst_rtp_h264_complete_au (GstRtpH264Depay * rtph264depay, * so downstream waiting for keyframe can pick up at SPS/PPS/IDR */ #define NAL_TYPE_IS_KEY(nt) (((nt) == 5) || ((nt) == 7) || ((nt) == 8)) -static gboolean +static GstBuffer * gst_rtp_h264_depay_handle_nal (GstRtpH264Depay * rtph264depay, GstBuffer * nal, GstClockTime in_timestamp, gboolean marker) { @@ -592,23 +592,22 @@ gst_rtp_h264_depay_handle_nal (GstRtpH264Depay * rtph264depay, GstBuffer * nal, GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); gst_buffer_set_caps (outbuf, GST_PAD_CAPS (depayload->srcpad)); - - gst_base_rtp_depayload_push (depayload, outbuf); } - return TRUE; + return outbuf; /* ERRORS */ short_nal: { GST_WARNING_OBJECT (depayload, "dropping short NAL"); gst_buffer_unref (nal); - return FALSE; + return NULL; } } -static void -gst_rtp_h264_push_fragmentation_unit (GstRtpH264Depay * rtph264depay) +static GstBuffer * +gst_rtp_h264_push_fragmentation_unit (GstRtpH264Depay * rtph264depay, + gboolean send) { guint outsize; guint8 *outdata; @@ -630,17 +629,26 @@ gst_rtp_h264_push_fragmentation_unit (GstRtpH264Depay * rtph264depay) outdata[3] = (outsize); } - gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, - rtph264depay->fu_timestamp, rtph264depay->fu_marker); - rtph264depay->current_fu_type = 0; + + if (send) { + outbuf = gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, + rtph264depay->fu_timestamp, rtph264depay->fu_marker); + if (outbuf) + gst_base_rtp_depayload_push (GST_BASE_RTP_DEPAYLOAD (rtph264depay), + outbuf); + return NULL; + } else { + return gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, + rtph264depay->fu_timestamp, rtph264depay->fu_marker); + } } static GstBuffer * gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) { GstRtpH264Depay *rtph264depay; - GstBuffer *outbuf; + GstBuffer *outbuf = NULL; guint8 nal_unit_type; rtph264depay = GST_RTP_H264_DEPAY (depayload); @@ -695,7 +703,7 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) * when the FU ended) and send out what we gathered thusfar */ if (G_UNLIKELY (rtph264depay->current_fu_type != 0 && nal_unit_type != rtph264depay->current_fu_type)) - gst_rtp_h264_push_fragmentation_unit (rtph264depay); + gst_rtp_h264_push_fragmentation_unit (rtph264depay, TRUE); switch (nal_unit_type) { case 0: @@ -757,7 +765,8 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) outsize = gst_adapter_available (rtph264depay->adapter); outbuf = gst_adapter_take_buffer (rtph264depay->adapter, outsize); - gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp, marker); + outbuf = gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp, + marker); break; } case 26: @@ -800,7 +809,7 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) * Assume that the remote payloader is buggy (doesn't set the end * bit) and send out what we've gathered thusfar */ if (G_UNLIKELY (rtph264depay->current_fu_type != 0)) - gst_rtp_h264_push_fragmentation_unit (rtph264depay); + gst_rtp_h264_push_fragmentation_unit (rtph264depay, TRUE); rtph264depay->current_fu_type = nal_unit_type; rtph264depay->fu_timestamp = timestamp; @@ -843,11 +852,12 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) gst_adapter_push (rtph264depay->adapter, outbuf); } + outbuf = NULL; rtph264depay->fu_marker = marker; /* if NAL unit ends, flush the adapter */ if (E) - gst_rtp_h264_push_fragmentation_unit (rtph264depay); + outbuf = gst_rtp_h264_push_fragmentation_unit (rtph264depay, FALSE); break; } default: @@ -870,13 +880,14 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) outdata += sizeof (sync_bytes); memcpy (outdata, payload, nalu_size); - gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp, marker); + outbuf = gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp, + marker); break; } } } - return NULL; + return outbuf; /* ERRORS */ undefined_type: From 2131a3b7f8dbfb8304475d32af2a636f28efb50b Mon Sep 17 00:00:00 2001 From: Julien Isorce Date: Fri, 23 Sep 2011 15:36:36 +0200 Subject: [PATCH 44/47] ac3parse: correctly check for ac3/e-ac3 switch https://bugzilla.gnome.org/show_bug.cgi?id=659943 --- gst/audioparsers/gstac3parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/audioparsers/gstac3parse.c b/gst/audioparsers/gstac3parse.c index b2f97643c4..f907249076 100644 --- a/gst/audioparsers/gstac3parse.c +++ b/gst/audioparsers/gstac3parse.c @@ -595,7 +595,7 @@ gst_ac3_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame) } if (G_UNLIKELY (ac3parse->sample_rate != rate || ac3parse->channels != chans - || ac3parse->eac != ac3parse->eac)) { + || ac3parse->eac != eac)) { GstCaps *caps = gst_caps_new_simple (eac ? "audio/x-eac3" : "audio/x-ac3", "framed", G_TYPE_BOOLEAN, TRUE, "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, chans, NULL); From be7cbd4c21c72d758e9d8b006712872cc1a09765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Fri, 23 Sep 2011 16:39:46 +0100 Subject: [PATCH 45/47] souphttpsink: don't create unused second sink pad object The base class will create the sink pad. --- ext/soup/gstsouphttpsink.c | 4 ---- ext/soup/gstsouphttpsink.h | 2 -- 2 files changed, 6 deletions(-) diff --git a/ext/soup/gstsouphttpsink.c b/ext/soup/gstsouphttpsink.c index 2de51d867f..ff525c624c 100644 --- a/ext/soup/gstsouphttpsink.c +++ b/ext/soup/gstsouphttpsink.c @@ -200,10 +200,6 @@ gst_soup_http_sink_init (GstSoupHttpSink * souphttpsink, { const char *proxy; - souphttpsink->sinkpad = - gst_pad_new_from_static_template (&gst_soup_http_sink_sink_template, - "sink"); - souphttpsink->mutex = g_mutex_new (); souphttpsink->cond = g_cond_new (); diff --git a/ext/soup/gstsouphttpsink.h b/ext/soup/gstsouphttpsink.h index f78272946e..0cf98d0a01 100644 --- a/ext/soup/gstsouphttpsink.h +++ b/ext/soup/gstsouphttpsink.h @@ -38,8 +38,6 @@ struct _GstSoupHttpSink { GstBaseSink base_souphttpsink; - GstPad *sinkpad; - GMutex *mutex; GCond *cond; GMainContext *context; From 3828537857e09ec1aa8644e1004309ae8349ea44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sun, 25 Sep 2011 15:13:39 +0100 Subject: [PATCH 46/47] soup: rename souphttpsink to souphttpclientsink To avoid confusion, and because we might want a server sink at some point too. https://bugzilla.gnome.org/show_bug.cgi?id=659947 --- ext/soup/Makefile.am | 4 +- ext/soup/gstsoup.c | 6 +- ...souphttpsink.c => gstsouphttpclientsink.c} | 168 +++++++++--------- ...souphttpsink.h => gstsouphttpclientsink.h} | 26 +-- 4 files changed, 105 insertions(+), 99 deletions(-) rename ext/soup/{gstsouphttpsink.c => gstsouphttpclientsink.c} (76%) rename ext/soup/{gstsouphttpsink.h => gstsouphttpclientsink.h} (60%) diff --git a/ext/soup/Makefile.am b/ext/soup/Makefile.am index 29564063cb..6916b174a8 100644 --- a/ext/soup/Makefile.am +++ b/ext/soup/Makefile.am @@ -1,10 +1,10 @@ plugin_LTLIBRARIES = libgstsouphttpsrc.la -libgstsouphttpsrc_la_SOURCES = gstsouphttpsrc.c gstsouphttpsink.c gstsoup.c +libgstsouphttpsrc_la_SOURCES = gstsouphttpsrc.c gstsouphttpclientsink.c gstsoup.c libgstsouphttpsrc_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(SOUP_CFLAGS) libgstsouphttpsrc_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgsttag-@GST_MAJORMINOR@ $(GST_BASE_LIBS) $(SOUP_LIBS) libgstsouphttpsrc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstsouphttpsrc_la_LIBTOOLFLAGS = --tag=disable-static -noinst_HEADERS = gstsouphttpsrc.h gstsouphttpsink.h +noinst_HEADERS = gstsouphttpsrc.h gstsouphttpclientsink.h diff --git a/ext/soup/gstsoup.c b/ext/soup/gstsoup.c index d01ed9b86e..de71df821b 100644 --- a/ext/soup/gstsoup.c +++ b/ext/soup/gstsoup.c @@ -19,7 +19,7 @@ #include #include "gstsouphttpsrc.h" -#include "gstsouphttpsink.h" +#include "gstsouphttpclientsink.h" static gboolean @@ -34,8 +34,8 @@ plugin_init (GstPlugin * plugin) gst_element_register (plugin, "souphttpsrc", GST_RANK_PRIMARY, GST_TYPE_SOUP_HTTP_SRC); - gst_element_register (plugin, "souphttpsink", GST_RANK_NONE, - GST_TYPE_SOUP_HTTP_SINK); + gst_element_register (plugin, "souphttpclientsink", GST_RANK_NONE, + GST_TYPE_SOUP_HTTP_CLIENT_SINK); return TRUE; } diff --git a/ext/soup/gstsouphttpsink.c b/ext/soup/gstsouphttpclientsink.c similarity index 76% rename from ext/soup/gstsouphttpsink.c rename to ext/soup/gstsouphttpclientsink.c index ff525c624c..4aa84aecd3 100644 --- a/ext/soup/gstsouphttpsink.c +++ b/ext/soup/gstsouphttpclientsink.c @@ -17,16 +17,16 @@ * Boston, MA 02110-1335, USA. */ /** - * SECTION:element-gstsouphttpsink + * SECTION:element-gstsouphttpclientsink * - * The souphttpsink element sends pipeline data to an HTTP server + * The souphttpclientsink element sends pipeline data to an HTTP server * using HTTP PUT commands. * * * Example launch line * |[ * gst-launch -v videotestsrc num-buffers=300 ! theoraenc ! oggmux ! - * souphttpsink location=http://server/filename.ogv + * souphttpclientsink location=http://server/filename.ogv * ]| * * This example encodes 10 seconds of video and sends it to the HTTP @@ -40,43 +40,44 @@ #include #include -#include "gstsouphttpsink.h" +#include "gstsouphttpclientsink.h" -GST_DEBUG_CATEGORY_STATIC (gst_soup_http_sink_debug_category); -#define GST_CAT_DEFAULT gst_soup_http_sink_debug_category +GST_DEBUG_CATEGORY_STATIC (souphttpclientsink_dbg); +#define GST_CAT_DEFAULT souphttpclientsink_dbg /* prototypes */ -static void gst_soup_http_sink_set_property (GObject * object, +static void gst_soup_http_client_sink_set_property (GObject * object, guint property_id, const GValue * value, GParamSpec * pspec); -static void gst_soup_http_sink_get_property (GObject * object, +static void gst_soup_http_client_sink_get_property (GObject * object, guint property_id, GValue * value, GParamSpec * pspec); -static void gst_soup_http_sink_dispose (GObject * object); -static void gst_soup_http_sink_finalize (GObject * object); +static void gst_soup_http_client_sink_dispose (GObject * object); +static void gst_soup_http_client_sink_finalize (GObject * object); -static gboolean gst_soup_http_sink_set_caps (GstBaseSink * sink, +static gboolean gst_soup_http_client_sink_set_caps (GstBaseSink * sink, GstCaps * caps); -static void gst_soup_http_sink_get_times (GstBaseSink * sink, +static void gst_soup_http_client_sink_get_times (GstBaseSink * sink, GstBuffer * buffer, GstClockTime * start, GstClockTime * end); -static gboolean gst_soup_http_sink_start (GstBaseSink * sink); -static gboolean gst_soup_http_sink_stop (GstBaseSink * sink); -static gboolean gst_soup_http_sink_unlock (GstBaseSink * sink); -static gboolean gst_soup_http_sink_event (GstBaseSink * sink, GstEvent * event); -static GstFlowReturn -gst_soup_http_sink_preroll (GstBaseSink * sink, GstBuffer * buffer); -static GstFlowReturn -gst_soup_http_sink_render (GstBaseSink * sink, GstBuffer * buffer); +static gboolean gst_soup_http_client_sink_start (GstBaseSink * sink); +static gboolean gst_soup_http_client_sink_stop (GstBaseSink * sink); +static gboolean gst_soup_http_client_sink_unlock (GstBaseSink * sink); +static gboolean gst_soup_http_client_sink_event (GstBaseSink * sink, + GstEvent * event); +static GstFlowReturn gst_soup_http_client_sink_preroll (GstBaseSink * sink, + GstBuffer * buffer); +static GstFlowReturn gst_soup_http_client_sink_render (GstBaseSink * sink, + GstBuffer * buffer); static void free_buffer_list (GList * list); -static void gst_soup_http_sink_reset (GstSoupHttpSink * souphttpsink); +static void gst_soup_http_client_sink_reset (GstSoupHttpClientSink * + souphttpsink); static void authenticate (SoupSession * session, SoupMessage * msg, SoupAuth * auth, gboolean retrying, gpointer user_data); -static void -callback (SoupSession * session, SoupMessage * msg, gpointer user_data); -static gboolean -gst_soup_http_sink_set_proxy (GstSoupHttpSink * souphttpsink, - const gchar * uri); +static void callback (SoupSession * session, SoupMessage * msg, + gpointer user_data); +static gboolean gst_soup_http_client_sink_set_proxy (GstSoupHttpClientSink * + souphttpsink, const gchar * uri); enum { @@ -93,11 +94,11 @@ enum PROP_SESSION }; -#define DEFAULT_USER_AGENT "GStreamer souphttpsink " +#define DEFAULT_USER_AGENT "GStreamer souphttpclientsink " /* pad templates */ -static GstStaticPadTemplate gst_soup_http_sink_sink_template = +static GstStaticPadTemplate gst_soup_http_client_sink_sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, @@ -107,19 +108,19 @@ GST_STATIC_PAD_TEMPLATE ("sink", /* class initialization */ #define DEBUG_INIT(bla) \ - GST_DEBUG_CATEGORY_INIT (gst_soup_http_sink_debug_category, "souphttpsink", 0, \ - "debug category for souphttpsink element"); + GST_DEBUG_CATEGORY_INIT (souphttpclientsink_dbg, "souphttpclientsink", 0, \ + "souphttpclientsink element"); -GST_BOILERPLATE_FULL (GstSoupHttpSink, gst_soup_http_sink, GstBaseSink, - GST_TYPE_BASE_SINK, DEBUG_INIT); +GST_BOILERPLATE_FULL (GstSoupHttpClientSink, gst_soup_http_client_sink, + GstBaseSink, GST_TYPE_BASE_SINK, DEBUG_INIT); static void -gst_soup_http_sink_base_init (gpointer g_class) +gst_soup_http_client_sink_base_init (gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&gst_soup_http_sink_sink_template)); + gst_static_pad_template_get (&gst_soup_http_client_sink_sink_template)); gst_element_class_set_details_simple (element_class, "HTTP client sink", "Generic", "Sends streams to HTTP server via PUT", @@ -127,26 +128,30 @@ gst_soup_http_sink_base_init (gpointer g_class) } static void -gst_soup_http_sink_class_init (GstSoupHttpSinkClass * klass) +gst_soup_http_client_sink_class_init (GstSoupHttpClientSinkClass * klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GstBaseSinkClass *base_sink_class = GST_BASE_SINK_CLASS (klass); - gobject_class->set_property = gst_soup_http_sink_set_property; - gobject_class->get_property = gst_soup_http_sink_get_property; - gobject_class->dispose = gst_soup_http_sink_dispose; - gobject_class->finalize = gst_soup_http_sink_finalize; - base_sink_class->set_caps = GST_DEBUG_FUNCPTR (gst_soup_http_sink_set_caps); + gobject_class->set_property = gst_soup_http_client_sink_set_property; + gobject_class->get_property = gst_soup_http_client_sink_get_property; + gobject_class->dispose = gst_soup_http_client_sink_dispose; + gobject_class->finalize = gst_soup_http_client_sink_finalize; + base_sink_class->set_caps = + GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_set_caps); if (0) base_sink_class->get_times = - GST_DEBUG_FUNCPTR (gst_soup_http_sink_get_times); - base_sink_class->start = GST_DEBUG_FUNCPTR (gst_soup_http_sink_start); - base_sink_class->stop = GST_DEBUG_FUNCPTR (gst_soup_http_sink_stop); - base_sink_class->unlock = GST_DEBUG_FUNCPTR (gst_soup_http_sink_unlock); - base_sink_class->event = GST_DEBUG_FUNCPTR (gst_soup_http_sink_event); + GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_get_times); + base_sink_class->start = GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_start); + base_sink_class->stop = GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_stop); + base_sink_class->unlock = + GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_unlock); + base_sink_class->event = GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_event); if (0) - base_sink_class->preroll = GST_DEBUG_FUNCPTR (gst_soup_http_sink_preroll); - base_sink_class->render = GST_DEBUG_FUNCPTR (gst_soup_http_sink_render); + base_sink_class->preroll = + GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_preroll); + base_sink_class->render = + GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_render); g_object_class_install_property (gobject_class, PROP_LOCATION, @@ -195,8 +200,8 @@ gst_soup_http_sink_class_init (GstSoupHttpSinkClass * klass) } static void -gst_soup_http_sink_init (GstSoupHttpSink * souphttpsink, - GstSoupHttpSinkClass * souphttpsink_class) +gst_soup_http_client_sink_init (GstSoupHttpClientSink * souphttpsink, + GstSoupHttpClientSinkClass * souphttpsink_class) { const char *proxy; @@ -213,17 +218,17 @@ gst_soup_http_sink_init (GstSoupHttpSink * souphttpsink, souphttpsink->prop_session = NULL; souphttpsink->timeout = 1; proxy = g_getenv ("http_proxy"); - if (proxy && !gst_soup_http_sink_set_proxy (souphttpsink, proxy)) { + if (proxy && !gst_soup_http_client_sink_set_proxy (souphttpsink, proxy)) { GST_WARNING_OBJECT (souphttpsink, "The proxy in the http_proxy env var (\"%s\") cannot be parsed.", proxy); } - gst_soup_http_sink_reset (souphttpsink); + gst_soup_http_client_sink_reset (souphttpsink); } static void -gst_soup_http_sink_reset (GstSoupHttpSink * souphttpsink) +gst_soup_http_client_sink_reset (GstSoupHttpClientSink * souphttpsink) { g_free (souphttpsink->reason_phrase); souphttpsink->reason_phrase = NULL; @@ -233,7 +238,8 @@ gst_soup_http_sink_reset (GstSoupHttpSink * souphttpsink) } static gboolean -gst_soup_http_sink_set_proxy (GstSoupHttpSink * souphttpsink, const gchar * uri) +gst_soup_http_client_sink_set_proxy (GstSoupHttpClientSink * souphttpsink, + const gchar * uri) { if (souphttpsink->proxy) { soup_uri_free (souphttpsink->proxy); @@ -252,10 +258,10 @@ gst_soup_http_sink_set_proxy (GstSoupHttpSink * souphttpsink, const gchar * uri) } void -gst_soup_http_sink_set_property (GObject * object, guint property_id, +gst_soup_http_client_sink_set_property (GObject * object, guint property_id, const GValue * value, GParamSpec * pspec) { - GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (object); + GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (object); g_mutex_lock (souphttpsink->mutex); switch (property_id) { @@ -303,7 +309,7 @@ gst_soup_http_sink_set_property (GObject * object, guint property_id, GST_WARNING ("proxy property cannot be NULL"); goto done; } - if (!gst_soup_http_sink_set_proxy (souphttpsink, proxy)) { + if (!gst_soup_http_client_sink_set_proxy (souphttpsink, proxy)) { GST_WARNING ("badly formatted proxy URI"); goto done; } @@ -322,10 +328,10 @@ done: } void -gst_soup_http_sink_get_property (GObject * object, guint property_id, +gst_soup_http_client_sink_get_property (GObject * object, guint property_id, GValue * value, GParamSpec * pspec) { - GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (object); + GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (object); switch (property_id) { case PROP_SESSION: @@ -372,9 +378,9 @@ gst_soup_http_sink_get_property (GObject * object, guint property_id, } void -gst_soup_http_sink_dispose (GObject * object) +gst_soup_http_client_sink_dispose (GObject * object) { - GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (object); + GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (object); /* clean up as possible. may be called multiple times */ if (souphttpsink->prop_session) @@ -385,9 +391,9 @@ gst_soup_http_sink_dispose (GObject * object) } void -gst_soup_http_sink_finalize (GObject * object) +gst_soup_http_client_sink_finalize (GObject * object) { - GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (object); + GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (object); /* clean up object here */ @@ -409,9 +415,9 @@ gst_soup_http_sink_finalize (GObject * object) static gboolean -gst_soup_http_sink_set_caps (GstBaseSink * sink, GstCaps * caps) +gst_soup_http_client_sink_set_caps (GstBaseSink * sink, GstCaps * caps) { - GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (sink); + GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (sink); GstStructure *structure; const GValue *value_array; int i, n; @@ -438,7 +444,7 @@ gst_soup_http_sink_set_caps (GstBaseSink * sink, GstCaps * caps) } static void -gst_soup_http_sink_get_times (GstBaseSink * sink, GstBuffer * buffer, +gst_soup_http_client_sink_get_times (GstBaseSink * sink, GstBuffer * buffer, GstClockTime * start, GstClockTime * end) { @@ -447,7 +453,7 @@ gst_soup_http_sink_get_times (GstBaseSink * sink, GstBuffer * buffer, static gpointer thread_func (gpointer ptr) { - GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (ptr); + GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (ptr); GST_DEBUG ("thread start"); @@ -460,9 +466,9 @@ thread_func (gpointer ptr) } static gboolean -gst_soup_http_sink_start (GstBaseSink * sink) +gst_soup_http_client_sink_start (GstBaseSink * sink) { - GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (sink); + GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (sink); if (souphttpsink->prop_session) { souphttpsink->session = souphttpsink->prop_session; @@ -491,9 +497,9 @@ gst_soup_http_sink_start (GstBaseSink * sink) } static gboolean -gst_soup_http_sink_stop (GstBaseSink * sink) +gst_soup_http_client_sink_stop (GstBaseSink * sink) { - GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (sink); + GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (sink); GST_DEBUG ("stop"); @@ -513,13 +519,13 @@ gst_soup_http_sink_stop (GstBaseSink * sink) souphttpsink->context = NULL; } - gst_soup_http_sink_reset (souphttpsink); + gst_soup_http_client_sink_reset (souphttpsink); return TRUE; } static gboolean -gst_soup_http_sink_unlock (GstBaseSink * sink) +gst_soup_http_client_sink_unlock (GstBaseSink * sink) { GST_DEBUG ("unlock"); @@ -527,9 +533,9 @@ gst_soup_http_sink_unlock (GstBaseSink * sink) } static gboolean -gst_soup_http_sink_event (GstBaseSink * sink, GstEvent * event) +gst_soup_http_client_sink_event (GstBaseSink * sink, GstEvent * event) { - GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (sink); + GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (sink); GST_DEBUG_OBJECT (souphttpsink, "event"); @@ -548,7 +554,7 @@ gst_soup_http_sink_event (GstBaseSink * sink, GstEvent * event) } static GstFlowReturn -gst_soup_http_sink_preroll (GstBaseSink * sink, GstBuffer * buffer) +gst_soup_http_client_sink_preroll (GstBaseSink * sink, GstBuffer * buffer) { GST_DEBUG ("preroll"); @@ -567,7 +573,7 @@ free_buffer_list (GList * list) } static void -send_message_locked (GstSoupHttpSink * souphttpsink) +send_message_locked (GstSoupHttpClientSink * souphttpsink) { GList *g; guint64 n; @@ -636,7 +642,7 @@ send_message_locked (GstSoupHttpSink * souphttpsink) } static gboolean -send_message (GstSoupHttpSink * souphttpsink) +send_message (GstSoupHttpClientSink * souphttpsink) { g_mutex_lock (souphttpsink->mutex); send_message_locked (souphttpsink); @@ -648,7 +654,7 @@ send_message (GstSoupHttpSink * souphttpsink) static void callback (SoupSession * session, SoupMessage * msg, gpointer user_data) { - GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (user_data); + GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (user_data); GST_DEBUG_OBJECT (souphttpsink, "callback status=%d %s", msg->status_code, msg->reason_phrase); @@ -672,9 +678,9 @@ callback (SoupSession * session, SoupMessage * msg, gpointer user_data) } static GstFlowReturn -gst_soup_http_sink_render (GstBaseSink * sink, GstBuffer * buffer) +gst_soup_http_client_sink_render (GstBaseSink * sink, GstBuffer * buffer) { - GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (sink); + GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (sink); GSource *source; gboolean wake; @@ -710,7 +716,7 @@ static void authenticate (SoupSession * session, SoupMessage * msg, SoupAuth * auth, gboolean retrying, gpointer user_data) { - GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (user_data); + GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (user_data); if (!retrying) { if (souphttpsink->user_id && souphttpsink->user_pw) { diff --git a/ext/soup/gstsouphttpsink.h b/ext/soup/gstsouphttpclientsink.h similarity index 60% rename from ext/soup/gstsouphttpsink.h rename to ext/soup/gstsouphttpclientsink.h index 0cf98d0a01..fab143051d 100644 --- a/ext/soup/gstsouphttpsink.h +++ b/ext/soup/gstsouphttpclientsink.h @@ -1,5 +1,5 @@ /* GStreamer - * Copyright (C) 2011 FIXME + * Copyright (C) 2011 David Schleef * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -17,24 +17,24 @@ * Boston, MA 02111-1307, USA. */ -#ifndef _GST_SOUP_HTTP_SINK_H_ -#define _GST_SOUP_HTTP_SINK_H_ +#ifndef _GST_SOUP_HTTP_CLIENT_SINK_H_ +#define _GST_SOUP_HTTP_CLIENT_SINK_H_ #include #include G_BEGIN_DECLS -#define GST_TYPE_SOUP_HTTP_SINK (gst_soup_http_sink_get_type()) -#define GST_SOUP_HTTP_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SOUP_HTTP_SINK,GstSoupHttpSink)) -#define GST_SOUP_HTTP_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SOUP_HTTP_SINK,GstSoupHttpSinkClass)) -#define GST_IS_SOUP_HTTP_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SOUP_HTTP_SINK)) -#define GST_IS_SOUP_HTTP_SINK_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SOUP_HTTP_SINK)) +#define GST_TYPE_SOUP_HTTP_CLIENT_SINK (gst_soup_http_client_sink_get_type()) +#define GST_SOUP_HTTP_CLIENT_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SOUP_HTTP_CLIENT_SINK,GstSoupHttpClientSink)) +#define GST_SOUP_HTTP_CLIENT_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SOUP_HTTP_CLIENT_SINK,GstSoupHttpClientSinkClass)) +#define GST_IS_SOUP_HTTP_CLIENT_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SOUP_HTTP_CLIENT_SINK)) +#define GST_IS_SOUP_HTTP_CLIENT_SINK_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SOUP_HTTP_CLIENT_SINK)) -typedef struct _GstSoupHttpSink GstSoupHttpSink; -typedef struct _GstSoupHttpSinkClass GstSoupHttpSinkClass; +typedef struct _GstSoupHttpClientSink GstSoupHttpClientSink; +typedef struct _GstSoupHttpClientSinkClass GstSoupHttpClientSinkClass; -struct _GstSoupHttpSink +struct _GstSoupHttpClientSink { GstBaseSink base_souphttpsink; @@ -69,12 +69,12 @@ struct _GstSoupHttpSink }; -struct _GstSoupHttpSinkClass +struct _GstSoupHttpClientSinkClass { GstBaseSinkClass base_souphttpsink_class; }; -GType gst_soup_http_sink_get_type (void); +GType gst_soup_http_client_sink_get_type (void); G_END_DECLS From 827c3aa14b3efd48c68e6ab565b29e6cfc0c0ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimo=20J=C3=A4rvi?= Date: Wed, 28 Sep 2011 00:10:09 +0300 Subject: [PATCH 47/47] goom2k1: Fix compiler warnings on 64 bit mingw-w64 Fixes bug #660294. --- gst/goom2k1/goom_core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gst/goom2k1/goom_core.c b/gst/goom2k1/goom_core.c index d66ffe25de..ee893f4076 100644 --- a/gst/goom2k1/goom_core.c +++ b/gst/goom2k1/goom_core.c @@ -66,9 +66,8 @@ goom_set_resolution (GoomData * goomdata, guint32 resx, guint32 resy) goomdata->back = (guint32 *) malloc (buffsize * sizeof (guint32) + 128); goomdata->buffsize = buffsize; - goomdata->p1 = - (void *) (((unsigned long) goomdata->pixel + 0x7f) & (~0x7f)); - goomdata->p2 = (void *) (((unsigned long) goomdata->back + 0x7f) & (~0x7f)); + goomdata->p1 = (void *) (((guintptr) goomdata->pixel + 0x7f) & (~0x7f)); + goomdata->p2 = (void *) (((guintptr) goomdata->back + 0x7f) & (~0x7f)); } goomdata->resolx = resx;