From df3cee6606bed13d2edc8480ed244d480ab4790c Mon Sep 17 00:00:00 2001 From: Antoine Jacoutot Date: Sat, 16 Jul 2011 23:47:50 +0100 Subject: [PATCH 01/32] goom: fix build on PPC on openbsd A missing sys/param.h include results in: /usr/include/sys/proc.h:64: error: 'MAXLOGNAME' undeclared here (not in a function) /usr/include/sys/proc.h:285: error: 'MAXCOMLEN' undeclared here (not in a function) when compiling goom on openbsd/ppc. We can just remove the two sys/ includes here, they are not needed for anything. https://bugzilla.gnome.org/show_bug.cgi?id=654749 --- gst/goom/plugin_info.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/gst/goom/plugin_info.c b/gst/goom/plugin_info.c index aba15ca8fb..b50c9dd9ab 100644 --- a/gst/goom/plugin_info.c +++ b/gst/goom/plugin_info.c @@ -35,8 +35,6 @@ #if defined (HAVE_CPU_PPC64) || defined (HAVE_CPU_PPC) -#include -#include #include "ppc_zoom_ultimate.h" #include "ppc_drawings.h" #endif /* HAVE_CPU_PPC64 || HAVE_CPU_PPC */ From 69c14012c9585780a9fd69c73f80d13f7917432d Mon Sep 17 00:00:00 2001 From: Alexey Fisher Date: Sat, 16 Jul 2011 19:38:51 +0200 Subject: [PATCH 02/32] matroskademux: fix pixel-aspect-ratio if header has only one display variable Current matroska demux calculates the pixel aspect ratio only if both DisplayHeight and DisplayWidth are set, but it is legal to use only one variable if the other is equal to PixelWidth or PixelHeight, at least the mkclean utility is doing that. So this makse mkcleaned files play correctly. https://bugzilla.gnome.org/show_bug.cgi?id=654744 --- gst/matroska/matroska-demux.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c index 9d2bf6e462..9481ed9722 100644 --- a/gst/matroska/matroska-demux.c +++ b/gst/matroska/matroska-demux.c @@ -4873,16 +4873,20 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * /* pixel width and height are the w and h of the video in pixels */ if (videocontext->pixel_width > 0 && videocontext->pixel_height > 0) { gint w = videocontext->pixel_width; - gint h = videocontext->pixel_height; gst_structure_set (structure, "width", G_TYPE_INT, w, "height", G_TYPE_INT, h, NULL); } - if (videocontext->display_width > 0 && videocontext->display_height > 0) { + if (videocontext->display_width > 0 || videocontext->display_height > 0) { int n, d; + if (videocontext->display_width <= 0) + videocontext->display_width = videocontext->pixel_width; + if (videocontext->display_height <= 0) + videocontext->display_height = videocontext->pixel_height; + /* calculate the pixel aspect ratio using the display and pixel w/h */ n = videocontext->display_width * videocontext->pixel_height; d = videocontext->display_height * videocontext->pixel_width; From c9096c68292b5b3a29b3c59e9dfb8277bf4f779d Mon Sep 17 00:00:00 2001 From: Alessandro Decina Date: Sun, 17 Jul 2011 23:36:55 +0200 Subject: [PATCH 03/32] multifilesink: refactor file opening and closing code --- gst/multifile/gstmultifilesink.c | 77 +++++++++++++++++++------------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/gst/multifile/gstmultifilesink.c b/gst/multifile/gstmultifilesink.c index b333e809d1..0ef5fd9003 100644 --- a/gst/multifile/gstmultifilesink.c +++ b/gst/multifile/gstmultifilesink.c @@ -148,6 +148,10 @@ static GstFlowReturn gst_multi_file_sink_render (GstBaseSink * sink, GstBuffer * buffer); static gboolean gst_multi_file_sink_set_caps (GstBaseSink * sink, GstCaps * caps); +static gboolean gst_multi_file_sink_open_next_file (GstMultiFileSink * + multifilesink); +static void gst_multi_file_sink_close_file (GstMultiFileSink * multifilesink, + GstBuffer * buffer); #define GST_TYPE_MULTI_FILE_SINK_NEXT (gst_multi_file_sink_next_get_type ()) static GType @@ -420,25 +424,12 @@ gst_multi_file_sink_render (GstBaseSink * sink, GstBuffer * buffer) break; case GST_MULTI_FILE_SINK_NEXT_DISCONT: if (GST_BUFFER_IS_DISCONT (buffer)) { - if (multifilesink->file) { - fclose (multifilesink->file); - multifilesink->file = NULL; - - filename = g_strdup_printf (multifilesink->filename, - multifilesink->index); - gst_multi_file_sink_post_message (multifilesink, buffer, filename); - g_free (filename); - multifilesink->index++; - } + if (multifilesink->file) + gst_multi_file_sink_close_file (multifilesink, buffer); } if (multifilesink->file == NULL) { - filename = g_strdup_printf (multifilesink->filename, - multifilesink->index); - multifilesink->file = g_fopen (filename, "wb"); - g_free (filename); - - if (multifilesink->file == NULL) + if (!gst_multi_file_sink_open_next_file (multifilesink)) goto stdio_write_error; } @@ -459,16 +450,8 @@ gst_multi_file_sink_render (GstBaseSink * sink, GstBuffer * buffer) if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) && GST_BUFFER_TIMESTAMP (buffer) >= multifilesink->next_segment && !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) { - if (multifilesink->file) { - fclose (multifilesink->file); - multifilesink->file = NULL; - - filename = g_strdup_printf (multifilesink->filename, - multifilesink->index); - gst_multi_file_sink_post_message (multifilesink, buffer, filename); - g_free (filename); - multifilesink->index++; - } + if (multifilesink->file) + gst_multi_file_sink_close_file (multifilesink, buffer); multifilesink->next_segment += 10 * GST_SECOND; } @@ -476,12 +459,7 @@ gst_multi_file_sink_render (GstBaseSink * sink, GstBuffer * buffer) if (multifilesink->file == NULL) { int i; - filename = g_strdup_printf (multifilesink->filename, - multifilesink->index); - multifilesink->file = g_fopen (filename, "wb"); - g_free (filename); - - if (multifilesink->file == NULL) + if (!gst_multi_file_sink_open_next_file (multifilesink)) goto stdio_write_error; if (multifilesink->streamheaders) { @@ -571,3 +549,38 @@ gst_multi_file_sink_set_caps (GstBaseSink * sink, GstCaps * caps) return TRUE; } + +static gboolean +gst_multi_file_sink_open_next_file (GstMultiFileSink * multifilesink) +{ + char *filename; + + g_return_val_if_fail (multifilesink->file == NULL, FALSE); + + filename = g_strdup_printf (multifilesink->filename, multifilesink->index); + multifilesink->file = g_fopen (filename, "wb"); + g_free (filename); + + if (multifilesink->file == NULL) + return FALSE; + + return TRUE; +} + +static void +gst_multi_file_sink_close_file (GstMultiFileSink * multifilesink, + GstBuffer * buffer) +{ + char *filename; + + fclose (multifilesink->file); + multifilesink->file = NULL; + + if (buffer) { + filename = g_strdup_printf (multifilesink->filename, multifilesink->index); + gst_multi_file_sink_post_message (multifilesink, buffer, filename); + g_free (filename); + } + + multifilesink->index++; +} From 072bd74cc4c1da524d89c5767efc5e698d878aec Mon Sep 17 00:00:00 2001 From: Alessandro Decina Date: Mon, 18 Jul 2011 09:38:26 +0200 Subject: [PATCH 04/32] multifilesink: add max-files property Add max-files property to limit the number of files saved on disk. API: multifilesink::max-files --- gst/multifile/gstmultifilesink.c | 62 +++++++++++++++++++++++++++++--- gst/multifile/gstmultifilesink.h | 3 ++ tests/check/elements/multifile.c | 56 +++++++++++++++++++++++++++-- 3 files changed, 114 insertions(+), 7 deletions(-) diff --git a/gst/multifile/gstmultifilesink.c b/gst/multifile/gstmultifilesink.c index 0ef5fd9003..d6fbec59ca 100644 --- a/gst/multifile/gstmultifilesink.c +++ b/gst/multifile/gstmultifilesink.c @@ -125,6 +125,7 @@ GST_DEBUG_CATEGORY_STATIC (gst_multi_file_sink_debug); #define DEFAULT_INDEX 0 #define DEFAULT_POST_MESSAGES FALSE #define DEFAULT_NEXT_FILE GST_MULTI_FILE_SINK_NEXT_BUFFER +#define DEFAULT_MAX_FILES 0 enum { @@ -133,6 +134,7 @@ enum PROP_INDEX, PROP_POST_MESSAGES, PROP_NEXT_FILE, + PROP_MAX_FILES, PROP_LAST }; @@ -152,6 +154,8 @@ static gboolean gst_multi_file_sink_open_next_file (GstMultiFileSink * multifilesink); static void gst_multi_file_sink_close_file (GstMultiFileSink * multifilesink, GstBuffer * buffer); +static void gst_multi_file_sink_ensure_max_files (GstMultiFileSink * + multifilesink); #define GST_TYPE_MULTI_FILE_SINK_NEXT (gst_multi_file_sink_next_get_type ()) static GType @@ -238,6 +242,22 @@ gst_multi_file_sink_class_init (GstMultiFileSinkClass * klass) GST_TYPE_MULTI_FILE_SINK_NEXT, DEFAULT_NEXT_FILE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS)); + + /** + * GstMultiFileSink:max-files + * + * Maximum number of files to keep on disk. Once the maximum is reached, old + * files start to be deleted to make room for new ones. + * + * Since: 0.10.31 + */ + g_object_class_install_property (gobject_class, PROP_MAX_FILES, + g_param_spec_uint ("max-files", "Max files", + "Maximum number of files to keep on disk. Once the maximum is reached," + "old files start to be deleted to make room for new ones.", + 0, G_MAXUINT, DEFAULT_MAX_FILES, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gobject_class->finalize = gst_multi_file_sink_finalize; gstbasesink_class->get_times = NULL; @@ -254,6 +274,9 @@ gst_multi_file_sink_init (GstMultiFileSink * multifilesink, multifilesink->filename = g_strdup (DEFAULT_LOCATION); multifilesink->index = DEFAULT_INDEX; multifilesink->post_messages = DEFAULT_POST_MESSAGES; + multifilesink->max_files = DEFAULT_MAX_FILES; + multifilesink->files = NULL; + multifilesink->n_files = 0; gst_base_sink_set_sync (GST_BASE_SINK (multifilesink), FALSE); @@ -266,6 +289,7 @@ gst_multi_file_sink_finalize (GObject * object) GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object); g_free (sink->filename); + g_slist_free_full (sink->files, (GDestroyNotify) g_free); G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -300,6 +324,9 @@ gst_multi_file_sink_set_property (GObject * object, guint prop_id, case PROP_NEXT_FILE: sink->next_file = g_value_get_enum (value); break; + case PROP_MAX_FILES: + sink->max_files = g_value_get_uint (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -325,6 +352,9 @@ gst_multi_file_sink_get_property (GObject * object, guint prop_id, case PROP_NEXT_FILE: g_value_set_enum (value, sink->next_file); break; + case PROP_MAX_FILES: + g_value_set_uint (value, sink->max_files); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -410,17 +440,20 @@ gst_multi_file_sink_render (GstBaseSink * sink, GstBuffer * buffer) switch (multifilesink->next_file) { case GST_MULTI_FILE_SINK_NEXT_BUFFER: + gst_multi_file_sink_ensure_max_files (multifilesink); + filename = g_strdup_printf (multifilesink->filename, multifilesink->index); - ret = g_file_set_contents (filename, (char *) data, size, &error); if (!ret) goto write_error; + multifilesink->files = g_slist_append (multifilesink->files, filename); + multifilesink->n_files += 1; + gst_multi_file_sink_post_message (multifilesink, buffer, filename); multifilesink->index++; - g_free (filename); break; case GST_MULTI_FILE_SINK_NEXT_DISCONT: if (GST_BUFFER_IS_DISCONT (buffer)) { @@ -550,6 +583,21 @@ gst_multi_file_sink_set_caps (GstBaseSink * sink, GstCaps * caps) return TRUE; } +static void +gst_multi_file_sink_ensure_max_files (GstMultiFileSink * multifilesink) +{ + char *filename; + + while (multifilesink->max_files && + multifilesink->n_files >= multifilesink->max_files) { + filename = multifilesink->files->data; + g_remove (filename); + multifilesink->files = g_slist_delete_link (multifilesink->files, + multifilesink->files); + multifilesink->n_files -= 1; + } +} + static gboolean gst_multi_file_sink_open_next_file (GstMultiFileSink * multifilesink) { @@ -557,12 +605,16 @@ gst_multi_file_sink_open_next_file (GstMultiFileSink * multifilesink) g_return_val_if_fail (multifilesink->file == NULL, FALSE); + gst_multi_file_sink_ensure_max_files (multifilesink); filename = g_strdup_printf (multifilesink->filename, multifilesink->index); multifilesink->file = g_fopen (filename, "wb"); - g_free (filename); - - if (multifilesink->file == NULL) + if (multifilesink->file == NULL) { + g_free (filename); return FALSE; + } + + multifilesink->files = g_slist_append (multifilesink->files, filename); + multifilesink->n_files += 1; return TRUE; } diff --git a/gst/multifile/gstmultifilesink.h b/gst/multifile/gstmultifilesink.h index c5515706a8..f2f005ff57 100644 --- a/gst/multifile/gstmultifilesink.h +++ b/gst/multifile/gstmultifilesink.h @@ -67,6 +67,9 @@ struct _GstMultiFileSink gboolean post_messages; GstMultiFileSinkNext next_file; FILE *file; + guint max_files; + GSList *files; + guint n_files; gint64 next_segment; diff --git a/tests/check/elements/multifile.c b/tests/check/elements/multifile.c index 6fe527effb..479e028b66 100644 --- a/tests/check/elements/multifile.c +++ b/tests/check/elements/multifile.c @@ -54,7 +54,7 @@ g_mkdtemp (const gchar * template) return tmpdir; } -GST_START_TEST (test_multifilesink) +GST_START_TEST (test_multifilesink_key_frame) { GstElement *pipeline; GstElement *mfs; @@ -98,6 +98,57 @@ GST_START_TEST (test_multifilesink) GST_END_TEST; +GST_START_TEST (test_multifilesink_max_files) +{ + GstElement *pipeline; + GstElement *mfs; + int i; + const gchar *tmpdir; + gchar *my_tmpdir; + gchar *template; + gchar *mfs_pattern; + + tmpdir = g_get_tmp_dir (); + template = g_build_filename (tmpdir, "multifile-test-XXXXXX", NULL); + my_tmpdir = g_mkdtemp (template); + fail_if (my_tmpdir == NULL); + + pipeline = + gst_parse_launch + ("videotestsrc num-buffers=10 ! video/x-raw-yuv,format=(fourcc)I420,width=320,height=240 ! multifilesink name=mfs", + NULL); + fail_if (pipeline == NULL); + mfs = gst_bin_get_by_name (GST_BIN (pipeline), "mfs"); + fail_if (mfs == NULL); + mfs_pattern = g_build_filename (my_tmpdir, "%05d", NULL); + g_object_set (G_OBJECT (mfs), "location", mfs_pattern, "max-files", 3, NULL); + g_object_unref (mfs); + run_pipeline (pipeline); + gst_object_unref (pipeline); + + for (i = 0; i < 7; i++) { + char *s; + + s = g_strdup_printf (mfs_pattern, i); + fail_unless (g_remove (s) != 0); + g_free (s); + } + for (i = 7; i < 10; i++) { + char *s; + + s = g_strdup_printf (mfs_pattern, i); + fail_if (g_remove (s) != 0); + g_free (s); + } + fail_if (g_remove (my_tmpdir) != 0); + + g_free (mfs_pattern); + g_free (my_tmpdir); + g_free (template); +} + +GST_END_TEST; + GST_START_TEST (test_multifilesrc) { GstElement *pipeline; @@ -164,7 +215,8 @@ libvisual_suite (void) suite_add_tcase (s, tc_chain); - tcase_add_test (tc_chain, test_multifilesink); + tcase_add_test (tc_chain, test_multifilesink_key_frame); + tcase_add_test (tc_chain, test_multifilesink_max_files); tcase_add_test (tc_chain, test_multifilesrc); return s; From 6c0aec783a7ab7a0004f2c89b83e7dc8b33bfcc7 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Mon, 18 Jul 2011 14:30:51 +0200 Subject: [PATCH 05/32] multifilesink: do not use g_slist_free_full ... as that is only in GLib 2.28, which is not yet required at this time. --- gst/multifile/gstmultifilesink.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gst/multifile/gstmultifilesink.c b/gst/multifile/gstmultifilesink.c index d6fbec59ca..53db5366d1 100644 --- a/gst/multifile/gstmultifilesink.c +++ b/gst/multifile/gstmultifilesink.c @@ -289,7 +289,8 @@ gst_multi_file_sink_finalize (GObject * object) GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object); g_free (sink->filename); - g_slist_free_full (sink->files, (GDestroyNotify) g_free); + g_slist_foreach (sink->files, (GFunc) g_free, NULL); + g_slist_free (sink->files); G_OBJECT_CLASS (parent_class)->finalize (object); } From 471904032da5194024c125ddf25cc81c2dce649a Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Mon, 18 Jul 2011 14:24:48 +0200 Subject: [PATCH 06/32] rtph264depay: reset upon FLUSH_STOP ... which is particularly needed when merging NAL units, where not resetting would lead to output of an older (pre-flush) AU (with unintended timestamp). --- gst/rtp/gstrtph264depay.c | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/gst/rtp/gstrtph264depay.c b/gst/rtp/gstrtph264depay.c index 0d62628384..b91b9ea97d 100644 --- a/gst/rtp/gstrtph264depay.c +++ b/gst/rtp/gstrtph264depay.c @@ -95,6 +95,8 @@ static GstBuffer *gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf); static gboolean gst_rtp_h264_depay_setcaps (GstBaseRTPDepayload * filter, GstCaps * caps); +static gboolean gst_rtp_h264_depay_handle_event (GstBaseRTPDepayload * depay, + GstEvent * event); static void gst_rtp_h264_depay_base_init (gpointer klass) @@ -141,6 +143,7 @@ gst_rtp_h264_depay_class_init (GstRtpH264DepayClass * klass) gstbasertpdepayload_class->process = gst_rtp_h264_depay_process; gstbasertpdepayload_class->set_caps = gst_rtp_h264_depay_setcaps; + gstbasertpdepayload_class->handle_event = gst_rtp_h264_depay_handle_event; GST_DEBUG_CATEGORY_INIT (rtph264depay_debug, "rtph264depay", 0, "H264 Video RTP Depayloader"); @@ -156,6 +159,17 @@ gst_rtp_h264_depay_init (GstRtpH264Depay * rtph264depay, rtph264depay->merge = DEFAULT_ACCESS_UNIT; } +static void +gst_rtp_h264_depay_reset (GstRtpH264Depay * rtph264depay) +{ + gst_adapter_clear (rtph264depay->adapter); + rtph264depay->wait_start = TRUE; + gst_adapter_clear (rtph264depay->picture_adapter); + rtph264depay->picture_start = FALSE; + rtph264depay->last_keyframe = FALSE; + rtph264depay->last_ts = 0; +} + static void gst_rtp_h264_depay_finalize (GObject * object) { @@ -835,6 +849,25 @@ not_implemented: } } +static gboolean +gst_rtp_h264_depay_handle_event (GstBaseRTPDepayload * depay, GstEvent * event) +{ + GstRtpH264Depay *rtph264depay; + + rtph264depay = GST_RTP_H264_DEPAY (depay); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + gst_rtp_h264_depay_reset (rtph264depay); + break; + default: + break; + } + + return + GST_BASE_RTP_DEPAYLOAD_CLASS (parent_class)->handle_event (depay, event); +} + static GstStateChangeReturn gst_rtp_h264_depay_change_state (GstElement * element, GstStateChange transition) @@ -848,11 +881,7 @@ gst_rtp_h264_depay_change_state (GstElement * element, case GST_STATE_CHANGE_NULL_TO_READY: break; case GST_STATE_CHANGE_READY_TO_PAUSED: - gst_adapter_clear (rtph264depay->adapter); - rtph264depay->wait_start = TRUE; - gst_adapter_clear (rtph264depay->picture_adapter); - rtph264depay->picture_start = FALSE; - rtph264depay->last_keyframe = FALSE; + gst_rtp_h264_depay_reset (rtph264depay); break; default: break; From 14b9fb7be62e0234ad567fcb8c83277f4e1f4215 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Mon, 18 Jul 2011 15:13:33 -0300 Subject: [PATCH 07/32] pulsesrc: Fix default value leaking Remember to free the default value of client name, avoiding a leak --- ext/pulse/pulsesrc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ext/pulse/pulsesrc.c b/ext/pulse/pulsesrc.c index 9c078fba75..f3f63a54fa 100644 --- a/ext/pulse/pulsesrc.c +++ b/ext/pulse/pulsesrc.c @@ -211,6 +211,7 @@ gst_pulsesrc_class_init (GstPulseSrcClass * klass) GstAudioSrcClass *gstaudiosrc_class = GST_AUDIO_SRC_CLASS (klass); GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass); GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + gchar *clientname; gobject_class->finalize = gst_pulsesrc_finalize; gobject_class->set_property = gst_pulsesrc_set_property; @@ -247,6 +248,7 @@ gst_pulsesrc_class_init (GstPulseSrcClass * klass) "Human-readable name of the sound device", DEFAULT_DEVICE_NAME, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + clientname = gst_pulse_client_name (); /** * GstPulseSink:client * @@ -257,9 +259,10 @@ gst_pulsesrc_class_init (GstPulseSrcClass * klass) g_object_class_install_property (gobject_class, PROP_CLIENT, g_param_spec_string ("client", "Client", - "The PulseAudio client_name_to_use", gst_pulse_client_name (), + "The PulseAudio client_name_to_use", clientname, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY)); + g_free (clientname); /** * GstPulseSrc:stream-properties From 1880c4145ec213b9bf2680145eed2344369c5edc Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Tue, 19 Jul 2011 12:05:51 +0200 Subject: [PATCH 08/32] auparse: avoid hanging on invalid short input ... as in such case there is no srcpad yet on which to forward EOS. --- gst/auparse/gstauparse.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gst/auparse/gstauparse.c b/gst/auparse/gstauparse.c index 69e5273192..d5a555536b 100644 --- a/gst/auparse/gstauparse.c +++ b/gst/auparse/gstauparse.c @@ -749,6 +749,12 @@ gst_au_parse_sink_event (GstPad * pad, GstEvent * event) gst_event_unref (event); break; } + case GST_EVENT_EOS: + if (!auparse->srcpad) { + GST_ELEMENT_ERROR (auparse, STREAM, WRONG_TYPE, + ("No valid input found before end of stream"), (NULL)); + } + /* fall-through */ default: ret = gst_pad_event_default (pad, event); break; From 216dc602c3ab3d73c96634c974c658240974d575 Mon Sep 17 00:00:00 2001 From: Alessandro Decina Date: Wed, 20 Jul 2011 08:52:58 +0200 Subject: [PATCH 09/32] multipart: fix compiler warning --- gst/multipart/multipartmux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/multipart/multipartmux.c b/gst/multipart/multipartmux.c index 9f69975f56..77af23ee7a 100644 --- a/gst/multipart/multipartmux.c +++ b/gst/multipart/multipartmux.c @@ -551,7 +551,7 @@ gst_multipart_mux_collected (GstCollectPads * pads, GstMultipartMux * mux) GST_BUFFER_OFFSET_END (footerbuf) = mux->offset; GST_BUFFER_FLAG_SET (footerbuf, GST_BUFFER_FLAG_DELTA_UNIT); - GST_DEBUG_OBJECT (mux, "pushing %" G_GSIZE_FORMAT " bytes footer buffer", 2); + GST_DEBUG_OBJECT (mux, "pushing 2 bytes footer buffer"); ret = gst_pad_push (mux->srcpad, footerbuf); beach: From 118a7cc36a0fb6b289927f199c78441a165fbae3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Mon, 18 Jul 2011 20:27:38 -0400 Subject: [PATCH 10/32] rtph264pay: Only set the marker bit on the last NALU of a multi-NALU access unit An access unit could contain multiple NAL units, in that case, only the last RTP packet of the last NALU should have its marker bit set. https://bugzilla.gnome.org/show_bug.cgi?id=654850 --- gst/rtp/gstrtph264pay.c | 44 +++++++++++++++++++++++++++++++++-------- gst/rtp/gstrtph264pay.h | 1 + 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/gst/rtp/gstrtph264pay.c b/gst/rtp/gstrtph264pay.c index 5f574a489a..738e938805 100644 --- a/gst/rtp/gstrtph264pay.c +++ b/gst/rtp/gstrtph264pay.c @@ -420,6 +420,7 @@ gst_rtp_h264_pay_setcaps (GstBaseRTPPayload * basepayload, GstCaps * caps) const GValue *value; guint8 *data; guint size; + const gchar *alignment; rtph264pay = GST_RTP_H264_PAY (basepayload); @@ -429,6 +430,12 @@ gst_rtp_h264_pay_setcaps (GstBaseRTPPayload * basepayload, GstCaps * caps) * NALs */ gst_basertppayload_set_options (basepayload, "video", TRUE, "H264", 90000); + alignment = gst_structure_get_string (str, "alignment"); + if (alignment && !strcmp (alignment, "au")) + rtph264pay->au_alignment = TRUE; + else + rtph264pay->au_alignment = FALSE; + /* packetized AVC video has a codec_data */ if ((value = gst_structure_get_value (str, "codec_data"))) { GstBuffer *buffer; @@ -763,7 +770,7 @@ gst_rtp_h264_pay_decode_nal (GstRtpH264Pay * payloader, static GstFlowReturn gst_rtp_h264_pay_payload_nal (GstBaseRTPPayload * basepayload, const guint8 * data, guint size, GstClockTime timestamp, - GstBuffer * buffer_orig); + GstBuffer * buffer_orig, gboolean end_of_au); static GstFlowReturn gst_rtp_h264_pay_send_sps_pps (GstBaseRTPPayload * basepayload, @@ -779,7 +786,7 @@ gst_rtp_h264_pay_send_sps_pps (GstBaseRTPPayload * basepayload, /* resend SPS */ ret = gst_rtp_h264_pay_payload_nal (basepayload, GST_BUFFER_DATA (sps_buf), GST_BUFFER_SIZE (sps_buf), timestamp, - sps_buf); + sps_buf, FALSE); /* Not critical here; but throw a warning */ if (ret != GST_FLOW_OK) GST_WARNING ("Problem pushing SPS"); @@ -791,7 +798,7 @@ gst_rtp_h264_pay_send_sps_pps (GstBaseRTPPayload * basepayload, /* resend PPS */ ret = gst_rtp_h264_pay_payload_nal (basepayload, GST_BUFFER_DATA (pps_buf), GST_BUFFER_SIZE (pps_buf), timestamp, - pps_buf); + pps_buf, FALSE); /* Not critical here; but throw a warning */ if (ret != GST_FLOW_OK) GST_WARNING ("Problem pushing PPS"); @@ -806,7 +813,7 @@ gst_rtp_h264_pay_send_sps_pps (GstBaseRTPPayload * basepayload, static GstFlowReturn gst_rtp_h264_pay_payload_nal (GstBaseRTPPayload * basepayload, const guint8 * data, guint size, GstClockTime timestamp, - GstBuffer * buffer_orig) + GstBuffer * buffer_orig, gboolean end_of_au) { GstRtpH264Pay *rtph264pay; GstFlowReturn ret; @@ -885,7 +892,7 @@ gst_rtp_h264_pay_payload_nal (GstBaseRTPPayload * basepayload, } /* only set the marker bit on packets containing access units */ - if (IS_ACCESS_UNIT (nalType)) { + if (IS_ACCESS_UNIT (nalType) && end_of_au) { gst_rtp_buffer_set_marker (outbuf, 1); } @@ -975,7 +982,7 @@ gst_rtp_h264_pay_payload_nal (GstBaseRTPPayload * basepayload, end = 1; } if (IS_ACCESS_UNIT (nalType)) { - gst_rtp_buffer_set_marker (outbuf, end); + gst_rtp_buffer_set_marker (outbuf, end && end_of_au); } /* FU indicator */ @@ -1075,6 +1082,7 @@ gst_rtp_h264_pay_handle_buffer (GstBaseRTPPayload * basepayload, while (size > nal_length_size) { gint i; + gboolean end_of_au = FALSE; nal_len = 0; for (i = 0; i < nal_length_size; i++) { @@ -1093,9 +1101,16 @@ gst_rtp_h264_pay_handle_buffer (GstBaseRTPPayload * basepayload, nal_len); } + /* If we're at the end of the buffer, then we're at the end of the + * access unit + */ + if (rtph264pay->au_alignment && size - nal_len <= nal_length_size) { + end_of_au = TRUE; + } + ret = gst_rtp_h264_pay_payload_nal (basepayload, data, nal_len, timestamp, - buffer); + buffer, end_of_au); if (ret != GST_FLOW_OK) break; @@ -1197,6 +1212,7 @@ gst_rtp_h264_pay_handle_buffer (GstBaseRTPPayload * basepayload, for (i = 0; i < nal_queue->len; i++) { guint size; + gboolean end_of_au = FALSE; nal_len = g_array_index (nal_queue, guint, i); /* skip start code */ @@ -1212,10 +1228,22 @@ gst_rtp_h264_pay_handle_buffer (GstBaseRTPPayload * basepayload, for (; size > 1 && data[size - 1] == 0x0; size--) /* skip */ ; + /* If it's the last nal unit we have in non-bytestream mode, we can + * assume it's the end of an access-unit + * + * FIXME: We need to wait until the next packet or EOS to + * actually payload the NAL so we can know if the current NAL is + * the last one of an access unit or not if we are in bytestream mode + */ + if (rtph264pay->au_alignment && + rtph264pay->scan_mode != GST_H264_SCAN_MODE_BYTESTREAM && + i == nal_queue->len - 1) + end_of_au = TRUE; + /* put the data in one or more RTP packets */ ret = gst_rtp_h264_pay_payload_nal (basepayload, data, size, timestamp, - buffer); + buffer, end_of_au); if (ret != GST_FLOW_OK) { break; } diff --git a/gst/rtp/gstrtph264pay.h b/gst/rtp/gstrtph264pay.h index 19e5aaa269..6303e308a9 100644 --- a/gst/rtp/gstrtph264pay.h +++ b/gst/rtp/gstrtph264pay.h @@ -55,6 +55,7 @@ struct _GstRtpH264Pay GList *sps, *pps; gboolean packetized; + gboolean au_alignment; guint nal_length_size; GArray *queue; From 2591a882aed16fb7e8ed175af38e1ecc4e6056c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Mon, 18 Jul 2011 16:46:27 -0400 Subject: [PATCH 11/32] rtph264depay: Complete merged AU on marker bit The marker bit on a RTP packet means the AU has been completed, so push it out immediately to reduce the latency. https://bugzilla.gnome.org/show_bug.cgi?id=654850 --- gst/rtp/gstrtph264depay.c | 53 ++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/gst/rtp/gstrtph264depay.c b/gst/rtp/gstrtph264depay.c index b91b9ea97d..79acf0ee6d 100644 --- a/gst/rtp/gstrtph264depay.c +++ b/gst/rtp/gstrtph264depay.c @@ -480,13 +480,34 @@ incomplete_caps: } } +static GstBuffer * +gst_rtp_h264_complete_au (GstRtpH264Depay * rtph264depay, + GstClockTime * out_timestamp, gboolean * out_keyframe) +{ + guint outsize; + GstBuffer *outbuf; + + /* we had a picture in the adapter and we completed it */ + GST_DEBUG_OBJECT (rtph264depay, "taking completed AU"); + outsize = gst_adapter_available (rtph264depay->picture_adapter); + outbuf = gst_adapter_take_buffer (rtph264depay->picture_adapter, outsize); + + *out_timestamp = rtph264depay->last_ts; + *out_keyframe = rtph264depay->last_keyframe; + + rtph264depay->last_keyframe = FALSE; + rtph264depay->picture_start = FALSE; + + return outbuf; +} + /* SPS/PPS/IDR considered key, all others DELTA; * 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 gst_rtp_h264_depay_handle_nal (GstRtpH264Depay * rtph264depay, GstBuffer * nal, - GstClockTime in_timestamp) + GstClockTime in_timestamp, gboolean marker) { GstBaseRTPDepayload *depayload = GST_BASE_RTP_DEPAYLOAD (rtph264depay); gint nal_type; @@ -532,20 +553,9 @@ gst_rtp_h264_depay_handle_nal (GstRtpH264Depay * rtph264depay, GstBuffer * nal, } GST_DEBUG_OBJECT (depayload, "start %d, complete %d", start, complete); - if (complete && rtph264depay->picture_start) { - guint outsize; - - /* we had a picture in the adapter and we completed it */ - GST_DEBUG_OBJECT (depayload, "taking completed AU"); - outsize = gst_adapter_available (rtph264depay->picture_adapter); - outbuf = gst_adapter_take_buffer (rtph264depay->picture_adapter, outsize); - - out_timestamp = rtph264depay->last_ts; - out_keyframe = rtph264depay->last_keyframe; - - rtph264depay->last_keyframe = FALSE; - rtph264depay->picture_start = FALSE; - } + if (complete && rtph264depay->picture_start) + outbuf = gst_rtp_h264_complete_au (rtph264depay, &out_timestamp, + &out_keyframe); /* add to adapter */ GST_DEBUG_OBJECT (depayload, "adding NAL to picture adapter"); @@ -553,6 +563,10 @@ gst_rtp_h264_depay_handle_nal (GstRtpH264Depay * rtph264depay, GstBuffer * nal, rtph264depay->last_ts = in_timestamp; rtph264depay->last_keyframe |= keyframe; rtph264depay->picture_start |= start; + + if (marker) + outbuf = gst_rtp_h264_complete_au (rtph264depay, &out_timestamp, + &out_keyframe); } else { /* no merge, output is input nal */ GST_DEBUG_OBJECT (depayload, "using NAL as output"); @@ -615,11 +629,13 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) guint8 *outdata; guint outsize, nalu_size; GstClockTime timestamp; + gboolean marker; timestamp = GST_BUFFER_TIMESTAMP (buf); payload_len = gst_rtp_buffer_get_payload_len (buf); payload = gst_rtp_buffer_get_payload (buf); + marker = gst_rtp_buffer_get_marker (buf); GST_DEBUG_OBJECT (rtph264depay, "receiving %d bytes", payload_len); @@ -703,7 +719,7 @@ 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); + gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp, marker); break; } case 26: @@ -797,7 +813,8 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) outdata[2] = (outsize >> 8); outdata[3] = (outsize); } - gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp); + gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp, + marker); } break; } @@ -821,7 +838,7 @@ 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); + gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp, marker); break; } } From 9764b57b0a0ea169dbd894e0d81894cd1d0b2e61 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Tue, 19 Jul 2011 13:38:01 +0200 Subject: [PATCH 12/32] rtspsrc: set SOURCE flag at init time Fixes #654816. --- gst/rtsp/gstrtspsrc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gst/rtsp/gstrtspsrc.c b/gst/rtsp/gstrtspsrc.c index 13f23b3dc9..7b1a9d7780 100644 --- a/gst/rtsp/gstrtspsrc.c +++ b/gst/rtsp/gstrtspsrc.c @@ -528,6 +528,8 @@ gst_rtspsrc_init (GstRTSPSrc * src, GstRTSPSrcClass * g_class) g_static_rec_mutex_init (src->state_rec_lock); src->state = GST_RTSP_STATE_INVALID; + + GST_OBJECT_FLAG_SET (src, GST_ELEMENT_IS_SOURCE); } static void @@ -5491,7 +5493,6 @@ gst_rtspsrc_open_from_sdp (GstRTSPSrc * src, GstSDPMessage * sdp, } src->state = GST_RTSP_STATE_INIT; - GST_OBJECT_FLAG_SET (src, GST_ELEMENT_IS_SOURCE); /* setup streams */ if ((res = gst_rtspsrc_setup_streams (src, async)) < 0) From 4d48109f9d5da9106130485d37841bbf3423cd6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Mon, 25 Apr 2011 16:13:38 -0400 Subject: [PATCH 13/32] rtpsession: Wait longer to timeout SSRC collision Using the current RTCP interval to timeout SSRC collision can lead to collisions being timed out immediately if a BYE packet is sent because it is sent immediately, so the interval is 0. This is not what we want. So just set a static 10 times the default RTCP interval, it should be enough https://bugzilla.gnome.org/show_bug.cgi?id=648642 --- gst/rtpmanager/rtpsession.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gst/rtpmanager/rtpsession.c b/gst/rtpmanager/rtpsession.c index bdd261f65f..541a3aebc4 100644 --- a/gst/rtpmanager/rtpsession.c +++ b/gst/rtpmanager/rtpsession.c @@ -3047,7 +3047,8 @@ rtp_session_on_timeout (RTPSession * sess, GstClockTime current_time, /* check for outdated collisions */ GST_DEBUG ("Timing out collisions"); rtp_source_timeout (sess->source, current_time, - data.interval * RTCP_INTERVAL_COLLISION_TIMEOUT, + /* "a relatively long time" -- RFC 3550 section 8.2 */ + RTP_STATS_MIN_INTERVAL * GST_SECOND * 10, running_time - sess->rtcp_feedback_retention_window); if (sess->change_ssrc) { From 354faabda02f84d68ae24fe3d96242e8a242f456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Wed, 8 Jun 2011 14:48:01 -0400 Subject: [PATCH 14/32] rtpsession: Don't let the computed RTP bandwidth fall too low If it falls too low, the computed RTCP bandwidth will be near zero and the RTCP thread will be stopped. https://bugzilla.gnome.org/show_bug.cgi?id=654583 --- gst/rtpmanager/rtpsession.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/rtpmanager/rtpsession.c b/gst/rtpmanager/rtpsession.c index 541a3aebc4..1dcc0bdc13 100644 --- a/gst/rtpmanager/rtpsession.c +++ b/gst/rtpmanager/rtpsession.c @@ -2444,7 +2444,7 @@ calculate_rtcp_interval (RTPSession * sess, gboolean deterministic, g_hash_table_foreach (sess->cnames, (GHFunc) add_bitrates, &bandwidth); bandwidth /= 8.0; } - if (bandwidth == 0) + if (bandwidth < 8000) bandwidth = RTP_STATS_BANDWIDTH; rtp_stats_set_bandwidths (&sess->stats, bandwidth, From 6095d2a3f00e1cb14736648fed42751b5f8a7832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Wed, 8 Jun 2011 15:57:37 -0400 Subject: [PATCH 15/32] rtpsession: Always send application requested feedback in immediate mode Send as many application requested feedback messages in immediate mode, even if they have already been sent. https://bugzilla.gnome.org/show_bug.cgi?id=654583 --- gst/rtpmanager/rtpsession.c | 27 +++++++++++++++++++++++++-- gst/rtpmanager/rtpsession.h | 1 + 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/gst/rtpmanager/rtpsession.c b/gst/rtpmanager/rtpsession.c index 1dcc0bdc13..26eae5d769 100644 --- a/gst/rtpmanager/rtpsession.c +++ b/gst/rtpmanager/rtpsession.c @@ -60,6 +60,7 @@ enum #define DEFAULT_SOURCES NULL #define DEFAULT_RTCP_MIN_INTERVAL (RTP_STATS_MIN_INTERVAL * GST_SECOND) #define DEFAULT_RTCP_FEEDBACK_RETENTION_WINDOW (2 * GST_SECOND) +#define DEFAULT_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD (3) enum { @@ -78,6 +79,7 @@ enum PROP_FAVOR_NEW, PROP_RTCP_MIN_INTERVAL, PROP_RTCP_FEEDBACK_RETENTION_WINDOW, + PROP_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD, PROP_LAST }; @@ -492,6 +494,14 @@ rtp_session_class_init (RTPSessionClass * klass) 0, G_MAXUINT64, DEFAULT_RTCP_FEEDBACK_RETENTION_WINDOW, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, + PROP_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD, + g_param_spec_uint ("rtcp-immediate-feedback-threshold", + "RTCP Immediate Feedback threshold", + "The maximum number of members of a RTP session for which immediate" + " feedback is used", + 0, G_MAXUINT, DEFAULT_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); klass->get_source_by_ssrc = GST_DEBUG_FUNCPTR (rtp_session_get_source_by_ssrc); @@ -550,6 +560,8 @@ rtp_session_init (RTPSession * sess) sess->first_rtcp = TRUE; sess->allow_early = TRUE; sess->rtcp_feedback_retention_window = DEFAULT_RTCP_FEEDBACK_RETENTION_WINDOW; + sess->rtcp_immediate_feedback_threshold = + DEFAULT_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD; sess->rtcp_pli_requests = g_array_new (FALSE, FALSE, sizeof (guint32)); @@ -649,6 +661,9 @@ rtp_session_set_property (GObject * object, guint prop_id, rtp_stats_set_min_interval (&sess->stats, (gdouble) g_value_get_uint64 (value) / GST_SECOND); break; + case PROP_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD: + sess->rtcp_immediate_feedback_threshold = g_value_get_uint (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -703,6 +718,9 @@ rtp_session_get_property (GObject * object, guint prop_id, case PROP_RTCP_MIN_INTERVAL: g_value_set_uint64 (value, sess->stats.min_interval * GST_SECOND); break; + case PROP_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD: + g_value_set_uint (value, sess->rtcp_immediate_feedback_threshold); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -3143,8 +3161,13 @@ rtp_session_request_early_rtcp (RTPSession * sess, GstClockTime current_time, if (current_time + T_dither_max > sess->next_rtcp_check_time) goto dont_send; - /* RFC 4585 section 3.5.2 step 4 */ - if (sess->allow_early == FALSE) + /* RFC 4585 section 3.5.2 step 4 + * Don't send if allow_early is FALSE, but not if we are in + * immediate mode, meaning we are part of a group of at most the + * application-specific threshold. + */ + if (sess->total_sources > sess->rtcp_immediate_feedback_threshold && + sess->allow_early == FALSE) goto dont_send; if (T_dither_max) { diff --git a/gst/rtpmanager/rtpsession.h b/gst/rtpmanager/rtpsession.h index 93fd300d95..30b74bb5db 100644 --- a/gst/rtpmanager/rtpsession.h +++ b/gst/rtpmanager/rtpsession.h @@ -232,6 +232,7 @@ struct _RTPSession { gboolean change_ssrc; gboolean favor_new; GstClockTime rtcp_feedback_retention_window; + guint rtcp_immediate_feedback_threshold; GArray *rtcp_pli_requests; GstClockTime last_keyframe_request; From 96972eb462cc672d73c82cb2312f2116df1ced7c Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Sat, 9 Apr 2011 12:26:56 +0530 Subject: [PATCH 16/32] ac3parse: Add support for IEC 61937 alignment When pushing out buffers over S/PDIF or HDMI, IEC 61937 payloading requires each buffer to contain 6 blocks from each substream. This adds code to collect all the frames needed to meet this requirement before pushing out a buffer. https://bugzilla.gnome.org/show_bug.cgi?id=650313 --- gst/audioparsers/gstac3parse.c | 126 +++++++++++++++++++++++++++++--- gst/audioparsers/gstac3parse.h | 7 ++ tests/check/elements/ac3parse.c | 2 +- 3 files changed, 122 insertions(+), 13 deletions(-) diff --git a/gst/audioparsers/gstac3parse.c b/gst/audioparsers/gstac3parse.c index 3d20aa8642..2ff8fa310f 100644 --- a/gst/audioparsers/gstac3parse.c +++ b/gst/audioparsers/gstac3parse.c @@ -206,6 +206,7 @@ gst_ac3_parse_reset (GstAc3Parse * ac3parse) ac3parse->sample_rate = -1; ac3parse->blocks = -1; ac3parse->eac = FALSE; + ac3parse->align = GST_AC3_PARSE_ALIGN_NONE; } static void @@ -241,15 +242,61 @@ gst_ac3_parse_stop (GstBaseParse * parse) return TRUE; } +static void +gst_ac3_parse_set_alignment (GstAc3Parse * ac3parse, gboolean eac) +{ + GstCaps *caps; + GstStructure *st; + const gchar *str = NULL; + int i; + + if (G_LIKELY (!eac)) + goto done; + + caps = gst_pad_get_allowed_caps (GST_BASE_PARSE_SRC_PAD (ac3parse)); + + if (!caps) + goto done; + + for (i = 0; i < gst_caps_get_size (caps); i++) { + st = gst_caps_get_structure (caps, i); + + if (!g_str_equal (gst_structure_get_name (st), "audio/x-eac3")) + continue; + + if ((str = gst_structure_get_string (st, "alignment"))) { + if (strcmp (str, "iec61937") == 0) { + ac3parse->align = GST_AC3_PARSE_ALIGN_IEC61937; + GST_DEBUG_OBJECT (ac3parse, "picked iec61937 alignment"); + } else + GST_INFO_OBJECT (ac3parse, "unknown alignment: %s", str); + break; + } + } + + if (caps) + gst_caps_unref (caps); + +done: + /* default */ + if (ac3parse->align == GST_AC3_PARSE_ALIGN_NONE) { + ac3parse->align = GST_AC3_PARSE_ALIGN_FRAME; + GST_DEBUG_OBJECT (ac3parse, "picked syncframe alignment"); + } +} + static gboolean gst_ac3_parse_frame_header_ac3 (GstAc3Parse * ac3parse, GstBuffer * buf, - guint * frame_size, guint * rate, guint * chans, guint * blks, guint * sid) + gint skip, guint * frame_size, guint * rate, guint * chans, guint * blks, + guint * sid) { GstBitReader bits = GST_BIT_READER_INIT_FROM_BUFFER (buf); guint8 fscod, frmsizcod, bsid, acmod, lfe_on; GST_LOG_OBJECT (ac3parse, "parsing ac3"); + gst_bit_reader_skip_unchecked (&bits, skip * 8); + gst_bit_reader_skip_unchecked (&bits, 16 + 16); fscod = gst_bit_reader_get_bits_uint8_unchecked (&bits, 2); frmsizcod = gst_bit_reader_get_bits_uint8_unchecked (&bits, 6); @@ -297,7 +344,8 @@ gst_ac3_parse_frame_header_ac3 (GstAc3Parse * ac3parse, GstBuffer * buf, static gboolean gst_ac3_parse_frame_header_eac3 (GstAc3Parse * ac3parse, GstBuffer * buf, - guint * frame_size, guint * rate, guint * chans, guint * blks, guint * sid) + gint skip, guint * frame_size, guint * rate, guint * chans, guint * blks, + guint * sid) { GstBitReader bits = GST_BIT_READER_INIT_FROM_BUFFER (buf); guint16 frmsiz, sample_rate, blocks; @@ -305,6 +353,8 @@ gst_ac3_parse_frame_header_eac3 (GstAc3Parse * ac3parse, GstBuffer * buf, GST_LOG_OBJECT (ac3parse, "parsing e-ac3"); + gst_bit_reader_skip_unchecked (&bits, skip * 8); + gst_bit_reader_skip_unchecked (&bits, 16); strmtyp = gst_bit_reader_get_bits_uint8_unchecked (&bits, 2); /* strmtyp */ if (G_UNLIKELY (strmtyp == 3)) { @@ -349,7 +399,7 @@ gst_ac3_parse_frame_header_eac3 (GstAc3Parse * ac3parse, GstBuffer * buf, } static gboolean -gst_ac3_parse_frame_header (GstAc3Parse * parse, GstBuffer * buf, +gst_ac3_parse_frame_header (GstAc3Parse * parse, GstBuffer * buf, gint skip, guint * framesize, guint * rate, guint * chans, guint * blocks, guint * sid, gboolean * eac) { @@ -359,6 +409,8 @@ gst_ac3_parse_frame_header (GstAc3Parse * parse, GstBuffer * buf, GST_MEMDUMP_OBJECT (parse, "AC3 frame sync", GST_BUFFER_DATA (buf), 16); + gst_bit_reader_skip_unchecked (&bits, skip * 8); + sync = gst_bit_reader_get_bits_uint16_unchecked (&bits, 16); gst_bit_reader_skip_unchecked (&bits, 16 + 8); bsid = gst_bit_reader_peek_bits_uint8_unchecked (&bits, 5); @@ -371,13 +423,13 @@ gst_ac3_parse_frame_header (GstAc3Parse * parse, GstBuffer * buf, if (bsid <= 10) { if (eac) *eac = FALSE; - return gst_ac3_parse_frame_header_ac3 (parse, buf, framesize, rate, chans, - blocks, sid); + return gst_ac3_parse_frame_header_ac3 (parse, buf, skip, framesize, rate, + chans, blocks, sid); } else if (bsid <= 16) { if (eac) *eac = TRUE; - return gst_ac3_parse_frame_header_eac3 (parse, buf, framesize, rate, chans, - blocks, sid); + return gst_ac3_parse_frame_header_eac3 (parse, buf, skip, framesize, rate, + chans, blocks, sid); } else { GST_DEBUG_OBJECT (parse, "unexpected bsid %d", bsid); return FALSE; @@ -392,7 +444,9 @@ gst_ac3_parse_check_valid_frame (GstBaseParse * parse, GstBuffer *buf = frame->buffer; GstByteReader reader = GST_BYTE_READER_INIT_FROM_BUFFER (buf); gint off; - gboolean lost_sync, draining; + gboolean lost_sync, draining, eac, more = FALSE; + guint frmsiz, blocks, sid; + gint have_blocks = 0; if (G_UNLIKELY (GST_BUFFER_SIZE (buf) < 6)) return FALSE; @@ -415,23 +469,68 @@ gst_ac3_parse_check_valid_frame (GstBaseParse * parse, } /* make sure the values in the frame header look sane */ - if (!gst_ac3_parse_frame_header (ac3parse, buf, framesize, NULL, NULL, - NULL, NULL, NULL)) { + if (!gst_ac3_parse_frame_header (ac3parse, buf, 0, &frmsiz, NULL, NULL, + &blocks, &sid, &eac)) { *skipsize = off + 2; return FALSE; } + *framesize = frmsiz; + + if (G_UNLIKELY (ac3parse->align == GST_AC3_PARSE_ALIGN_NONE)) + gst_ac3_parse_set_alignment (ac3parse, eac); + GST_LOG_OBJECT (parse, "got frame"); lost_sync = GST_BASE_PARSE_LOST_SYNC (parse); draining = GST_BASE_PARSE_DRAINING (parse); + if (ac3parse->align == GST_AC3_PARSE_ALIGN_IEC61937) { + /* We need 6 audio blocks from each substream, so we keep going forwards + * till we have it */ + + g_assert (blocks > 0); + GST_LOG_OBJECT (ac3parse, "Need %d frames before pushing", 6 / blocks); + + if (sid != 0) { + /* We need the first substream to be the one with id 0 */ + GST_LOG_OBJECT (ac3parse, "Skipping till we find sid 0"); + *skipsize = off + 2; + return FALSE; + } + + *framesize = 0; + + /* Loop till we have 6 blocks per substream */ + for (have_blocks = 0; !more && have_blocks < 6; have_blocks += blocks) { + /* Loop till we get one frame from each substream */ + do { + *framesize += frmsiz; + + if (!gst_byte_reader_skip (&reader, frmsiz) || + GST_BUFFER_SIZE (buf) < (*framesize + 6)) { + more = TRUE; + break; + } + + if (!gst_ac3_parse_frame_header (ac3parse, buf, *framesize, &frmsiz, + NULL, NULL, NULL, &sid, &eac)) { + *skipsize = off + 2; + return FALSE; + } + } while (sid); + } + + /* We're now at the next frame, so no need to skip if resyncing */ + frmsiz = 0; + } + if (lost_sync && !draining) { guint16 word = 0; GST_DEBUG_OBJECT (ac3parse, "resyncing; checking next frame syncword"); - if (!gst_byte_reader_skip (&reader, *framesize) || + if (more || !gst_byte_reader_skip (&reader, frmsiz) || !gst_byte_reader_get_uint16_be (&reader, &word)) { GST_DEBUG_OBJECT (ac3parse, "... but not sufficient data"); gst_base_parse_set_min_frame_size (parse, *framesize + 6); @@ -460,7 +559,7 @@ gst_ac3_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame) guint fsize, rate, chans, blocks, sid; gboolean eac, update_rate = FALSE; - if (!gst_ac3_parse_frame_header (ac3parse, buf, &fsize, &rate, &chans, + if (!gst_ac3_parse_frame_header (ac3parse, buf, 0, &fsize, &rate, &chans, &blocks, &sid, &eac)) goto broken_header; @@ -486,6 +585,9 @@ gst_ac3_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame) 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); + gst_caps_set_simple (caps, "alignment", G_TYPE_STRING, + ac3parse->align == GST_AC3_PARSE_ALIGN_IEC61937 ? "iec61937" : "frame", + NULL); gst_buffer_set_caps (buf, caps); gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps); gst_caps_unref (caps); diff --git a/gst/audioparsers/gstac3parse.h b/gst/audioparsers/gstac3parse.h index 13270bb8ad..7b651ee2b3 100644 --- a/gst/audioparsers/gstac3parse.h +++ b/gst/audioparsers/gstac3parse.h @@ -42,6 +42,12 @@ G_BEGIN_DECLS typedef struct _GstAc3Parse GstAc3Parse; typedef struct _GstAc3ParseClass GstAc3ParseClass; +enum { + GST_AC3_PARSE_ALIGN_NONE, + GST_AC3_PARSE_ALIGN_FRAME, + GST_AC3_PARSE_ALIGN_IEC61937, +}; + /** * GstAc3Parse: * @@ -55,6 +61,7 @@ struct _GstAc3Parse { gint channels; gint blocks; gboolean eac; + gint align; }; /** diff --git a/tests/check/elements/ac3parse.c b/tests/check/elements/ac3parse.c index 03e8e1dc85..eb25004971 100644 --- a/tests/check/elements/ac3parse.c +++ b/tests/check/elements/ac3parse.c @@ -110,7 +110,7 @@ GST_END_TEST; GST_START_TEST (test_parse_detect_stream) { gst_parser_test_output_caps (ac3_frame, sizeof (ac3_frame), - NULL, SINK_CAPS_TMPL ",channels=1,rate=48000"); + NULL, SINK_CAPS_TMPL ",channels=1,rate=48000,alignment=frame"); } GST_END_TEST; From 89564fcb69b481050f8064f0ba232a86cd9c5f51 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Tue, 12 Apr 2011 17:01:47 +0530 Subject: [PATCH 17/32] ac3parse: Support switching alignment on-the-fly This allows switching of alignment for E-AC3 streams at run-time. This is requested by downstream elements via a custom event. https://bugzilla.gnome.org/show_bug.cgi?id=650313 --- gst/audioparsers/gstac3parse.c | 60 ++++++++++++++++++++++++++++------ gst/audioparsers/gstac3parse.h | 10 +++--- 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/gst/audioparsers/gstac3parse.c b/gst/audioparsers/gstac3parse.c index 2ff8fa310f..80c1f4d613 100644 --- a/gst/audioparsers/gstac3parse.c +++ b/gst/audioparsers/gstac3parse.c @@ -163,6 +163,8 @@ static gboolean gst_ac3_parse_check_valid_frame (GstBaseParse * parse, GstBaseParseFrame * frame, guint * size, gint * skipsize); static GstFlowReturn gst_ac3_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame); +static gboolean gst_ac3_parse_src_event (GstBaseParse * parse, + GstEvent * event); GST_BOILERPLATE (GstAc3Parse, gst_ac3_parse, GstBaseParse, GST_TYPE_BASE_PARSE); @@ -197,6 +199,8 @@ gst_ac3_parse_class_init (GstAc3ParseClass * klass) parse_class->check_valid_frame = GST_DEBUG_FUNCPTR (gst_ac3_parse_check_valid_frame); parse_class->parse_frame = GST_DEBUG_FUNCPTR (gst_ac3_parse_parse_frame); + + parse_class->src_event = GST_DEBUG_FUNCPTR (gst_ac3_parse_src_event); } static void @@ -206,7 +210,7 @@ gst_ac3_parse_reset (GstAc3Parse * ac3parse) ac3parse->sample_rate = -1; ac3parse->blocks = -1; ac3parse->eac = FALSE; - ac3parse->align = GST_AC3_PARSE_ALIGN_NONE; + g_atomic_int_set (&ac3parse->align, GST_AC3_PARSE_ALIGN_NONE); } static void @@ -265,11 +269,16 @@ gst_ac3_parse_set_alignment (GstAc3Parse * ac3parse, gboolean eac) continue; if ((str = gst_structure_get_string (st, "alignment"))) { - if (strcmp (str, "iec61937") == 0) { - ac3parse->align = GST_AC3_PARSE_ALIGN_IEC61937; + if (g_str_equal (str, "iec61937")) { + g_atomic_int_set (&ac3parse->align, GST_AC3_PARSE_ALIGN_IEC61937); GST_DEBUG_OBJECT (ac3parse, "picked iec61937 alignment"); - } else - GST_INFO_OBJECT (ac3parse, "unknown alignment: %s", str); + } else if (g_str_equal (str, "frame") == 0) { + g_atomic_int_set (&ac3parse->align, GST_AC3_PARSE_ALIGN_FRAME); + GST_DEBUG_OBJECT (ac3parse, "picked frame alignment"); + } else { + g_atomic_int_set (&ac3parse->align, GST_AC3_PARSE_ALIGN_FRAME); + GST_WARNING_OBJECT (ac3parse, "unknown alignment: %s", str); + } break; } } @@ -280,7 +289,7 @@ gst_ac3_parse_set_alignment (GstAc3Parse * ac3parse, gboolean eac) done: /* default */ if (ac3parse->align == GST_AC3_PARSE_ALIGN_NONE) { - ac3parse->align = GST_AC3_PARSE_ALIGN_FRAME; + g_atomic_int_set (&ac3parse->align, GST_AC3_PARSE_ALIGN_FRAME); GST_DEBUG_OBJECT (ac3parse, "picked syncframe alignment"); } } @@ -477,7 +486,8 @@ gst_ac3_parse_check_valid_frame (GstBaseParse * parse, *framesize = frmsiz; - if (G_UNLIKELY (ac3parse->align == GST_AC3_PARSE_ALIGN_NONE)) + if (G_UNLIKELY (g_atomic_int_get (&ac3parse->align) == + GST_AC3_PARSE_ALIGN_NONE)) gst_ac3_parse_set_alignment (ac3parse, eac); GST_LOG_OBJECT (parse, "got frame"); @@ -485,7 +495,7 @@ gst_ac3_parse_check_valid_frame (GstBaseParse * parse, lost_sync = GST_BASE_PARSE_LOST_SYNC (parse); draining = GST_BASE_PARSE_DRAINING (parse); - if (ac3parse->align == GST_AC3_PARSE_ALIGN_IEC61937) { + if (g_atomic_int_get (&ac3parse->align) == GST_AC3_PARSE_ALIGN_IEC61937) { /* We need 6 audio blocks from each substream, so we keep going forwards * till we have it */ @@ -586,8 +596,8 @@ gst_ac3_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame) "framed", G_TYPE_BOOLEAN, TRUE, "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, chans, NULL); gst_caps_set_simple (caps, "alignment", G_TYPE_STRING, - ac3parse->align == GST_AC3_PARSE_ALIGN_IEC61937 ? "iec61937" : "frame", - NULL); + g_atomic_int_get (&ac3parse->align) == GST_AC3_PARSE_ALIGN_IEC61937 ? + "iec61937" : "frame", NULL); gst_buffer_set_caps (buf, caps); gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps); gst_caps_unref (caps); @@ -618,3 +628,33 @@ broken_header: return GST_FLOW_ERROR; } } + +static gboolean +gst_ac3_parse_src_event (GstBaseParse * parse, GstEvent * event) +{ + GstAc3Parse *ac3parse = GST_AC3_PARSE (parse); + + if (G_UNLIKELY (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_UPSTREAM) && + gst_event_has_name (event, "ac3parse-set-alignment")) { + const GstStructure *st = gst_event_get_structure (event); + const gchar *align = gst_structure_get_string (st, "alignment"); + + if (g_str_equal (align, "iec61937")) { + GST_DEBUG_OBJECT (ac3parse, "Switching to iec61937 alignment"); + g_atomic_int_set (&ac3parse->align, GST_AC3_PARSE_ALIGN_IEC61937); + } else if (g_str_equal (align, "frame")) { + GST_DEBUG_OBJECT (ac3parse, "Switching to frame alignment"); + g_atomic_int_set (&ac3parse->align, GST_AC3_PARSE_ALIGN_FRAME); + } else { + g_atomic_int_set (&ac3parse->align, GST_AC3_PARSE_ALIGN_FRAME); + GST_WARNING_OBJECT (ac3parse, "Got unknown alignment request (%s) " + "reverting to frame alignment.", + gst_structure_get_string (st, "alignment")); + } + + gst_event_unref (event); + return TRUE; + } + + return GST_BASE_PARSE_CLASS (parent_class)->src_event (parse, event); +} diff --git a/gst/audioparsers/gstac3parse.h b/gst/audioparsers/gstac3parse.h index 7b651ee2b3..545419f14f 100644 --- a/gst/audioparsers/gstac3parse.h +++ b/gst/audioparsers/gstac3parse.h @@ -57,11 +57,11 @@ struct _GstAc3Parse { GstBaseParse baseparse; /*< private >*/ - gint sample_rate; - gint channels; - gint blocks; - gboolean eac; - gint align; + gint sample_rate; + gint channels; + gint blocks; + gboolean eac; + volatile gint align; }; /** From 4c73439ee371adf5893cc4514437df824cb0f6a0 Mon Sep 17 00:00:00 2001 From: Sjoerd Simons Date: Wed, 27 Jul 2011 18:15:20 +0100 Subject: [PATCH 18/32] rtph264depay: Cope with FU-A E bit not being set Some h264 payloaders are unfortunately buggy and don't correctly set the E bit in FU-A NAL when they have ended. Work around this by assuming such a fragmentation unit has ended when there was no packet loss and a new NAL is started --- gst/rtp/gstrtph264depay.c | 70 ++++++++++++++++++++++++++++----------- gst/rtp/gstrtph264depay.h | 5 +++ 2 files changed, 56 insertions(+), 19 deletions(-) diff --git a/gst/rtp/gstrtph264depay.c b/gst/rtp/gstrtph264depay.c index 79acf0ee6d..fb0697797d 100644 --- a/gst/rtp/gstrtph264depay.c +++ b/gst/rtp/gstrtph264depay.c @@ -168,6 +168,7 @@ gst_rtp_h264_depay_reset (GstRtpH264Depay * rtph264depay) rtph264depay->picture_start = FALSE; rtph264depay->last_keyframe = FALSE; rtph264depay->last_ts = 0; + rtph264depay->current_fu_type = 0; } static void @@ -606,6 +607,35 @@ short_nal: } } +static void +gst_rtp_h264_push_fragmentation_unit (GstRtpH264Depay * rtph264depay) +{ + guint outsize; + guint8 *outdata; + GstBuffer *outbuf; + + outsize = gst_adapter_available (rtph264depay->adapter); + outbuf = gst_adapter_take_buffer (rtph264depay->adapter, outsize); + outdata = GST_BUFFER_DATA (outbuf); + + GST_DEBUG_OBJECT (rtph264depay, "output %d bytes", outsize); + + if (rtph264depay->byte_stream) { + memcpy (outdata, sync_bytes, sizeof (sync_bytes)); + } else { + outsize -= 4; + outdata[0] = (outsize >> 24); + outdata[1] = (outsize >> 16); + outdata[2] = (outsize >> 8); + outdata[3] = (outsize); + } + + gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, + rtph264depay->fu_timestamp, rtph264depay->fu_marker); + + rtph264depay->current_fu_type = 0; +} + static GstBuffer * gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) { @@ -619,6 +649,7 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) if (GST_BUFFER_IS_DISCONT (buf)) { gst_adapter_clear (rtph264depay->adapter); rtph264depay->wait_start = TRUE; + rtph264depay->current_fu_type = 0; } { @@ -659,6 +690,13 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) GST_DEBUG_OBJECT (rtph264depay, "NRI %d, Type %d", nal_ref_idc, nal_unit_type); + /* If FU unit was being processed, but the current nal is of a different + * type. Assume that the remote payloader is buggy (didn't set the end bit + * 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); + switch (nal_unit_type) { case 0: case 30: @@ -758,6 +796,15 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) /* NAL unit starts here */ guint8 nal_header; + /* If a new FU unit started, while still processing an older one. + * 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); + + rtph264depay->current_fu_type = nal_unit_type; + rtph264depay->fu_timestamp = timestamp; + rtph264depay->wait_start = FALSE; /* reconstruct NAL header */ @@ -796,26 +843,11 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) gst_adapter_push (rtph264depay->adapter, outbuf); } + rtph264depay->fu_marker = marker; + /* if NAL unit ends, flush the adapter */ - if (E) { - GST_DEBUG_OBJECT (rtph264depay, "output %d bytes", outsize); - - outsize = gst_adapter_available (rtph264depay->adapter); - outbuf = gst_adapter_take_buffer (rtph264depay->adapter, outsize); - outdata = GST_BUFFER_DATA (outbuf); - - if (rtph264depay->byte_stream) { - memcpy (outdata, sync_bytes, sizeof (sync_bytes)); - } else { - outsize -= 4; - outdata[0] = (outsize >> 24); - outdata[1] = (outsize >> 16); - outdata[2] = (outsize >> 8); - outdata[3] = (outsize); - } - gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp, - marker); - } + if (E) + gst_rtp_h264_push_fragmentation_unit (rtph264depay); break; } default: diff --git a/gst/rtp/gstrtph264depay.h b/gst/rtp/gstrtph264depay.h index e801f1fe82..f50ffe6a37 100644 --- a/gst/rtp/gstrtph264depay.h +++ b/gst/rtp/gstrtph264depay.h @@ -56,6 +56,11 @@ struct _GstRtpH264Depay gboolean picture_start; GstClockTime last_ts; gboolean last_keyframe; + + /* Work around broken payloaders wrt. FU-A & FU-B */ + guint8 current_fu_type; + GstClockTime fu_timestamp; + gboolean fu_marker; }; struct _GstRtpH264DepayClass From e26b5391c2d9259bb85f031e0760f05f037fe9bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Tue, 24 May 2011 11:17:25 +0300 Subject: [PATCH 19/32] rtpssrcdemux: Use PADs lock https://bugzilla.gnome.org/show_bug.cgi?id=650916 --- gst/rtpmanager/gstrtpssrcdemux.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gst/rtpmanager/gstrtpssrcdemux.c b/gst/rtpmanager/gstrtpssrcdemux.c index 25ab9c3485..43369cfa7f 100644 --- a/gst/rtpmanager/gstrtpssrcdemux.c +++ b/gst/rtpmanager/gstrtpssrcdemux.c @@ -167,11 +167,11 @@ find_or_create_demux_pad_for_ssrc (GstRtpSsrcDemux * demux, guint32 ssrc) GST_DEBUG_OBJECT (demux, "creating pad for SSRC %08x", ssrc); - GST_OBJECT_LOCK (demux); + GST_PAD_LOCK (demux); demuxpad = find_demux_pad_for_ssrc (demux, ssrc); if (demuxpad != NULL) { - GST_OBJECT_UNLOCK (demux); + GST_PAD_UNLOCK (demux); return demuxpad; } @@ -214,7 +214,7 @@ find_or_create_demux_pad_for_ssrc (GstRtpSsrcDemux * demux, guint32 ssrc) gst_rtp_ssrc_demux_iterate_internal_links_src); gst_pad_set_active (rtcp_pad, TRUE); - GST_OBJECT_UNLOCK (demux); + GST_PAD_UNLOCK (demux); gst_element_add_pad (GST_ELEMENT_CAST (demux), rtp_pad); gst_element_add_pad (GST_ELEMENT_CAST (demux), rtcp_pad); From c7b9b98648e5b14e54908d3ac414e414dbf2cbd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Tue, 24 May 2011 11:29:57 +0300 Subject: [PATCH 20/32] rtpssrcdemux: Make the pads lock recursive and hold it across the signal emit We need to keep the lock held because we don't want a push before the "new-ssrc-pad" handler has completed. But we may want to push an event from inside that handler, hence the recursive mutex. https://bugzilla.gnome.org/show_bug.cgi?id=650916 --- gst/rtpmanager/gstrtpssrcdemux.c | 12 ++++++------ gst/rtpmanager/gstrtpssrcdemux.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/gst/rtpmanager/gstrtpssrcdemux.c b/gst/rtpmanager/gstrtpssrcdemux.c index 43369cfa7f..46ed8b04ce 100644 --- a/gst/rtpmanager/gstrtpssrcdemux.c +++ b/gst/rtpmanager/gstrtpssrcdemux.c @@ -83,8 +83,8 @@ GST_STATIC_PAD_TEMPLATE ("rtcp_src_%d", GST_STATIC_CAPS ("application/x-rtcp") ); -#define GST_PAD_LOCK(obj) (g_mutex_lock ((obj)->padlock)) -#define GST_PAD_UNLOCK(obj) (g_mutex_unlock ((obj)->padlock)) +#define GST_PAD_LOCK(obj) (g_static_rec_mutex_lock (&(obj)->padlock)) +#define GST_PAD_UNLOCK(obj) (g_static_rec_mutex_unlock (&(obj)->padlock)) /* signals */ enum @@ -214,14 +214,14 @@ find_or_create_demux_pad_for_ssrc (GstRtpSsrcDemux * demux, guint32 ssrc) gst_rtp_ssrc_demux_iterate_internal_links_src); gst_pad_set_active (rtcp_pad, TRUE); - GST_PAD_UNLOCK (demux); - gst_element_add_pad (GST_ELEMENT_CAST (demux), rtp_pad); gst_element_add_pad (GST_ELEMENT_CAST (demux), rtcp_pad); g_signal_emit (G_OBJECT (demux), gst_rtp_ssrc_demux_signals[SIGNAL_NEW_SSRC_PAD], 0, ssrc, rtp_pad); + GST_PAD_UNLOCK (demux); + return demuxpad; } @@ -336,7 +336,7 @@ gst_rtp_ssrc_demux_init (GstRtpSsrcDemux * demux, gst_rtp_ssrc_demux_iterate_internal_links_sink); gst_element_add_pad (GST_ELEMENT_CAST (demux), demux->rtcp_sink); - demux->padlock = g_mutex_new (); + g_static_rec_mutex_init (&demux->padlock); gst_segment_init (&demux->segment, GST_FORMAT_UNDEFINED); } @@ -378,7 +378,7 @@ gst_rtp_ssrc_demux_finalize (GObject * object) GstRtpSsrcDemux *demux; demux = GST_RTP_SSRC_DEMUX (object); - g_mutex_free (demux->padlock); + g_static_rec_mutex_free (&demux->padlock); G_OBJECT_CLASS (parent_class)->finalize (object); } diff --git a/gst/rtpmanager/gstrtpssrcdemux.h b/gst/rtpmanager/gstrtpssrcdemux.h index d5a13caf4b..6f792d9682 100644 --- a/gst/rtpmanager/gstrtpssrcdemux.h +++ b/gst/rtpmanager/gstrtpssrcdemux.h @@ -41,7 +41,7 @@ struct _GstRtpSsrcDemux GstPad *rtp_sink; GstPad *rtcp_sink; - GMutex *padlock; + GStaticRecMutex padlock; GSList *srcpads; }; From 3a98f6f0fdd50d90f4881d096eade2e1d9da61a8 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Thu, 28 Jul 2011 14:44:57 +0200 Subject: [PATCH 21/32] rtpssrcdemux: keep a ref on the src pad while using it Prevent a possible race if clear_ssrc() is called between getting the pad and doing the push. Based on patch by https://bugzilla.gnome.org/show_bug.cgi?id=650916 --- gst/rtpmanager/gstrtpssrcdemux.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/gst/rtpmanager/gstrtpssrcdemux.c b/gst/rtpmanager/gstrtpssrcdemux.c index 46ed8b04ce..a6a6bdc39b 100644 --- a/gst/rtpmanager/gstrtpssrcdemux.c +++ b/gst/rtpmanager/gstrtpssrcdemux.c @@ -156,6 +156,7 @@ find_demux_pad_for_ssrc (GstRtpSsrcDemux * demux, guint32 ssrc) return NULL; } +/* with PAD_LOCK */ static GstRtpSsrcDemuxPad * find_or_create_demux_pad_for_ssrc (GstRtpSsrcDemux * demux, guint32 ssrc) { @@ -167,11 +168,8 @@ find_or_create_demux_pad_for_ssrc (GstRtpSsrcDemux * demux, guint32 ssrc) GST_DEBUG_OBJECT (demux, "creating pad for SSRC %08x", ssrc); - GST_PAD_LOCK (demux); - demuxpad = find_demux_pad_for_ssrc (demux, ssrc); if (demuxpad != NULL) { - GST_PAD_UNLOCK (demux); return demuxpad; } @@ -220,8 +218,6 @@ find_or_create_demux_pad_for_ssrc (GstRtpSsrcDemux * demux, guint32 ssrc) g_signal_emit (G_OBJECT (demux), gst_rtp_ssrc_demux_signals[SIGNAL_NEW_SSRC_PAD], 0, ssrc, rtp_pad); - GST_PAD_UNLOCK (demux); - return demuxpad; } @@ -518,6 +514,7 @@ gst_rtp_ssrc_demux_chain (GstPad * pad, GstBuffer * buf) GstRtpSsrcDemux *demux; guint32 ssrc; GstRtpSsrcDemuxPad *dpad; + GstPad *srcpad; demux = GST_RTP_SSRC_DEMUX (GST_OBJECT_PARENT (pad)); @@ -528,12 +525,19 @@ gst_rtp_ssrc_demux_chain (GstPad * pad, GstBuffer * buf) GST_DEBUG_OBJECT (demux, "received buffer of SSRC %08x", ssrc); + GST_PAD_LOCK (demux); dpad = find_or_create_demux_pad_for_ssrc (demux, ssrc); - if (dpad == NULL) + if (dpad == NULL) { + GST_PAD_UNLOCK (demux); goto create_failed; + } + srcpad = gst_object_ref (dpad->rtp_pad); + GST_PAD_UNLOCK (demux); /* push to srcpad */ - ret = gst_pad_push (dpad->rtp_pad, buf); + ret = gst_pad_push (srcpad, buf); + + gst_object_unref (srcpad); return ret; @@ -563,6 +567,7 @@ gst_rtp_ssrc_demux_rtcp_chain (GstPad * pad, GstBuffer * buf) guint32 ssrc; GstRtpSsrcDemuxPad *dpad; GstRTCPPacket packet; + GstPad *srcpad; demux = GST_RTP_SSRC_DEMUX (GST_OBJECT_PARENT (pad)); @@ -585,13 +590,19 @@ gst_rtp_ssrc_demux_rtcp_chain (GstPad * pad, GstBuffer * buf) GST_DEBUG_OBJECT (demux, "received RTCP of SSRC %08x", ssrc); + GST_PAD_LOCK (demux); dpad = find_or_create_demux_pad_for_ssrc (demux, ssrc); - if (dpad == NULL) + if (dpad == NULL) { + GST_PAD_UNLOCK (demux); goto create_failed; - + } + srcpad = gst_object_ref (dpad->rtcp_pad); + GST_PAD_UNLOCK (demux); /* push to srcpad */ - ret = gst_pad_push (dpad->rtcp_pad, buf); + ret = gst_pad_push (srcpad, buf); + + gst_object_unref (srcpad); return ret; From 379049809c86ed142564e53004a0f234c9959883 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Fri, 29 Jul 2011 00:07:52 +0530 Subject: [PATCH 22/32] pulsesrc: Add a source-output-index property This exposes the source output index of the record stream that we open so that clients can use this with the introspection if they want (to move the stream, for example). --- ext/pulse/pulsesrc.c | 25 +++++++++++++++++++++++++ ext/pulse/pulsesrc.h | 1 + 2 files changed, 26 insertions(+) diff --git a/ext/pulse/pulsesrc.c b/ext/pulse/pulsesrc.c index f3f63a54fa..da3ae24771 100644 --- a/ext/pulse/pulsesrc.c +++ b/ext/pulse/pulsesrc.c @@ -63,6 +63,7 @@ enum PROP_DEVICE_NAME, PROP_CLIENT, PROP_STREAM_PROPERTIES, + PROP_SOURCE_OUTPUT_INDEX, PROP_LAST }; @@ -286,6 +287,20 @@ gst_pulsesrc_class_init (GstPulseSrcClass * klass) g_param_spec_boxed ("stream-properties", "stream properties", "list of pulseaudio stream properties", GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GstPulseSrc:source-output-index + * + * The index of the PulseAudio source output corresponding to this element. + * + * Since: 0.10.31 + */ + g_object_class_install_property (gobject_class, + PROP_SOURCE_OUTPUT_INDEX, + g_param_spec_uint ("source-output-index", "source output index", + "The index of the PulseAudio source output corresponding to this " + "record stream", 0, G_MAXUINT, PA_INVALID_INDEX, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); } static void @@ -298,6 +313,7 @@ gst_pulsesrc_init (GstPulseSrc * pulsesrc, GstPulseSrcClass * klass) pulsesrc->context = NULL; pulsesrc->stream = NULL; + pulsesrc->source_output_idx = PA_INVALID_INDEX; pulsesrc->read_buffer = NULL; pulsesrc->read_buffer_length = 0; @@ -327,6 +343,8 @@ gst_pulsesrc_destroy_stream (GstPulseSrc * pulsesrc) pa_stream_disconnect (pulsesrc->stream); pa_stream_unref (pulsesrc->stream); pulsesrc->stream = NULL; + pulsesrc->source_output_idx = PA_INVALID_INDEX; + g_object_notify (G_OBJECT (pulsesrc), "source-output-index"); } g_free (pulsesrc->device_description); @@ -524,6 +542,9 @@ gst_pulsesrc_get_property (GObject * object, case PROP_STREAM_PROPERTIES: gst_value_set_structure (value, pulsesrc->properties); break; + case PROP_SOURCE_OUTPUT_INDEX: + g_value_set_uint (value, pulsesrc->source_output_idx); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1065,6 +1086,10 @@ gst_pulsesrc_prepare (GstAudioSrc * asrc, GstRingBufferSpec * spec) pa_threaded_mainloop_wait (pulsesrc->mainloop); } + /* store the source output index so it can be accessed via a property */ + pulsesrc->source_output_idx = pa_stream_get_index (pulsesrc->stream); + g_object_notify (G_OBJECT (pulsesrc), "source-output-index"); + /* get the actual buffering properties now */ actual = pa_stream_get_buffer_attr (pulsesrc->stream); diff --git a/ext/pulse/pulsesrc.h b/ext/pulse/pulsesrc.h index 6e6322b9c3..a308afd334 100644 --- a/ext/pulse/pulsesrc.h +++ b/ext/pulse/pulsesrc.h @@ -61,6 +61,7 @@ struct _GstPulseSrc pa_context *context; pa_stream *stream; + guint32 source_output_idx; pa_sample_spec sample_spec; From a67b536741e330a15ce15d7efef585d6df9c23c4 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Tue, 1 Mar 2011 15:34:46 +0530 Subject: [PATCH 23/32] pulsesink: Use the extended stream API if available This uses the new extended API for creating streams. This will allow us to support compressed formats natively in pulsesink as well. --- configure.ac | 4 +++ ext/pulse/pulsesink.c | 48 +++++++++++++++++++++++++++ ext/pulse/pulseutil.c | 75 +++++++++++++++++++++++++++++++++++++++++++ ext/pulse/pulseutil.h | 4 +++ 4 files changed, 131 insertions(+) diff --git a/configure.ac b/configure.ac index d6f2b9ba2e..304feddc44 100644 --- a/configure.ac +++ b/configure.ac @@ -839,6 +839,10 @@ AG_GST_CHECK_FEATURE(PULSE, [pulseaudio plug-in], pulseaudio, [ if test x$HAVE_PULSE_0_9_20 = xyes; then AC_DEFINE(HAVE_PULSE_0_9_20, 1, [defined if pulseaudio >= 0.9.20 is available]) fi + AG_GST_PKG_CHECK_MODULES(PULSE_1_0, libpulse >= 0.98) + if test x$HAVE_PULSE_1_0 = xyes; then + AC_DEFINE(HAVE_PULSE_1_0, 1, [defined if pulseaudio >= 1.0 is available]) + fi ]) dnl *** dv1394 *** diff --git a/ext/pulse/pulsesink.c b/ext/pulse/pulsesink.c index 83ccdb2950..ad8282cc70 100644 --- a/ext/pulse/pulsesink.c +++ b/ext/pulse/pulsesink.c @@ -138,7 +138,12 @@ struct _GstPulseRingBuffer pa_context *context; pa_stream *stream; +#if HAVE_PULSE_1_0 + pa_format_info *format; + guint channels; +#else pa_sample_spec sample_spec; +#endif void *m_data; size_t m_towrite; @@ -222,7 +227,12 @@ gst_pulseringbuffer_init (GstPulseRingBuffer * pbuf) pbuf->context = NULL; pbuf->stream = NULL; +#ifdef HAVE_PULSE_1_0 + pbuf->format = NULL; + pbuf->channels = 0; +#else pa_sample_spec_init (&pbuf->sample_spec); +#endif pbuf->m_data = NULL; pbuf->m_towrite = 0; @@ -251,6 +261,13 @@ gst_pulsering_destroy_stream (GstPulseRingBuffer * pbuf) pbuf->m_offset = 0; pbuf->m_lastoffset = 0; } +#ifdef HAVE_PULSE_1_0 + if (pbuf->format) { + pa_format_info_free (pbuf->format); + pbuf->format = NULL; + pbuf->channels = 0; + } +#endif pa_stream_disconnect (pbuf->stream); @@ -718,14 +735,22 @@ gst_pulseringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) pa_stream_flags_t flags; const gchar *name; GstAudioClock *clock; +#ifdef HAVE_PULSE_1_0 + pa_format_info *formats[1]; +#endif psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (buf)); pbuf = GST_PULSERING_BUFFER_CAST (buf); GST_LOG_OBJECT (psink, "creating sample spec"); /* convert the gstreamer sample spec to the pulseaudio format */ +#ifdef HAVE_PULSE_1_0 + if (!gst_pulse_fill_format_info (spec, &pbuf->format, &pbuf->channels)) + goto invalid_spec; +#else if (!gst_pulse_fill_sample_spec (spec, &pbuf->sample_spec)) goto invalid_spec; +#endif pa_threaded_mainloop_lock (mainloop); @@ -742,7 +767,13 @@ gst_pulseringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) pa_operation_unref (o); /* initialize the channel map */ +#ifdef HAVE_PULSE_1_0 + if (pa_format_info_is_pcm (pbuf->format) && + gst_pulse_gst_to_channel_map (&channel_map, spec)) + pa_format_info_set_channel_map (pbuf->format, &channel_map); +#else gst_pulse_gst_to_channel_map (&channel_map, spec); +#endif /* find a good name for the stream */ if (psink->stream_name) @@ -751,10 +782,17 @@ gst_pulseringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) name = "Playback Stream"; /* create a stream */ +#ifdef HAVE_PULSE_1_0 + formats[0] = pbuf->format; + if (!(pbuf->stream = pa_stream_new_extended (pbuf->context, name, formats, 1, + psink->proplist))) + goto stream_failed; +#else GST_LOG_OBJECT (psink, "creating stream with name %s", name); if (!(pbuf->stream = pa_stream_new_with_proplist (pbuf->context, name, &pbuf->sample_spec, &channel_map, psink->proplist))) goto stream_failed; +#endif /* install essential callbacks */ pa_stream_set_state_callback (pbuf->stream, @@ -792,8 +830,13 @@ gst_pulseringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) if (psink->volume_set) { GST_LOG_OBJECT (psink, "have volume of %f", psink->volume); pv = &v; +#ifdef HAVE_PULSE_1_0 + if (pa_format_info_is_pcm (pbuf->format)) + gst_pulse_cvolume_from_linear (pv, pbuf->channels, psink->volume); +#else gst_pulse_cvolume_from_linear (pv, pbuf->sample_spec.channels, psink->volume); +#endif } else { pv = NULL; } @@ -1923,7 +1966,12 @@ gst_pulsesink_set_volume (GstPulseSink * psink, gdouble volume) if ((idx = pa_stream_get_index (pbuf->stream)) == PA_INVALID_INDEX) goto no_index; +#ifdef HAVE_PULSE_1_0 + if (pa_format_info_is_pcm (pbuf->format)) + gst_pulse_cvolume_from_linear (&v, pbuf->channels, volume); +#else gst_pulse_cvolume_from_linear (&v, pbuf->sample_spec.channels, volume); +#endif if (!(o = pa_context_set_sink_input_volume (pbuf->context, idx, &v, NULL, NULL))) diff --git a/ext/pulse/pulseutil.c b/ext/pulse/pulseutil.c index 8fbb3caece..a634b71f64 100644 --- a/ext/pulse/pulseutil.c +++ b/ext/pulse/pulseutil.c @@ -118,6 +118,81 @@ gst_pulse_fill_sample_spec (GstRingBufferSpec * spec, pa_sample_spec * ss) return TRUE; } +#ifdef HAVE_PULSE_1_0 +gboolean +gst_pulse_fill_format_info (GstRingBufferSpec * spec, pa_format_info ** f, + guint * channels) +{ + pa_format_info *format; + pa_sample_format_t sf = PA_SAMPLE_INVALID; + + format = pa_format_info_new (); + + if (spec->format == GST_MU_LAW && spec->width == 8) { + format->encoding = PA_ENCODING_PCM; + sf = PA_SAMPLE_ULAW; + } else if (spec->format == GST_A_LAW && spec->width == 8) { + format->encoding = PA_ENCODING_PCM; + sf = PA_SAMPLE_ALAW; + } else if (spec->format == GST_U8 && spec->width == 8) { + format->encoding = PA_ENCODING_PCM; + sf = PA_SAMPLE_U8; + } else if (spec->format == GST_S16_LE && spec->width == 16) { + format->encoding = PA_ENCODING_PCM; + sf = PA_SAMPLE_S16LE; + } else if (spec->format == GST_S16_BE && spec->width == 16) { + format->encoding = PA_ENCODING_PCM; + sf = PA_SAMPLE_S16BE; + } else if (spec->format == GST_FLOAT32_LE && spec->width == 32) { + format->encoding = PA_ENCODING_PCM; + sf = PA_SAMPLE_FLOAT32LE; + } else if (spec->format == GST_FLOAT32_BE && spec->width == 32) { + format->encoding = PA_ENCODING_PCM; + sf = PA_SAMPLE_FLOAT32BE; + } else if (spec->format == GST_S32_LE && spec->width == 32) { + format->encoding = PA_ENCODING_PCM; + sf = PA_SAMPLE_S32LE; + } else if (spec->format == GST_S32_BE && spec->width == 32) { + format->encoding = PA_ENCODING_PCM; + sf = PA_SAMPLE_S32BE; + } else if (spec->format == GST_S24_3LE && spec->width == 24) { + format->encoding = PA_ENCODING_PCM; + sf = PA_SAMPLE_S24LE; + } else if (spec->format == GST_S24_3BE && spec->width == 24) { + format->encoding = PA_ENCODING_PCM; + sf = PA_SAMPLE_S24BE; + } else if (spec->format == GST_S24_LE && spec->width == 32) { + format->encoding = PA_ENCODING_PCM; + sf = PA_SAMPLE_S24_32LE; + } else if (spec->format == GST_S24_BE && spec->width == 32) { + format->encoding = PA_ENCODING_PCM; + sf = PA_SAMPLE_S24_32BE; + } else { + goto fail; + } + + if (format->encoding == PA_ENCODING_PCM) { + pa_format_info_set_sample_format (format, sf); + pa_format_info_set_channels (format, spec->channels); + } + + pa_format_info_set_rate (format, spec->rate); + + if (!pa_format_info_valid (format)) + goto fail; + + *f = format; + *channels = spec->channels; + + return TRUE; + +fail: + if (format) + pa_format_info_free (format); + return FALSE; +} +#endif + /* PATH_MAX is not defined everywhere, e.g. on GNU Hurd */ #ifndef PATH_MAX #define PATH_MAX 4096 diff --git a/ext/pulse/pulseutil.h b/ext/pulse/pulseutil.h index ec04ffc0cb..91c8502e39 100644 --- a/ext/pulse/pulseutil.h +++ b/ext/pulse/pulseutil.h @@ -28,6 +28,10 @@ gboolean gst_pulse_fill_sample_spec (GstRingBufferSpec * spec, pa_sample_spec * ss); +#ifdef HAVE_PULSE_1_0 +gboolean gst_pulse_fill_format_info (GstRingBufferSpec * spec, + pa_format_info ** f, guint * channels); +#endif gchar *gst_pulse_client_name (void); From ac7cad431cbe178f67edbb0a5964ed5621bb7c4b Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Wed, 9 Mar 2011 11:04:36 +0530 Subject: [PATCH 24/32] pulsesink: Add support for compressed formats This adds support for various compressed formats (AC3, E-AC3, DTS and MP3) payloaded in IEC 61937 format (used for transmission over S/PDIF, HDMI and Bluetooth). The acceptcaps() function allows bins to probe for what formats the sink being connected to support. This only works after the element is set to at least READY. If the underlying sink changes and the format we are streaming is not available, we emit a message that will allow upstream elements/bins to block and renegotiate a new format. --- ext/pulse/pulsesink.c | 417 ++++++++++++++++++++++++++++++++++++++---- ext/pulse/pulsesink.h | 7 + ext/pulse/pulseutil.c | 8 + 3 files changed, 394 insertions(+), 38 deletions(-) diff --git a/ext/pulse/pulsesink.c b/ext/pulse/pulsesink.c index ad8282cc70..dc3c777c12 100644 --- a/ext/pulse/pulsesink.c +++ b/ext/pulse/pulsesink.c @@ -54,6 +54,7 @@ #include #include #include +#include #include /* only used for GST_PLUGINS_BASE_VERSION_* */ @@ -138,7 +139,7 @@ struct _GstPulseRingBuffer pa_context *context; pa_stream *stream; -#if HAVE_PULSE_1_0 +#ifdef HAVE_PULSE_1_0 pa_format_info *format; guint channels; #else @@ -712,11 +713,60 @@ gst_pulsering_stream_event_cb (pa_stream * p, const char *name, gst_element_post_message (GST_ELEMENT_CAST (psink), gst_message_new_request_state (GST_OBJECT_CAST (psink), GST_STATE_PLAYING)); +#ifdef HAVE_PULSE_1_0 + } else if (!strcmp (name, PA_STREAM_EVENT_FORMAT_LOST)) { + GstEvent *renego; + + if (g_atomic_int_get (&psink->format_lost)) { + /* Duplicate event before we're done reconfiguring, discard */ + return; + } + + GST_DEBUG_OBJECT (psink, "got FORMAT LOST"); + g_atomic_int_set (&psink->format_lost, 1); + psink->format_lost_time = g_ascii_strtoull (pa_proplist_gets (pl, + "stream-time"), NULL, 0) * 1000; + + g_free (psink->device); + psink->device = g_strdup (pa_proplist_gets (pl, "device")); + + renego = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, + gst_structure_new ("pulse-format-lost", NULL)); + + if (!gst_pad_push_event (GST_BASE_SINK (psink)->sinkpad, renego)) { + /* Nobody handled the format change - emit an error */ + GST_ELEMENT_ERROR (psink, STREAM, FORMAT, ("Sink format changed"), + ("Sink format changed")); + } +#endif } else { GST_DEBUG_OBJECT (psink, "got unknown event %s", name); } } +/* Called with the mainloop locked */ +static gboolean +gst_pulsering_wait_for_stream_ready (GstPulseSink * psink, pa_stream * stream) +{ + pa_stream_state_t state; + + for (;;) { + state = pa_stream_get_state (stream); + + GST_LOG_OBJECT (psink, "stream state is now %d", state); + + if (!PA_STREAM_IS_GOOD (state)) + return FALSE; + + if (state == PA_STREAM_READY) + return TRUE; + + /* Wait until the stream is ready */ + pa_threaded_mainloop_wait (mainloop); + } +} + + /* This method should create a new stream of the given @spec. No playback should * start yet so we start in the corked state. */ static gboolean @@ -737,6 +787,9 @@ gst_pulseringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) GstAudioClock *clock; #ifdef HAVE_PULSE_1_0 pa_format_info *formats[1]; +#ifndef GST_DISABLE_GST_DEBUG + gchar print_buf[PA_FORMAT_INFO_SNPRINT_MAX]; +#endif #endif psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (buf)); @@ -833,6 +886,10 @@ gst_pulseringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) #ifdef HAVE_PULSE_1_0 if (pa_format_info_is_pcm (pbuf->format)) gst_pulse_cvolume_from_linear (pv, pbuf->channels, psink->volume); + else { + GST_DEBUG_OBJECT (psink, "passthrough stream, not setting volume"); + pv = NULL; + } #else gst_pulse_cvolume_from_linear (pv, pbuf->sample_spec.channels, psink->volume); @@ -863,22 +920,19 @@ gst_pulseringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) clock = GST_AUDIO_CLOCK (GST_BASE_AUDIO_SINK (psink)->provided_clock); gst_audio_clock_reset (clock, 0); - for (;;) { - pa_stream_state_t state; + if (!gst_pulsering_wait_for_stream_ready (psink, pbuf->stream)) + goto connect_failed; - state = pa_stream_get_state (pbuf->stream); +#ifdef HAVE_PULSE_1_0 + g_free (psink->device); + psink->device = g_strdup (pa_stream_get_device_name (pbuf->stream)); - GST_LOG_OBJECT (psink, "stream state is now %d", state); - - if (!PA_STREAM_IS_GOOD (state)) - goto connect_failed; - - if (state == PA_STREAM_READY) - break; - - /* Wait until the stream is ready */ - pa_threaded_mainloop_wait (mainloop); - } +#ifndef GST_DISABLE_GST_DEBUG + pa_format_info_snprint (print_buf, sizeof (print_buf), + pa_stream_get_format_info (pbuf->stream)); + GST_INFO_OBJECT (psink, "negotiated to: %s", print_buf); +#endif +#endif /* After we passed the volume off of to PA we never want to set it again, since it is PA's job to save/restore volumes. */ @@ -945,13 +999,20 @@ static gboolean gst_pulseringbuffer_release (GstRingBuffer * buf) { GstPulseRingBuffer *pbuf; + GstPulseSink *psink; pbuf = GST_PULSERING_BUFFER_CAST (buf); + psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf)); pa_threaded_mainloop_lock (mainloop); gst_pulsering_destroy_stream (pbuf); pa_threaded_mainloop_unlock (mainloop); +#ifdef HAVE_PULSE_1_0 + g_atomic_int_set (&psink->format_lost, FALSE); + psink->format_lost_time = GST_CLOCK_TIME_NONE; +#endif + return TRUE; } @@ -973,6 +1034,13 @@ gst_pulsering_set_corked (GstPulseRingBuffer * pbuf, gboolean corked, psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf)); +#ifdef HAVE_PULSE_1_0 + if (g_atomic_int_get (&psink->format_lost)) { + /* Sink format changed, stream's gone so fake being paused */ + return TRUE; + } +#endif + GST_DEBUG_OBJECT (psink, "setting corked state to %d", corked); if (pbuf->corked != corked) { if (!(o = pa_stream_cork (pbuf->stream, corked, @@ -1146,13 +1214,22 @@ gst_pulseringbuffer_stop (GstRingBuffer * buf) psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf)); pa_threaded_mainloop_lock (mainloop); + pbuf->paused = TRUE; res = gst_pulsering_set_corked (pbuf, TRUE, TRUE); + /* Inform anyone waiting in _commit() call that it shall wakeup */ if (pbuf->in_commit) { GST_DEBUG_OBJECT (psink, "signal commit thread"); pa_threaded_mainloop_signal (mainloop, 0); } +#ifdef HAVE_PULSE_1_0 + if (g_atomic_int_get (&psink->format_lost)) { + /* Don't try to flush, the stream's probably gone by now */ + res = TRUE; + goto cleanup; + } +#endif /* then try to flush, it's not fatal when this fails */ GST_DEBUG_OBJECT (psink, "flushing"); @@ -1260,7 +1337,6 @@ G_STMT_START { \ GST_DEBUG ("rev_down end %d/%d",*accum,*toprocess); \ } G_STMT_END - /* our custom commit function because we write into the buffer of pulseaudio * instead of keeping our own buffer */ static guint @@ -1299,6 +1375,7 @@ gst_pulseringbuffer_commit (GstRingBuffer * buf, guint64 * sample, } pa_threaded_mainloop_lock (mainloop); + GST_DEBUG_OBJECT (psink, "entering commit"); pbuf->in_commit = TRUE; @@ -1323,6 +1400,13 @@ gst_pulseringbuffer_commit (GstRingBuffer * buf, guint64 * sample, * needed to properly handle reverse playback: it points to the last sample. */ data_end = data + (bps * inr); +#ifdef HAVE_PULSE_1_0 + if (g_atomic_int_get (&psink->format_lost)) { + /* Sink format changed, drop the data and hope upstream renegotiates */ + goto fake_done; + } +#endif + if (pbuf->paused) goto was_paused; @@ -1372,6 +1456,13 @@ gst_pulseringbuffer_commit (GstRingBuffer * buf, guint64 * sample, for (;;) { pbuf->m_writable = pa_stream_writable_size (pbuf->stream); +#ifdef HAVE_PULSE_1_0 + if (g_atomic_int_get (&psink->format_lost)) { + /* Sink format changed, give up and hope upstream renegotiates */ + goto fake_done; + } +#endif + if (pbuf->m_writable == (size_t) - 1) goto writable_size_failed; @@ -1430,6 +1521,14 @@ gst_pulseringbuffer_commit (GstRingBuffer * buf, guint64 * sample, GST_LOG_OBJECT (psink, "writing %u samples at offset %" G_GUINT64_FORMAT, (guint) avail, offset); +#ifdef HAVE_PULSE_1_0 + /* No trick modes for passthrough streams */ + if (G_UNLIKELY (inr != outr || reverse)) { + GST_WARNING_OBJECT (psink, "Passthrough stream can't run in trick mode"); + goto unlock_and_fail; + } +#endif + if (G_LIKELY (inr == outr && !reverse)) { /* no rate conversion, simply write out the samples */ /* copy the data into internal buffer */ @@ -1509,6 +1608,10 @@ gst_pulseringbuffer_commit (GstRingBuffer * buf, guint64 * sample, } } } + +#ifdef HAVE_PULSE_1_0 +fake_done: +#endif /* we consumed all samples here */ data = data_end + bps; @@ -1730,8 +1833,16 @@ gst_pulsesink_base_init (gpointer g_class) "rate = (int) [ 1, MAX], " "channels = (int) [ 1, 32 ];" "audio/x-mulaw, " - "rate = (int) [ 1, MAX], " "channels = (int) [ 1, 32 ]") - ); + "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 + )); GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); @@ -1754,6 +1865,40 @@ gst_pulsesink_create_ringbuffer (GstBaseAudioSink * sink) return buffer; } +static GstBuffer * +gst_pulsesink_payload (GstBaseAudioSink * sink, GstBuffer * buf) +{ + switch (sink->ringbuffer->spec.type) { + case GST_BUFTYPE_AC3: + case GST_BUFTYPE_EAC3: + case GST_BUFTYPE_DTS: + case GST_BUFTYPE_MPEG: + { + /* FIXME: alloc memory from PA if possible */ + gint framesize = gst_audio_iec61937_frame_size (&sink->ringbuffer->spec); + GstBuffer *out; + + if (framesize <= 0) + return NULL; + + out = gst_buffer_new_and_alloc (framesize); + + if (!gst_audio_iec61937_payload (GST_BUFFER_DATA (buf), + GST_BUFFER_SIZE (buf), GST_BUFFER_DATA (out), + GST_BUFFER_SIZE (out), &sink->ringbuffer->spec)) { + gst_buffer_unref (out); + return NULL; + } + + gst_buffer_copy_metadata (out, buf, GST_BUFFER_COPY_ALL); + return out; + } + + default: + return gst_buffer_ref (buf); + } +} + static void gst_pulsesink_class_init (GstPulseSinkClass * klass) { @@ -1778,6 +1923,7 @@ gst_pulsesink_class_init (GstPulseSinkClass * klass) gstaudiosink_class->create_ringbuffer = GST_DEBUG_FUNCPTR (gst_pulsesink_create_ringbuffer); + gstaudiosink_class->payload = GST_DEBUG_FUNCPTR (gst_pulsesink_payload); /* Overwrite GObject fields */ g_object_class_install_property (gobject_class, @@ -1860,6 +2006,14 @@ gst_pulsesink_get_time (GstClock * clock, GstBaseAudioSink * sink) pbuf = GST_PULSERING_BUFFER_CAST (sink->ringbuffer); psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf)); +#ifdef HAVE_PULSE_1_0 + if (g_atomic_int_get (&psink->format_lost)) { + /* Stream was lost in a format change, it'll get set up again once + * upstream renegotiates */ + return psink->format_lost_time; + } +#endif + pa_threaded_mainloop_lock (mainloop); if (gst_pulsering_is_dead (psink, pbuf, TRUE)) goto server_dead; @@ -1888,6 +2042,181 @@ server_dead: } } +static void +gst_pulsesink_sink_info_cb (pa_context * c, const pa_sink_info * i, int eol, + void *userdata) +{ + GstPulseRingBuffer *pbuf; + GstPulseSink *psink; +#ifdef HAVE_PULSE_1_0 + GList *l; + guint8 j; +#endif + + pbuf = GST_PULSERING_BUFFER_CAST (userdata); + psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf)); + + if (!i) + goto done; + + g_free (psink->device_description); + psink->device_description = g_strdup (i->description); + +#ifdef HAVE_PULSE_1_0 + g_mutex_lock (psink->sink_formats_lock); + + for (l = g_list_first (psink->sink_formats); l; l = g_list_next (l)) + pa_format_info_free ((pa_format_info *) l->data); + + g_list_free (psink->sink_formats); + psink->sink_formats = NULL; + + for (j = 0; j < i->n_formats; j++) + psink->sink_formats = g_list_prepend (psink->sink_formats, + pa_format_info_copy (i->formats[j])); + + g_mutex_unlock (psink->sink_formats_lock); +#endif + +done: + pa_threaded_mainloop_signal (mainloop, 0); +} + +#ifdef HAVE_PULSE_1_0 +static gboolean +gst_pulsesink_pad_acceptcaps (GstPad * pad, GstCaps * caps) +{ + GstPulseSink *psink = GST_PULSESINK (gst_pad_get_parent_element (pad)); + GstPulseRingBuffer *pbuf = GST_PULSERING_BUFFER_CAST (GST_BASE_AUDIO_SINK + (psink)->ringbuffer); + GstCaps *pad_caps; + GstStructure *st; + gboolean ret = FALSE; + + GstRingBufferSpec spec = { 0 }; + pa_stream *stream = NULL; + pa_operation *o = NULL; + pa_channel_map channel_map; + pa_stream_flags_t flags; + pa_format_info *format = NULL, *formats[1]; + guint channels; + + pad_caps = gst_pad_get_caps_reffed (pad); + if (pad_caps) { + ret = gst_caps_can_intersect (pad_caps, caps); + gst_caps_unref (pad_caps); + } + + /* Either template caps didn't match, or we're still in NULL state */ + if (!ret || !pbuf->context) + goto done; + + /* 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 done; + + ret = FALSE; + + pa_threaded_mainloop_lock (mainloop); + + spec.latency_time = GST_BASE_AUDIO_SINK (psink)->latency_time; + if (!gst_ring_buffer_parse_caps (&spec, caps)) + goto out; + + if (!gst_pulse_fill_format_info (&spec, &format, &channels)) + goto out; + + /* Make sure input is framed (one frame per buffer) and can be payloaded */ + if (!pa_format_info_is_pcm (format)) { + gboolean framed = FALSE, parsed = FALSE; + st = gst_caps_get_structure (caps, 0); + + 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; + } + + /* initialize the channel map */ + if (pa_format_info_is_pcm (format) && + gst_pulse_gst_to_channel_map (&channel_map, &spec)) + pa_format_info_set_channel_map (format, &channel_map); + + if (pbuf->stream) { + /* We're already in PAUSED or above, so just reuse this stream to query + * sink formats and use those. */ + GList *i; + + if (!(o = pa_context_get_sink_info_by_name (pbuf->context, psink->device, + gst_pulsesink_sink_info_cb, pbuf))) + goto info_failed; + + while (pa_operation_get_state (o) == PA_OPERATION_RUNNING) { + pa_threaded_mainloop_wait (mainloop); + if (gst_pulsering_is_dead (psink, pbuf, TRUE)) + goto out; + } + + g_mutex_lock (psink->sink_formats_lock); + for (i = g_list_first (psink->sink_formats); i; i = g_list_next (i)) { + if (pa_format_info_is_compatible ((pa_format_info *) i->data, format)) { + ret = TRUE; + break; + } + } + g_mutex_unlock (psink->sink_formats_lock); + } else { + /* We're in READY, let's connect a stream to see if the format is + * accpeted by whatever sink we're routed to */ + formats[0] = format; + + if (!(stream = pa_stream_new_extended (pbuf->context, "pulsesink probe", + formats, 1, psink->proplist))) + goto out; + + /* construct the flags */ + flags = PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE | + PA_STREAM_ADJUST_LATENCY | PA_STREAM_START_CORKED; + + pa_stream_set_state_callback (stream, gst_pulsering_stream_state_cb, pbuf); + + if (pa_stream_connect_playback (stream, psink->device, NULL, flags, NULL, + NULL) < 0) + goto out; + + ret = gst_pulsering_wait_for_stream_ready (psink, stream); + } + +out: + if (format) + pa_format_info_free (format); + + if (o) + pa_operation_unref (o); + + if (stream) { + pa_stream_set_state_callback (stream, NULL, NULL); + pa_stream_disconnect (stream); + pa_stream_unref (stream); + } + + pa_threaded_mainloop_unlock (mainloop); + +done: + gst_object_unref (psink); + return ret; + +info_failed: + { + GST_ELEMENT_ERROR (psink, RESOURCE, FAILED, + ("pa_context_get_sink_input_info() failed: %s", + pa_strerror (pa_context_errno (pbuf->context))), (NULL)); + goto out; + } +} +#endif + static void gst_pulsesink_init (GstPulseSink * pulsesink, GstPulseSinkClass * klass) { @@ -1896,6 +2225,11 @@ gst_pulsesink_init (GstPulseSink * pulsesink, GstPulseSinkClass * klass) pulsesink->device_description = NULL; pulsesink->client_name = gst_pulse_client_name (); +#ifdef HAVE_PULSE_1_0 + pulsesink->sink_formats_lock = g_mutex_new (); + pulsesink->sink_formats = NULL; +#endif + pulsesink->volume = DEFAULT_VOLUME; pulsesink->volume_set = FALSE; @@ -1904,6 +2238,11 @@ gst_pulsesink_init (GstPulseSink * pulsesink, GstPulseSinkClass * klass) pulsesink->notify = 0; +#ifdef HAVE_PULSE_1_0 + g_atomic_int_set (&pulsesink->format_lost, FALSE); + pulsesink->format_lost_time = GST_CLOCK_TIME_NONE; +#endif + pulsesink->properties = NULL; pulsesink->proplist = NULL; @@ -1915,6 +2254,11 @@ gst_pulsesink_init (GstPulseSink * pulsesink, GstPulseSinkClass * klass) gst_audio_clock_new ("GstPulseSinkClock", (GstAudioClockGetTimeFunc) gst_pulsesink_get_time, pulsesink); +#ifdef HAVE_PULSE_1_0 + gst_pad_set_acceptcaps_function (GST_BASE_SINK (pulsesink)->sinkpad, + GST_DEBUG_FUNCPTR (gst_pulsesink_pad_acceptcaps)); +#endif + /* TRUE for sinks, FALSE for sources */ pulsesink->probe = gst_pulseprobe_new (G_OBJECT (pulsesink), G_OBJECT_GET_CLASS (pulsesink), PROP_DEVICE, pulsesink->device, @@ -1925,12 +2269,23 @@ static void gst_pulsesink_finalize (GObject * object) { GstPulseSink *pulsesink = GST_PULSESINK_CAST (object); +#ifdef HAVE_PULSE_1_0 + GList *i; +#endif g_free (pulsesink->server); g_free (pulsesink->device); g_free (pulsesink->device_description); g_free (pulsesink->client_name); +#ifdef HAVE_PULSE_1_0 + for (i = g_list_first (pulsesink->sink_formats); i; i = g_list_next (i)) + pa_format_info_free ((pa_format_info *) i->data); + + g_list_free (pulsesink->sink_formats); + g_mutex_free (pulsesink->sink_formats_lock); +#endif + if (pulsesink->properties) gst_structure_free (pulsesink->properties); if (pulsesink->proplist) @@ -1969,6 +2324,10 @@ gst_pulsesink_set_volume (GstPulseSink * psink, gdouble volume) #ifdef HAVE_PULSE_1_0 if (pa_format_info_is_pcm (pbuf->format)) gst_pulse_cvolume_from_linear (&v, pbuf->channels, volume); + else + /* FIXME: this will eventually be superceded by checks to see if the volume + * is readable/writable */ + goto unlock; #else gst_pulse_cvolume_from_linear (&v, pbuf->sample_spec.channels, volume); #endif @@ -2248,26 +2607,6 @@ info_failed: } } -static void -gst_pulsesink_sink_info_cb (pa_context * c, const pa_sink_info * i, int eol, - void *userdata) -{ - GstPulseRingBuffer *pbuf; - GstPulseSink *psink; - - pbuf = GST_PULSERING_BUFFER_CAST (userdata); - psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf)); - - if (!i) - goto done; - - g_free (psink->device_description); - psink->device_description = g_strdup (i->description); - -done: - pa_threaded_mainloop_signal (mainloop, 0); -} - static gchar * gst_pulsesink_device_description (GstPulseSink * psink) { @@ -2664,6 +3003,7 @@ gst_pulsesink_change_state (GstElement * element, GstStateChange transition) gst_message_new_clock_provide (GST_OBJECT_CAST (element), GST_BASE_AUDIO_SINK (pulsesink)->provided_clock, TRUE)); break; + default: break; } @@ -2674,6 +3014,7 @@ gst_pulsesink_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: + /* format_lost is reset in release() in baseaudiosink */ gst_element_post_message (element, gst_message_new_clock_lost (GST_OBJECT_CAST (element), GST_BASE_AUDIO_SINK (pulsesink)->provided_clock)); diff --git a/ext/pulse/pulsesink.h b/ext/pulse/pulsesink.h index e3029295ef..ad8831f393 100644 --- a/ext/pulse/pulsesink.h +++ b/ext/pulse/pulsesink.h @@ -72,6 +72,13 @@ struct _GstPulseSink GstStructure *properties; pa_proplist *proplist; + +#ifdef HAVE_PULSE_1_0 + GMutex *sink_formats_lock; + GList *sink_formats; + volatile gint format_lost; + GstClockTime format_lost_time; +#endif }; struct _GstPulseSinkClass diff --git a/ext/pulse/pulseutil.c b/ext/pulse/pulseutil.c index a634b71f64..0d8af79d90 100644 --- a/ext/pulse/pulseutil.c +++ b/ext/pulse/pulseutil.c @@ -167,6 +167,14 @@ gst_pulse_fill_format_info (GstRingBufferSpec * spec, pa_format_info ** f, } else if (spec->format == GST_S24_BE && spec->width == 32) { format->encoding = PA_ENCODING_PCM; sf = PA_SAMPLE_S24_32BE; + } else if (spec->format == GST_AC3) { + format->encoding = PA_ENCODING_AC3_IEC61937; + } else if (spec->format == GST_EAC3) { + format->encoding = PA_ENCODING_EAC3_IEC61937; + } else if (spec->format == GST_DTS) { + format->encoding = PA_ENCODING_DTS_IEC61937; + } else if (spec->format == GST_MPEG) { + format->encoding = PA_ENCODING_MPEG_IEC61937; } else { goto fail; } From c03648c8bbd6a470d5eeaad4c48644fa8f5c24d4 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Fri, 29 Jul 2011 12:07:24 +0200 Subject: [PATCH 25/32] rtpsession: properly init rtcp_min_interval --- gst/rtpmanager/rtpsession.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gst/rtpmanager/rtpsession.c b/gst/rtpmanager/rtpsession.c index 26eae5d769..33c73757ec 100644 --- a/gst/rtpmanager/rtpsession.c +++ b/gst/rtpmanager/rtpsession.c @@ -544,6 +544,9 @@ rtp_session_init (RTPSession * sess) sess->stats.active_sources++; INIT_AVG (sess->stats.avg_rtcp_packet_size, 100); + rtp_stats_set_min_interval (&sess->stats, + (gdouble) DEFAULT_RTCP_MIN_INTERVAL / GST_SECOND); + /* default UDP header length */ sess->header_len = 28; sess->mtu = DEFAULT_RTCP_MTU; From 25ace0e524e8f689431f97a9ee6b976d9b916f8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Fri, 29 Jul 2011 13:05:42 +0100 Subject: [PATCH 26/32] pulsesink: fix variable-set-but-not-used compiler warning with older pulse versions --- ext/pulse/pulsesink.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ext/pulse/pulsesink.c b/ext/pulse/pulsesink.c index dc3c777c12..c9f0b585b5 100644 --- a/ext/pulse/pulsesink.c +++ b/ext/pulse/pulsesink.c @@ -999,18 +999,21 @@ static gboolean gst_pulseringbuffer_release (GstRingBuffer * buf) { GstPulseRingBuffer *pbuf; - GstPulseSink *psink; pbuf = GST_PULSERING_BUFFER_CAST (buf); - psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf)); pa_threaded_mainloop_lock (mainloop); gst_pulsering_destroy_stream (pbuf); pa_threaded_mainloop_unlock (mainloop); #ifdef HAVE_PULSE_1_0 - g_atomic_int_set (&psink->format_lost, FALSE); - psink->format_lost_time = GST_CLOCK_TIME_NONE; + { + GstPulseSink *psink; + + psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf)); + g_atomic_int_set (&psink->format_lost, FALSE); + psink->format_lost_time = GST_CLOCK_TIME_NONE; + } #endif return TRUE; From 62cd1215c7fc00bf03f5a93f0bc27827e4a48077 Mon Sep 17 00:00:00 2001 From: Mart Raudsepp Date: Sun, 31 Jul 2011 04:19:00 +0300 Subject: [PATCH 27/32] deinterlace: Fix Since tags for fieldanalysis related new properties commit c1b100cf9c is after 0.10.29 and 0.10.30 was a branched release. So fix Since tags from 0.10.29 to 0.10.31 for the new properties. --- gst/deinterlace/gstdeinterlace.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gst/deinterlace/gstdeinterlace.c b/gst/deinterlace/gstdeinterlace.c index 415cb00691..c4d6cf94dd 100644 --- a/gst/deinterlace/gstdeinterlace.c +++ b/gst/deinterlace/gstdeinterlace.c @@ -609,7 +609,7 @@ gst_deinterlace_class_init (GstDeinterlaceClass * klass) * processing latency and accuracy of timestamp adjustment for telecine * streams. * - * Since: 0.10.29. + * Since: 0.10.31 * */ g_object_class_install_property (gobject_class, PROP_LOCKING, @@ -623,7 +623,7 @@ gst_deinterlace_class_init (GstDeinterlaceClass * klass) * This selects whether to ignore obscure/rare telecine patterns. * NTSC 2:3 pulldown variants are the only really common patterns. * - * Since: 0.10.29. + * Since: 0.10.31 * */ g_object_class_install_property (gobject_class, PROP_IGNORE_OBSCURE, @@ -638,7 +638,7 @@ gst_deinterlace_class_init (GstDeinterlaceClass * klass) * This selects whether to drop orphan fields at the beginning of telecine * patterns in active locking mode. * - * Since: 0.10.29. + * Since: 0.10.31 * */ g_object_class_install_property (gobject_class, PROP_DROP_ORPHANS, From a1712ad87c20fd5abd08eea52293c91b7d7a6dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Tue, 2 Aug 2011 23:42:58 +0100 Subject: [PATCH 28/32] docs: fix two more Since: tags --- gst/multipart/multipartdemux.c | 2 +- sys/v4l2/gstv4l2object.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gst/multipart/multipartdemux.c b/gst/multipart/multipartdemux.c index b8c6e1c52a..a0d6effdf8 100644 --- a/gst/multipart/multipartdemux.c +++ b/gst/multipart/multipartdemux.c @@ -174,7 +174,7 @@ gst_multipart_demux_class_init (GstMultipartDemuxClass * klass) * not change and emit no-more-pads as soon as the first boundary * content is parsed, decoded, and pads are linked. * - * Since: 0.10.30 + * Since: 0.10.31 */ g_object_class_install_property (gobject_class, PROP_SINGLE_STREAM, g_param_spec_boolean ("single-stream", "Single Stream", diff --git a/sys/v4l2/gstv4l2object.c b/sys/v4l2/gstv4l2object.c index 29249f9b4d..ae1b591b80 100644 --- a/sys/v4l2/gstv4l2object.c +++ b/sys/v4l2/gstv4l2object.c @@ -442,7 +442,7 @@ gst_v4l2_object_install_properties_helper (GObjectClass * gobject_class, * * TV norm * - * Since: 0.10.30 + * Since: 0.10.31 */ g_object_class_install_property (gobject_class, PROP_TV_NORM, g_param_spec_enum ("norm", "TV norm", From c26442a3bae0f93947f7dc4f858b968a254842ee Mon Sep 17 00:00:00 2001 From: Tristan Matthews Date: Tue, 2 Aug 2011 22:05:08 -0400 Subject: [PATCH 29/32] jackaudiosink: Don't call g_alloca() in process_cb g_alloca() is not RT-safe, so instead we should allocate the memory needed in advance. Fixes #655866 --- ext/jack/gstjackaudiosink.c | 16 +++++++++------- ext/jack/gstjackaudiosink.h | 1 + 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/ext/jack/gstjackaudiosink.c b/ext/jack/gstjackaudiosink.c index e9190507f0..0aa578b756 100644 --- a/ext/jack/gstjackaudiosink.c +++ b/ext/jack/gstjackaudiosink.c @@ -83,6 +83,7 @@ gst_jack_audio_sink_allocate_channels (GstJackAudioSink * sink, gint channels) /* alloc enough output ports */ sink->ports = g_realloc (sink->ports, sizeof (jack_port_t *) * channels); + sink->buffers = g_realloc (sink->buffers, sizeof (sample_t *) * channels); /* create an output port for each channel */ while (sink->port_count < channels) { @@ -123,6 +124,8 @@ gst_jack_audio_sink_free_channels (GstJackAudioSink * sink) } g_free (sink->ports); sink->ports = NULL; + g_free (sink->buffers); + sink->buffers = NULL; } /* ringbuffer abstract base class */ @@ -187,19 +190,17 @@ jack_process_cb (jack_nframes_t nframes, void *arg) gint readseg, len; guint8 *readptr; gint i, j, flen, channels; - sample_t **buffers, *data; + sample_t *data; buf = GST_RING_BUFFER_CAST (arg); sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf)); channels = buf->spec.channels; - /* alloc pointers to samples */ - buffers = g_alloca (sizeof (sample_t *) * channels); - /* get target buffers */ for (i = 0; i < channels; i++) { - buffers[i] = (sample_t *) jack_port_get_buffer (sink->ports[i], nframes); + sink->buffers[i] = + (sample_t *) jack_port_get_buffer (sink->ports[i], nframes); } if (gst_ring_buffer_prepare_read (buf, &readseg, &readptr, &len)) { @@ -217,7 +218,7 @@ jack_process_cb (jack_nframes_t nframes, void *arg) * deinterleave into the jack target buffers */ for (i = 0; i < nframes; i++) { for (j = 0; j < channels; j++) { - buffers[j][i] = *data++; + sink->buffers[j][i] = *data++; } } @@ -231,7 +232,7 @@ jack_process_cb (jack_nframes_t nframes, void *arg) /* We are not allowed to read from the ringbuffer, write silence to all * jack output buffers */ for (i = 0; i < channels; i++) { - memset (buffers[i], 0, nframes * sizeof (sample_t)); + memset (sink->buffers[i], 0, nframes * sizeof (sample_t)); } } return 0; @@ -737,6 +738,7 @@ gst_jack_audio_sink_init (GstJackAudioSink * sink, sink->jclient = NULL; sink->ports = NULL; sink->port_count = 0; + sink->buffers = NULL; } static void diff --git a/ext/jack/gstjackaudiosink.h b/ext/jack/gstjackaudiosink.h index def423329d..6f9c45c8eb 100644 --- a/ext/jack/gstjackaudiosink.h +++ b/ext/jack/gstjackaudiosink.h @@ -65,6 +65,7 @@ struct _GstJackAudioSink { /* our ports */ jack_port_t **ports; int port_count; + sample_t **buffers; }; struct _GstJackAudioSinkClass { From f7893b8721832cdcbfd80b9fdb78143637c7d4ae Mon Sep 17 00:00:00 2001 From: Robert Krakora Date: Wed, 3 Aug 2011 10:09:42 +0200 Subject: [PATCH 30/32] rtpjpegpay: Add support for H.264 payload in MJPEG container See http://www.quickcamteam.net/uvc-h264/USB_Video_Payload_H.264_0.87.pdf Fixes bug #655530. --- gst/rtp/gstrtpjpegpay.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gst/rtp/gstrtpjpegpay.c b/gst/rtp/gstrtpjpegpay.c index f22384709f..33e026bb73 100644 --- a/gst/rtp/gstrtpjpegpay.c +++ b/gst/rtp/gstrtpjpegpay.c @@ -89,6 +89,7 @@ typedef enum _RtpJpegMarker RtpJpegMarker; * @JPEG_MARKER_SOS: Start of Scan marker * @JPEG_MARKER_EOI: End of Image marker * @JPEG_MARKER_DRI: Define Restart Interval marker + * @JPEG_MARKER_H264: H264 marker * * Identifers for markers in JPEG header */ @@ -103,7 +104,8 @@ enum _RtpJpegMarker JPEG_MARKER_DHT = 0xC4, JPEG_MARKER_SOS = 0xDA, JPEG_MARKER_EOI = 0xD9, - JPEG_MARKER_DRI = 0xDD + JPEG_MARKER_DRI = 0xDD, + JPEG_MARKER_H264 = 0xE4 }; #define DEFAULT_JPEG_QUANT 255 @@ -590,7 +592,7 @@ gst_rtp_jpeg_pay_scan_marker (const guint8 * data, guint size, guint * offset) guint8 marker; marker = data[*offset]; - GST_LOG ("found %02x marker at offset %u", marker, *offset); + GST_LOG ("found 0x%02x marker at offset %u", marker, *offset); (*offset)++; return marker; } @@ -644,6 +646,7 @@ gst_rtp_jpeg_pay_handle_buffer (GstBaseRTPPayload * basepayload, case JPEG_MARKER_JFIF: case JPEG_MARKER_CMT: case JPEG_MARKER_DHT: + case JPEG_MARKER_H264: GST_LOG_OBJECT (pay, "skipping marker"); offset += gst_rtp_jpeg_pay_header_size (data, offset); break; From 0424368cfc66445b41a58f805f6f024eb8e20ab7 Mon Sep 17 00:00:00 2001 From: Philippe Normand Date: Fri, 29 Jul 2011 13:03:55 +0200 Subject: [PATCH 31/32] qtdemux: soften assertion check on stream size https://bugzilla.gnome.org/show_bug.cgi?id=655570 --- gst/isomp4/qtdemux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/isomp4/qtdemux.c b/gst/isomp4/qtdemux.c index 898fc71f6d..dd6d173f5e 100644 --- a/gst/isomp4/qtdemux.c +++ b/gst/isomp4/qtdemux.c @@ -7458,7 +7458,7 @@ gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux) /* Subtract the header size */ GST_DEBUG_OBJECT (qtdemux, "Total size %" G_GINT64_FORMAT ", header size %u", size, qtdemux->header_size); - g_assert (size > qtdemux->header_size); + g_assert (size >= qtdemux->header_size); size = size - qtdemux->header_size; if (!gst_qtdemux_get_duration (qtdemux, &duration) || From 1438bf26ac6f2cc5c1cd3c6d6cba7ddc9ccda390 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Wed, 3 Aug 2011 22:50:05 +1000 Subject: [PATCH 32/32] matroska: Register new debug category Register the matroskareadcommon debug category when the plugin is loaded to avoid assertion output when debug is turned on. --- gst/matroska/matroska-read-common.c | 2 +- gst/matroska/matroska-read-common.h | 2 ++ gst/matroska/matroska.c | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/gst/matroska/matroska-read-common.c b/gst/matroska/matroska-read-common.c index 4431a51af8..32b513e12b 100644 --- a/gst/matroska/matroska-read-common.c +++ b/gst/matroska/matroska-read-common.c @@ -45,7 +45,7 @@ #include "ebml-read.h" #include "matroska-read-common.h" -GST_DEBUG_CATEGORY_STATIC (matroskareadcommon_debug); +GST_DEBUG_CATEGORY (matroskareadcommon_debug); #define GST_CAT_DEFAULT matroskareadcommon_debug #define DEBUG_ELEMENT_START(common, ebml, element) \ diff --git a/gst/matroska/matroska-read-common.h b/gst/matroska/matroska-read-common.h index 68b0f6c34f..cf617e6e8e 100644 --- a/gst/matroska/matroska-read-common.h +++ b/gst/matroska/matroska-read-common.h @@ -31,6 +31,8 @@ G_BEGIN_DECLS +GST_DEBUG_CATEGORY_EXTERN(matroskareadcommon_debug); + typedef enum { GST_MATROSKA_READ_STATE_START, GST_MATROSKA_READ_STATE_SEGMENT, diff --git a/gst/matroska/matroska.c b/gst/matroska/matroska.c index 57db7a31e0..5391400401 100644 --- a/gst/matroska/matroska.c +++ b/gst/matroska/matroska.c @@ -25,6 +25,7 @@ #include "matroska-demux.h" #include "matroska-parse.h" +#include "matroska-read-common.h" #include "matroska-mux.h" #include "matroska-ids.h" #include "webm-mux.h" @@ -40,6 +41,9 @@ plugin_init (GstPlugin * plugin) gst_matroska_register_tags (); + GST_DEBUG_CATEGORY_INIT (matroskareadcommon_debug, "matroskareadcommon", 0, + "Matroska demuxer/parser shared debug"); + ret = gst_matroska_demux_plugin_init (plugin); ret &= gst_matroska_parse_plugin_init (plugin); ret &= gst_element_register (plugin, "matroskamux", GST_RANK_PRIMARY,