diff options
author | Sebastian Dröge <sebastian.droege@collabora.co.uk> | 2012-01-10 14:32:32 +0100 |
---|---|---|
committer | Sebastian Dröge <sebastian.droege@collabora.co.uk> | 2012-01-10 14:32:32 +0100 |
commit | 93e3ed5a869afb79f9658f22ced4691ce475e8a1 (patch) | |
tree | baaec96daaf22857d61128401c12e2649ef25304 | |
parent | 692e00cc00d772fe2b01f518cb9ac88d124807d1 (diff) | |
parent | 2b2c0940f1b7ce8a858a26ad5e246fd645a83830 (diff) |
Merge branch 'master' into 0.11
Conflicts:
ext/cairo/gsttextoverlay.c
ext/pulse/pulseaudiosink.c
gst/audioparsers/gstaacparse.c
gst/avi/gstavimux.c
gst/flv/gstflvmux.c
gst/interleave/interleave.c
gst/isomp4/gstqtmux.c
gst/matroska/matroska-demux.c
gst/matroska/matroska-mux.c
gst/matroska/matroska-mux.h
gst/matroska/matroska-read-common.c
gst/multifile/gstmultifilesink.c
gst/multipart/multipartmux.c
gst/shapewipe/gstshapewipe.c
gst/smpte/gstsmpte.c
gst/udp/gstmultiudpsink.c
gst/videobox/gstvideobox.c
gst/videocrop/gstaspectratiocrop.c
gst/videomixer/videomixer.c
gst/videomixer/videomixer2.c
gst/wavparse/gstwavparse.c
po/ja.po
po/lv.po
po/sr.po
tests/check/Makefile.am
tests/check/elements/qtmux.c
tests/check/elements/rgvolume.c
85 files changed, 2635 insertions, 681 deletions
diff --git a/Makefile.am b/Makefile.am index 0119fad4c..9638883cb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -26,7 +26,8 @@ DISTCLEANFILES = _stdint.h noinst_HEADERS = \ gst-libs/gst/gettext.h \ - gst-libs/gst/gst-i18n-plugin.h + gst-libs/gst/gst-i18n-plugin.h \ + gst-libs/gst/glib-compat-private.h ACLOCAL_AMFLAGS = -I m4 -I common/m4 diff --git a/configure.ac b/configure.ac index f2e608bda..5e16f67b1 100644 --- a/configure.ac +++ b/configure.ac @@ -203,7 +203,7 @@ AC_CHECK_TYPE([struct ip_mreqn], [ dnl *** checks for dependency libraries *** dnl GLib is required -AG_GST_GLIB_CHECK([2.20]) +AG_GST_GLIB_CHECK([2.24]) PKG_CHECK_MODULES(GIO, [ gio-2.0 >= 2.20 ], , AC_MSG_ERROR([gio is required])) dnl Orc @@ -438,6 +438,12 @@ int main () AC_SUBST(HAVE_DIRECTSOUND) ]) +dnl *** Win32 WaveOut *** +translit(dnm, m, l) AM_CONDITIONAL(USE_WAVEFORM, true) +AG_GST_CHECK_FEATURE(WAVEFORM, [Win32 WaveForm], waveformsink, [ + AC_CHECK_HEADER(mmsystem.h, HAVE_WAVEFORM="yes", HAVE_WAVEFORM="no", [#include <windows.h>]) +]) + dnl *** OSS audio *** (Linux, *BSD) translit(dnm, m, l) AM_CONDITIONAL(USE_OSS, true) AG_GST_CHECK_FEATURE(OSS, [OSS audio], ossaudio, [ @@ -1005,6 +1011,7 @@ AM_CONDITIONAL(USE_SOUP, false) AM_CONDITIONAL(USE_SPEEX, false) AM_CONDITIONAL(USE_SUNAUDIO, false) AM_CONDITIONAL(USE_TAGLIB, false) +AM_CONDITIONAL(USE_WAVEFORM, false) AM_CONDITIONAL(USE_WAVPACK, false) AM_CONDITIONAL(USE_X, false) AM_CONDITIONAL(USE_XSHM, false) diff --git a/ext/cairo/gsttextoverlay.c b/ext/cairo/gsttextoverlay.c index 7d79f98f7..2a09b9cb1 100644 --- a/ext/cairo/gsttextoverlay.c +++ b/ext/cairo/gsttextoverlay.c @@ -106,7 +106,7 @@ static gboolean gst_text_overlay_setcaps (GstPad * pad, GstCaps * caps); static GstPadLinkReturn gst_text_overlay_text_pad_linked (GstPad * pad, GstPad * peer); static void gst_text_overlay_text_pad_unlinked (GstPad * pad); -static GstFlowReturn gst_text_overlay_collected (GstCollectPads * pads, +static GstFlowReturn gst_text_overlay_collected (GstCollectPads2 * pads, gpointer data); static void gst_text_overlay_finalize (GObject * object); static void gst_text_overlay_font_init (GstCairoTextOverlay * overlay); @@ -215,7 +215,7 @@ gst_text_overlay_finalize (GObject * object) { GstCairoTextOverlay *overlay = GST_CAIRO_TEXT_OVERLAY (object); - gst_collect_pads_stop (overlay->collect); + gst_collect_pads2_stop (overlay->collect); gst_object_unref (overlay->collect); g_free (overlay->text_fill_image); @@ -279,16 +279,16 @@ gst_text_overlay_init (GstCairoTextOverlay * overlay, overlay->fps_n = 0; overlay->fps_d = 1; - overlay->collect = gst_collect_pads_new (); + overlay->collect = gst_collect_pads2_new (); - gst_collect_pads_set_function (overlay->collect, + gst_collect_pads2_set_function (overlay->collect, GST_DEBUG_FUNCPTR (gst_text_overlay_collected), overlay); - overlay->video_collect_data = gst_collect_pads_add_pad (overlay->collect, - overlay->video_sinkpad, sizeof (GstCollectData), NULL); + overlay->video_collect_data = gst_collect_pads2_add_pad (overlay->collect, + overlay->video_sinkpad, sizeof (GstCollectData2)); /* FIXME: hacked way to override/extend the event function of - * GstCollectPads; because it sets its own event function giving the + * GstCollectPads2; because it sets its own event function giving the * element no access to events. Nicked from avimux. */ overlay->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (overlay->video_sinkpad); @@ -640,8 +640,8 @@ gst_text_overlay_text_pad_linked (GstPad * pad, GstPad * peer) GST_DEBUG_OBJECT (overlay, "Text pad linked"); if (overlay->text_collect_data == NULL) { - overlay->text_collect_data = gst_collect_pads_add_pad (overlay->collect, - overlay->text_sinkpad, sizeof (GstCollectData), NULL); + overlay->text_collect_data = gst_collect_pads2_add_pad (overlay->collect, + overlay->text_sinkpad, sizeof (GstCollectData2)); } overlay->need_render = TRUE; @@ -660,7 +660,7 @@ gst_text_overlay_text_pad_unlinked (GstPad * pad) GST_DEBUG_OBJECT (overlay, "Text pad unlinked"); if (overlay->text_collect_data) { - gst_collect_pads_remove_pad (overlay->collect, overlay->text_sinkpad); + gst_collect_pads2_remove_pad (overlay->collect, overlay->text_sinkpad); overlay->text_collect_data = NULL; } @@ -807,7 +807,7 @@ gst_text_overlay_pop_video (GstCairoTextOverlay * overlay) { GstBuffer *buf; - buf = gst_collect_pads_pop (overlay->collect, overlay->video_collect_data); + buf = gst_collect_pads2_pop (overlay->collect, overlay->video_collect_data); g_return_if_fail (buf != NULL); gst_buffer_unref (buf); } @@ -818,7 +818,7 @@ gst_text_overlay_pop_text (GstCairoTextOverlay * overlay) GstBuffer *buf; if (overlay->text_collect_data) { - buf = gst_collect_pads_pop (overlay->collect, overlay->text_collect_data); + buf = gst_collect_pads2_pop (overlay->collect, overlay->text_collect_data); g_return_if_fail (buf != NULL); gst_buffer_unref (buf); } @@ -828,7 +828,7 @@ gst_text_overlay_pop_text (GstCairoTextOverlay * overlay) /* This function is called when there is data on all pads */ static GstFlowReturn -gst_text_overlay_collected (GstCollectPads * pads, gpointer data) +gst_text_overlay_collected (GstCollectPads2 * pads, gpointer data) { GstCairoTextOverlay *overlay; GstFlowReturn ret = GST_FLOW_OK; @@ -842,14 +842,14 @@ gst_text_overlay_collected (GstCollectPads * pads, gpointer data) GST_DEBUG ("Collecting"); - video_frame = gst_collect_pads_peek (overlay->collect, + video_frame = gst_collect_pads2_peek (overlay->collect, overlay->video_collect_data); /* send EOS if video stream EOSed regardless of text stream */ if (video_frame == NULL) { GST_DEBUG ("Video stream at EOS"); if (overlay->text_collect_data) { - text_buf = gst_collect_pads_pop (overlay->collect, + text_buf = gst_collect_pads2_pop (overlay->collect, overlay->text_collect_data); } gst_pad_push_event (overlay->srcpad, gst_event_new_eos ()); @@ -892,7 +892,7 @@ gst_text_overlay_collected (GstCollectPads * pads, gpointer data) goto done; } - text_buf = gst_collect_pads_peek (overlay->collect, + text_buf = gst_collect_pads2_peek (overlay->collect, overlay->text_collect_data); /* just push the video frame if the text stream has EOSed */ @@ -1004,7 +1004,7 @@ gst_text_overlay_video_event (GstPad * pad, GstEvent * event) gst_pad_push_event (overlay->srcpad, event); } - /* now GstCollectPads can take care of the rest, e.g. EOS */ + /* now GstCollectPads2 can take care of the rest, e.g. EOS */ ret = overlay->collect_event (pad, event); gst_object_unref (overlay); return ret; @@ -1018,12 +1018,12 @@ gst_text_overlay_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: - gst_collect_pads_start (overlay->collect); + gst_collect_pads2_start (overlay->collect); break; case GST_STATE_CHANGE_PAUSED_TO_READY: /* need to unblock the collectpads before calling the * parent change_state so that streaming can finish */ - gst_collect_pads_stop (overlay->collect); + gst_collect_pads2_stop (overlay->collect); break; default: break; diff --git a/ext/cairo/gsttextoverlay.h b/ext/cairo/gsttextoverlay.h index dbb21546f..1f98d3d58 100644 --- a/ext/cairo/gsttextoverlay.h +++ b/ext/cairo/gsttextoverlay.h @@ -3,7 +3,7 @@ #define __GST_CAIRO_TEXT_OVERLAY_H__ #include <gst/gst.h> -#include <gst/base/gstcollectpads.h> +#include <gst/base/gstcollectpads2.h> G_BEGIN_DECLS @@ -45,9 +45,9 @@ struct _GstCairoTextOverlay { GstPad *text_sinkpad; GstPad *srcpad; - GstCollectPads *collect; - GstCollectData *video_collect_data; - GstCollectData *text_collect_data; + GstCollectPads2 *collect; + GstCollectData2 *video_collect_data; + GstCollectData2 *text_collect_data; GstPadEventFunction collect_event; gint width; diff --git a/ext/dv/gstdvdemux.c b/ext/dv/gstdvdemux.c index d236c3ffa..d3618ed19 100644 --- a/ext/dv/gstdvdemux.c +++ b/ext/dv/gstdvdemux.c @@ -21,6 +21,11 @@ #ifdef HAVE_CONFIG_H #include "config.h" #endif + +/* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex + * with newer GLib versions (>= 2.31.0) */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + #include <string.h> #include <math.h> diff --git a/ext/flac/gstflacdec.c b/ext/flac/gstflacdec.c index 94c0402cc..7066d7db9 100644 --- a/ext/flac/gstflacdec.c +++ b/ext/flac/gstflacdec.c @@ -41,6 +41,11 @@ #ifdef HAVE_CONFIG_H #include "config.h" #endif + +/* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex + * with newer GLib versions (>= 2.31.0) */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + #include <string.h> #include "gstflacdec.h" diff --git a/ext/gdk_pixbuf/gstgdkpixbufsink.c b/ext/gdk_pixbuf/gstgdkpixbufsink.c index f18d3c183..923538677 100644 --- a/ext/gdk_pixbuf/gstgdkpixbufsink.c +++ b/ext/gdk_pixbuf/gstgdkpixbufsink.c @@ -271,7 +271,7 @@ gst_gdk_pixbuf_sink_set_caps (GstBaseSink * basesink, GstCaps * caps) GST_INFO_OBJECT (sink, "format : %d", fmt); GST_INFO_OBJECT (sink, "width x height : %d x %d", w, h); - GST_INFO_OBJECT (sink, "pixel-aspect-ratio : %d/%d", par_d, par_n); + GST_INFO_OBJECT (sink, "pixel-aspect-ratio : %d/%d", par_n, par_d); return TRUE; } @@ -344,7 +344,7 @@ gst_gdk_pixbuf_sink_handle_buffer (GstBaseSink * basesink, GstBuffer * buf, * The structure will take its own ref to the pixbuf. */ s = gst_structure_new (msg_name, "pixbuf", GDK_TYPE_PIXBUF, pixbuf, - "pixel-aspect-ratio", GST_TYPE_FRACTION, sink->par_d, sink->par_n, + "pixel-aspect-ratio", GST_TYPE_FRACTION, sink->par_n, sink->par_d, NULL); msg = gst_message_new_element (GST_OBJECT_CAST (sink), s); diff --git a/ext/jack/gstjackaudioclient.c b/ext/jack/gstjackaudioclient.c index 1789edb60..2bb355529 100644 --- a/ext/jack/gstjackaudioclient.c +++ b/ext/jack/gstjackaudioclient.c @@ -23,6 +23,8 @@ #include "gstjackaudioclient.h" +#include <gst/glib-compat-private.h> + GST_DEBUG_CATEGORY_STATIC (gst_jack_audio_client_debug); #define GST_CAT_DEFAULT gst_jack_audio_client_debug diff --git a/ext/jack/gstjackaudiosink.c b/ext/jack/gstjackaudiosink.c index fe911840f..8055f9219 100644 --- a/ext/jack/gstjackaudiosink.c +++ b/ext/jack/gstjackaudiosink.c @@ -329,7 +329,11 @@ gst_jack_ring_buffer_open_device (GstAudioRingBuffer * buf) GST_DEBUG_OBJECT (sink, "open"); - name = g_get_application_name (); + if (sink->client_name) { + name = sink->client_name; + } else { + name = g_get_application_name (); + } if (!name) name = "GStreamer"; @@ -648,8 +652,9 @@ enum SIGNAL_LAST }; -#define DEFAULT_PROP_CONNECT GST_JACK_CONNECT_AUTO -#define DEFAULT_PROP_SERVER NULL +#define DEFAULT_PROP_CONNECT GST_JACK_CONNECT_AUTO +#define DEFAULT_PROP_SERVER NULL +#define DEFAULT_PROP_CLIENT_NAME NULL enum { @@ -657,6 +662,7 @@ enum PROP_CONNECT, PROP_SERVER, PROP_CLIENT, + PROP_CLIENT_NAME, PROP_LAST }; @@ -705,6 +711,19 @@ gst_jack_audio_sink_class_init (GstJackAudioSinkClass * klass) "The Jack server to connect to (NULL = default)", DEFAULT_PROP_SERVER, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * GstJackAudioSink:client-name + * + * The client name to use. + * + * Since: 0.10.31 + */ + g_object_class_install_property (gobject_class, PROP_CLIENT_NAME, + g_param_spec_string ("client-name", "Client name", + "The client name of the Jack instance (NULL = default)", + DEFAULT_PROP_CLIENT_NAME, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_CLIENT, g_param_spec_boxed ("client", "JackClient", "Handle for jack client", GST_TYPE_JACK_CLIENT, @@ -738,6 +757,7 @@ gst_jack_audio_sink_init (GstJackAudioSink * sink) sink->jclient = NULL; sink->ports = NULL; sink->port_count = 0; + sink->client_name = g_strdup (DEFAULT_PROP_CLIENT_NAME); sink->buffers = NULL; } @@ -747,6 +767,12 @@ gst_jack_audio_sink_dispose (GObject * object) GstJackAudioSink *sink = GST_JACK_AUDIO_SINK (object); gst_caps_replace (&sink->caps, NULL); + + if (sink->client_name != NULL) { + g_free (sink->client_name); + sink->client_name = NULL; + } + G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -759,6 +785,10 @@ gst_jack_audio_sink_set_property (GObject * object, guint prop_id, sink = GST_JACK_AUDIO_SINK (object); switch (prop_id) { + case PROP_CLIENT_NAME: + g_free (sink->client_name); + sink->client_name = g_value_dup_string (value); + break; case PROP_CONNECT: sink->connect = g_value_get_enum (value); break; @@ -787,6 +817,9 @@ gst_jack_audio_sink_get_property (GObject * object, guint prop_id, sink = GST_JACK_AUDIO_SINK (object); switch (prop_id) { + case PROP_CLIENT_NAME: + g_value_set_string (value, sink->client_name); + break; case PROP_CONNECT: g_value_set_enum (value, sink->connect); break; diff --git a/ext/jack/gstjackaudiosink.h b/ext/jack/gstjackaudiosink.h index 02a8db9fc..0dbc142e0 100644 --- a/ext/jack/gstjackaudiosink.h +++ b/ext/jack/gstjackaudiosink.h @@ -58,6 +58,7 @@ struct _GstJackAudioSink { GstJackConnect connect; gchar *server; jack_client_t *jclient; + gchar *client_name; /* our client */ GstJackAudioClient *client; diff --git a/ext/jack/gstjackaudiosrc.c b/ext/jack/gstjackaudiosrc.c index 4973a8b77..cf21cbfc1 100644 --- a/ext/jack/gstjackaudiosrc.c +++ b/ext/jack/gstjackaudiosrc.c @@ -336,7 +336,11 @@ gst_jack_ring_buffer_open_device (GstAudioRingBuffer * buf) GST_DEBUG_OBJECT (src, "open"); - name = g_get_application_name (); + if (src->client_name) { + name = src->client_name; + } else { + name = g_get_application_name (); + } if (!name) name = "GStreamer"; @@ -651,8 +655,9 @@ enum LAST_SIGNAL }; -#define DEFAULT_PROP_CONNECT GST_JACK_CONNECT_AUTO -#define DEFAULT_PROP_SERVER NULL +#define DEFAULT_PROP_CONNECT GST_JACK_CONNECT_AUTO +#define DEFAULT_PROP_SERVER NULL +#define DEFAULT_PROP_CLIENT_NAME NULL enum { @@ -660,6 +665,7 @@ enum PROP_CONNECT, PROP_SERVER, PROP_CLIENT, + PROP_CLIENT_NAME, PROP_LAST }; @@ -726,6 +732,19 @@ gst_jack_audio_src_class_init (GstJackAudioSrcClass * klass) "The Jack server to connect to (NULL = default)", DEFAULT_PROP_SERVER, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * GstJackAudioSrc:client-name + * + * The client name to use. + * + * Since: 0.10.31 + */ + g_object_class_install_property (gobject_class, PROP_CLIENT_NAME, + g_param_spec_string ("client-name", "Client name", + "The client name of the Jack instance (NULL = default)", + DEFAULT_PROP_CLIENT_NAME, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_CLIENT, g_param_spec_boxed ("client", "JackClient", "Handle for jack client", GST_TYPE_JACK_CLIENT, @@ -765,6 +784,7 @@ gst_jack_audio_src_init (GstJackAudioSrc * src) src->ports = NULL; src->port_count = 0; src->buffers = NULL; + src->client_name = g_strdup (DEFAULT_PROP_CLIENT_NAME); } static void @@ -773,6 +793,12 @@ gst_jack_audio_src_dispose (GObject * object) GstJackAudioSrc *src = GST_JACK_AUDIO_SRC (object); gst_caps_replace (&src->caps, NULL); + + if (src->client_name != NULL) { + g_free (src->client_name); + src->client_name = NULL; + } + G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -783,6 +809,10 @@ gst_jack_audio_src_set_property (GObject * object, guint prop_id, GstJackAudioSrc *src = GST_JACK_AUDIO_SRC (object); switch (prop_id) { + case PROP_CLIENT_NAME: + g_free (src->client_name); + src->client_name = g_value_dup_string (value); + break; case PROP_CONNECT: src->connect = g_value_get_enum (value); break; @@ -809,6 +839,9 @@ gst_jack_audio_src_get_property (GObject * object, guint prop_id, GstJackAudioSrc *src = GST_JACK_AUDIO_SRC (object); switch (prop_id) { + case PROP_CLIENT_NAME: + g_value_set_string (value, src->client_name); + break; case PROP_CONNECT: g_value_set_enum (value, src->connect); break; diff --git a/ext/jack/gstjackaudiosrc.h b/ext/jack/gstjackaudiosrc.h index fa119472c..1401d9b0d 100644 --- a/ext/jack/gstjackaudiosrc.h +++ b/ext/jack/gstjackaudiosrc.h @@ -75,6 +75,7 @@ struct _GstJackAudioSrc GstJackConnect connect; gchar *server; jack_client_t *jclient; + gchar *client_name; /* our client */ GstJackAudioClient *client; diff --git a/ext/jpeg/gstjpegdec.c b/ext/jpeg/gstjpegdec.c index 15658ed25..8301214ec 100644 --- a/ext/jpeg/gstjpegdec.c +++ b/ext/jpeg/gstjpegdec.c @@ -761,7 +761,7 @@ gst_jpeg_dec_getcaps (GstPad * pad, GstCaps * filter) templ_caps = gst_pad_get_pad_template_caps (pad); caps = gst_caps_intersect_full (peer_caps, templ_caps, GST_CAPS_INTERSECT_FIRST); - + gst_caps_unref (peer_caps); gst_object_unref (peer); } else { caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); diff --git a/ext/pulse/pulsesink.c b/ext/pulse/pulsesink.c index 4fa873c4d..371863e7a 100644 --- a/ext/pulse/pulsesink.c +++ b/ext/pulse/pulsesink.c @@ -58,6 +58,8 @@ #include <gst/pbutils/pbutils.h> /* only used for GST_PLUGINS_BASE_VERSION_* */ +#include <gst/glib-compat-private.h> + #include "pulsesink.h" #include "pulseutil.h" diff --git a/ext/soup/gstsouphttpclientsink.c b/ext/soup/gstsouphttpclientsink.c index af238277f..d8f46df9d 100644 --- a/ext/soup/gstsouphttpclientsink.c +++ b/ext/soup/gstsouphttpclientsink.c @@ -42,6 +42,8 @@ #include <gst/base/gstbasesink.h> #include "gstsouphttpclientsink.h" +#include <gst/glib-compat-private.h> + GST_DEBUG_CATEGORY_STATIC (souphttpclientsink_dbg); #define GST_CAT_DEFAULT souphttpclientsink_dbg @@ -445,6 +447,20 @@ gst_soup_http_client_sink_get_times (GstBaseSink * sink, GstBuffer * buffer, } +static gboolean +thread_ready_idle_cb (gpointer data) +{ + GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (data); + + GST_LOG_OBJECT (souphttpsink, "thread ready"); + + g_mutex_lock (souphttpsink->mutex); + g_cond_signal (souphttpsink->cond); + g_mutex_unlock (souphttpsink->mutex); + + return FALSE; /* only run once */ +} + static gpointer thread_func (gpointer ptr) { @@ -452,7 +468,6 @@ thread_func (gpointer ptr) GST_DEBUG ("thread start"); - souphttpsink->loop = g_main_loop_new (souphttpsink->context, TRUE); g_main_loop_run (souphttpsink->loop); GST_DEBUG ("thread quit"); @@ -468,12 +483,35 @@ gst_soup_http_client_sink_start (GstBaseSink * sink) if (souphttpsink->prop_session) { souphttpsink->session = souphttpsink->prop_session; } else { + GSource *source; GError *error = NULL; souphttpsink->context = g_main_context_new (); + /* set up idle source to signal when the main loop is running and + * it's safe for ::stop() to call g_main_loop_quit() */ + source = g_idle_source_new (); + g_source_set_callback (source, thread_ready_idle_cb, sink, NULL); + g_source_attach (source, souphttpsink->context); + g_source_unref (source); + + souphttpsink->loop = g_main_loop_new (souphttpsink->context, TRUE); + + g_mutex_lock (souphttpsink->mutex); + + /* FIXME: error handling */ +#if !GLIB_CHECK_VERSION (2, 31, 0) souphttpsink->thread = g_thread_create (thread_func, souphttpsink, TRUE, &error); +#else + souphttpsink->thread = g_thread_try_new ("souphttpclientsink-thread", + thread_func, souphttpsink, &error); +#endif + + GST_LOG_OBJECT (souphttpsink, "waiting for main loop thread to start up"); + g_cond_wait (souphttpsink->cond, souphttpsink->mutex); + g_mutex_unlock (souphttpsink->mutex); + GST_LOG_OBJECT (souphttpsink, "main loop thread running"); souphttpsink->session = soup_session_async_new_with_options (SOUP_SESSION_ASYNC_CONTEXT, diff --git a/ext/wavpack/gstwavpackparse.c b/ext/wavpack/gstwavpackparse.c index 50e1df933..4bb1e7e2b 100644 --- a/ext/wavpack/gstwavpackparse.c +++ b/ext/wavpack/gstwavpackparse.c @@ -41,6 +41,11 @@ #ifdef HAVE_CONFIG_H #include "config.h" #endif + +/* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex + * with newer GLib versions (>= 2.31.0) */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + #include <gst/gst.h> #include <gst/gst-i18n-plugin.h> diff --git a/gst-libs/gst/glib-compat-private.h b/gst-libs/gst/glib-compat-private.h new file mode 100644 index 000000000..b9248e686 --- /dev/null +++ b/gst-libs/gst/glib-compat-private.h @@ -0,0 +1,135 @@ +/* + * glib-compat.c + * Functions copied from glib 2.10 + * + * Copyright 2005 David Schleef <ds@schleef.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GLIB_COMPAT_PRIVATE_H__ +#define __GLIB_COMPAT_PRIVATE_H__ + +#include <glib.h> + +G_BEGIN_DECLS + +#if !GLIB_CHECK_VERSION(2,25,0) + +#if defined (_MSC_VER) && !defined(_WIN64) +typedef struct _stat32 GStatBuf; +#else +typedef struct stat GStatBuf; +#endif + +#endif + +#if GLIB_CHECK_VERSION(2,26,0) +#define GLIB_HAS_GDATETIME +#endif + +/* See bug #651514 */ +#if GLIB_CHECK_VERSION(2,29,5) +#define G_ATOMIC_POINTER_COMPARE_AND_EXCHANGE(a,b,c) \ + g_atomic_pointer_compare_and_exchange ((a),(b),(c)) +#define G_ATOMIC_INT_COMPARE_AND_EXCHANGE(a,b,c) \ + g_atomic_int_compare_and_exchange ((a),(b),(c)) +#else +#define G_ATOMIC_POINTER_COMPARE_AND_EXCHANGE(a,b,c) \ + g_atomic_pointer_compare_and_exchange ((volatile gpointer *)(a),(b),(c)) +#define G_ATOMIC_INT_COMPARE_AND_EXCHANGE(a,b,c) \ + g_atomic_int_compare_and_exchange ((volatile int *)(a),(b),(c)) +#endif + +/* See bug #651514 */ +#if GLIB_CHECK_VERSION(2,29,5) +#define G_ATOMIC_INT_ADD(a,b) g_atomic_int_add ((a),(b)) +#else +#define G_ATOMIC_INT_ADD(a,b) g_atomic_int_exchange_and_add ((a),(b)) +#endif + +/* copies */ + +#if GLIB_CHECK_VERSION (2, 31, 0) +#define g_mutex_new gst_g_mutex_new +static inline GMutex * +gst_g_mutex_new (void) +{ + GMutex *mutex = g_slice_new (GMutex); + g_mutex_init (mutex); + return mutex; +} +#define g_mutex_free gst_g_mutex_free +static inline void +gst_g_mutex_free (GMutex *mutex) +{ + g_mutex_clear (mutex); + g_slice_free (GMutex, mutex); +} +#define g_static_rec_mutex_init gst_g_static_rec_mutex_init +static inline void +gst_g_static_rec_mutex_init (GStaticRecMutex *mutex) +{ + static const GStaticRecMutex init_mutex = G_STATIC_REC_MUTEX_INIT; + + *mutex = init_mutex; +} +#define g_cond_new gst_g_cond_new +static inline GCond * +gst_g_cond_new (void) +{ + GCond *cond = g_slice_new (GCond); + g_cond_init (cond); + return cond; +} +#define g_cond_free gst_g_cond_free +static inline void +gst_g_cond_free (GCond *cond) +{ + g_cond_clear (cond); + g_slice_free (GCond, cond); +} +#define g_cond_timed_wait gst_g_cond_timed_wait +static inline gboolean +gst_g_cond_timed_wait (GCond *cond, GMutex *mutex, GTimeVal *abs_time) +{ + gint64 end_time; + + if (abs_time == NULL) { + g_cond_wait (cond, mutex); + return TRUE; + } + + end_time = abs_time->tv_sec; + end_time *= 1000000; + end_time += abs_time->tv_usec; + + /* would be nice if we had clock_rtoffset, but that didn't seem to + * make it into the kernel yet... + */ + /* if CLOCK_MONOTONIC is not defined then g_get_montonic_time() and + * g_get_real_time() are returning the same clock and we'd add ~0 + */ + end_time += g_get_monotonic_time () - g_get_real_time (); + return g_cond_wait_until (cond, mutex, end_time); +} +#endif /* GLIB_CHECK_VERSION (2, 31, 0) */ + +/* adaptations */ + +G_END_DECLS + +#endif diff --git a/gst/alpha/gstalpha.c b/gst/alpha/gstalpha.c index c802c64e6..325bdc53b 100644 --- a/gst/alpha/gstalpha.c +++ b/gst/alpha/gstalpha.c @@ -145,6 +145,8 @@ GST_STATIC_PAD_TEMPLATE ("sink", static GstStaticCaps gst_alpha_alpha_caps = GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ AYUV, ARGB, BGRA, ABGR, RGBA }")); +/* FIXME: why do we need our own lock for this? */ +#if !GLIB_CHECK_VERSION (2, 31, 0) #define GST_ALPHA_LOCK(alpha) G_STMT_START { \ GST_LOG_OBJECT (alpha, "Locking alpha from thread %p", g_thread_self ()); \ g_static_mutex_lock (&alpha->lock); \ @@ -155,6 +157,18 @@ GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ AYUV, ARGB, BGRA, ABGR, RGBA }")); GST_LOG_OBJECT (alpha, "Unlocking alpha from thread %p", g_thread_self ()); \ g_static_mutex_unlock (&alpha->lock); \ } G_STMT_END +#else +#define GST_ALPHA_LOCK(alpha) G_STMT_START { \ + GST_LOG_OBJECT (alpha, "Locking alpha from thread %p", g_thread_self ()); \ + g_mutex_lock (&alpha->lock); \ + GST_LOG_OBJECT (alpha, "Locked alpha from thread %p", g_thread_self ()); \ +} G_STMT_END + +#define GST_ALPHA_UNLOCK(alpha) G_STMT_START { \ + GST_LOG_OBJECT (alpha, "Unlocking alpha from thread %p", g_thread_self ()); \ + g_mutex_unlock (&alpha->lock); \ +} G_STMT_END +#endif static GstCaps *gst_alpha_transform_caps (GstBaseTransform * btrans, GstPadDirection direction, GstCaps * caps, GstCaps * filter); @@ -296,7 +310,11 @@ gst_alpha_init (GstAlpha * alpha) alpha->black_sensitivity = DEFAULT_BLACK_SENSITIVITY; alpha->white_sensitivity = DEFAULT_WHITE_SENSITIVITY; +#if !GLIB_CHECK_VERSION (2, 31, 0) g_static_mutex_init (&alpha->lock); +#else + g_mutex_init (&alpha->lock); +#endif } static void @@ -304,7 +322,11 @@ gst_alpha_finalize (GObject * object) { GstAlpha *alpha = GST_ALPHA (object); +#if !GLIB_CHECK_VERSION (2, 31, 0) g_static_mutex_free (&alpha->lock); +#else + g_mutex_clear (&alpha->lock); +#endif G_OBJECT_CLASS (parent_class)->finalize (object); } diff --git a/gst/alpha/gstalpha.h b/gst/alpha/gstalpha.h index c24b753ba..48592f1bb 100644 --- a/gst/alpha/gstalpha.h +++ b/gst/alpha/gstalpha.h @@ -69,7 +69,11 @@ struct _GstAlpha /* <private> */ /* caps */ +#if !GLIB_CHECK_VERSION (2, 31, 0) GStaticMutex lock; +#else + GMutex lock; +#endif gboolean in_sdtv, out_sdtv; diff --git a/gst/audiofx/audiochebband.c b/gst/audiofx/audiochebband.c index f0d2e34f0..cf431618e 100644 --- a/gst/audiofx/audiochebband.c +++ b/gst/audiofx/audiochebband.c @@ -83,6 +83,8 @@ #include "audiochebband.h" +#include "gst/glib-compat-private.h" + #define GST_CAT_DEFAULT gst_audio_cheb_band_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); diff --git a/gst/audiofx/audiocheblimit.c b/gst/audiofx/audiocheblimit.c index 250857ec0..9654a0564 100644 --- a/gst/audiofx/audiocheblimit.c +++ b/gst/audiofx/audiocheblimit.c @@ -79,6 +79,8 @@ #include "audiocheblimit.h" +#include "gst/glib-compat-private.h" + #define GST_CAT_DEFAULT gst_audio_cheb_limit_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); diff --git a/gst/audiofx/audiofirfilter.c b/gst/audiofx/audiofirfilter.c index 414768920..9fa21111b 100644 --- a/gst/audiofx/audiofirfilter.c +++ b/gst/audiofx/audiofirfilter.c @@ -56,6 +56,8 @@ #include "audiofirfilter.h" +#include "gst/glib-compat-private.h" + #define GST_CAT_DEFAULT gst_audio_fir_filter_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); diff --git a/gst/audiofx/audioiirfilter.c b/gst/audiofx/audioiirfilter.c index f411d8871..dc3c17efb 100644 --- a/gst/audiofx/audioiirfilter.c +++ b/gst/audiofx/audioiirfilter.c @@ -52,6 +52,8 @@ #include "audioiirfilter.h" +#include "gst/glib-compat-private.h" + #define GST_CAT_DEFAULT gst_audio_iir_filter_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); diff --git a/gst/audiofx/audiowsincband.c b/gst/audiofx/audiowsincband.c index 3f23b561a..cd50b0655 100644 --- a/gst/audiofx/audiowsincband.c +++ b/gst/audiofx/audiowsincband.c @@ -63,6 +63,8 @@ #include "audiowsincband.h" +#include "gst/glib-compat-private.h" + #define GST_CAT_DEFAULT gst_gst_audio_wsincband_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); diff --git a/gst/audiofx/audiowsinclimit.c b/gst/audiofx/audiowsinclimit.c index f13d2d020..7a9f5713b 100644 --- a/gst/audiofx/audiowsinclimit.c +++ b/gst/audiofx/audiowsinclimit.c @@ -63,6 +63,8 @@ #include "audiowsinclimit.h" +#include "gst/glib-compat-private.h" + #define GST_CAT_DEFAULT gst_audio_wsinclimit_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); diff --git a/gst/audioparsers/gstaacparse.c b/gst/audioparsers/gstaacparse.c index 5c0d88a06..789731599 100644 --- a/gst/audioparsers/gstaacparse.c +++ b/gst/audioparsers/gstaacparse.c @@ -44,6 +44,7 @@ #include <string.h> +#include <gst/base/gstbitreader.h> #include "gstaacparse.h" @@ -52,7 +53,7 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_ALWAYS, GST_STATIC_CAPS ("audio/mpeg, " "framed = (boolean) true, " "mpegversion = (int) { 2, 4 }, " - "stream-format = (string) { raw, adts, adif };")); + "stream-format = (string) { raw, adts, adif, loas };")); static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, @@ -65,10 +66,21 @@ GST_DEBUG_CATEGORY_STATIC (aacparse_debug); #define ADIF_MAX_SIZE 40 /* Should be enough */ #define ADTS_MAX_SIZE 10 /* Should be enough */ +#define LOAS_MAX_SIZE 3 /* Should be enough */ #define AAC_FRAME_DURATION(parse) (GST_SECOND/parse->frames_per_sec) +static const gint loas_sample_rate_table[32] = { + 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000, 7350, 0, 0, 0 +}; + +static const gint loas_channels_table[32] = { + 0, 1, 2, 3, 4, 5, 6, 8, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + static gboolean gst_aac_parse_start (GstBaseParse * parse); static gboolean gst_aac_parse_stop (GstBaseParse * parse); @@ -190,6 +202,9 @@ gst_aac_parse_set_src_caps (GstAacParse * aacparse, GstCaps * sink_caps) case DSPAAC_HEADER_ADIF: stream_format = "adif"; break; + case DSPAAC_HEADER_LOAS: + stream_format = "loas"; + break; default: stream_format = NULL; } @@ -329,6 +344,8 @@ gst_aac_parse_check_adts_frame (GstAacParse * aacparse, const guint8 * data, const guint avail, gboolean drain, guint * framesize, guint * needed_data) { + *needed_data = 0; + if (G_UNLIKELY (avail < 2)) return FALSE; @@ -367,6 +384,292 @@ gst_aac_parse_check_adts_frame (GstAacParse * aacparse, return FALSE; } +static gboolean +gst_aac_parse_latm_get_value (GstAacParse * aacparse, GstBitReader * br, + guint32 * value) +{ + guint8 bytes, i, byte; + + *value = 0; + if (!gst_bit_reader_get_bits_uint8 (br, &bytes, 2)) + return FALSE; + for (i = 0; i < bytes; ++i) { + *value <<= 8; + if (!gst_bit_reader_get_bits_uint8 (br, &byte, 8)) + return FALSE; + *value += byte; + } + return TRUE; +} + +static gboolean +gst_aac_parse_get_audio_object_type (GstAacParse * aacparse, GstBitReader * br, + guint8 * audio_object_type) +{ + if (!gst_bit_reader_get_bits_uint8 (br, audio_object_type, 5)) + return FALSE; + if (*audio_object_type == 31) { + if (!gst_bit_reader_get_bits_uint8 (br, audio_object_type, 6)) + return FALSE; + *audio_object_type += 32; + } + GST_LOG_OBJECT (aacparse, "audio object type %u", *audio_object_type); + return TRUE; +} + +static gboolean +gst_aac_parse_get_audio_sample_rate (GstAacParse * aacparse, GstBitReader * br, + gint * sample_rate) +{ + guint8 sampling_frequency_index; + if (!gst_bit_reader_get_bits_uint8 (br, &sampling_frequency_index, 4)) + return FALSE; + GST_LOG_OBJECT (aacparse, "sampling_frequency_index: %u", + sampling_frequency_index); + if (sampling_frequency_index == 0xf) { + guint32 sampling_rate; + if (!gst_bit_reader_get_bits_uint32 (br, &sampling_rate, 24)) + return FALSE; + *sample_rate = sampling_rate; + } else { + *sample_rate = loas_sample_rate_table[sampling_frequency_index]; + if (!*sample_rate) + return FALSE; + } + return TRUE; +} + +/* See table 1.13 in ISO/IEC 14496-3 */ +static gboolean +gst_aac_parse_read_loas_audio_specific_config (GstAacParse * aacparse, + GstBitReader * br, gint * sample_rate, gint * channels, guint32 * bits) +{ + guint8 audio_object_type, channel_configuration; + + if (!gst_aac_parse_get_audio_object_type (aacparse, br, &audio_object_type)) + return FALSE; + + if (!gst_aac_parse_get_audio_sample_rate (aacparse, br, sample_rate)) + return FALSE; + + if (!gst_bit_reader_get_bits_uint8 (br, &channel_configuration, 4)) + return FALSE; + GST_LOG_OBJECT (aacparse, "channel_configuration: %d", channel_configuration); + *channels = loas_channels_table[channel_configuration]; + if (!*channels) + return FALSE; + + if (audio_object_type == 5) { + GST_LOG_OBJECT (aacparse, + "Audio object type 5, so rereading sampling rate..."); + if (!gst_aac_parse_get_audio_sample_rate (aacparse, br, sample_rate)) + return FALSE; + } + + GST_INFO_OBJECT (aacparse, "Found LOAS config: %d Hz, %d channels", + *sample_rate, *channels); + + /* There's LOTS of stuff next, but we ignore it for now as we have + what we want (sample rate and number of channels */ + GST_DEBUG_OBJECT (aacparse, + "Need more code to parse humongous LOAS data, currently ignored"); + if (bits) + *bits = 0; + return TRUE; +} + + +static gboolean +gst_aac_parse_read_loas_config (GstAacParse * aacparse, const guint8 * data, + guint avail, gint * sample_rate, gint * channels, gint * version) +{ + GstBitReader br; + guint8 u8, v, vA; + + /* No version in the bitstream, but the spec has LOAS in the MPEG-4 section */ + if (version) + *version = 4; + + gst_bit_reader_init (&br, data, avail); + + /* skip sync word (11 bits) and size (13 bits) */ + gst_bit_reader_skip (&br, 11 + 13); + + /* First bit is "use last config" */ + if (!gst_bit_reader_get_bits_uint8 (&br, &u8, 1)) + return FALSE; + if (u8) { + GST_DEBUG_OBJECT (aacparse, "Frame uses previous config"); + if (!aacparse->sample_rate || !aacparse->channels) { + GST_WARNING_OBJECT (aacparse, "No previous config to use"); + } + *sample_rate = aacparse->sample_rate; + *channels = aacparse->channels; + return TRUE; + } + + GST_DEBUG_OBJECT (aacparse, "Frame contains new config"); + + if (!gst_bit_reader_get_bits_uint8 (&br, &v, 1)) + return FALSE; + if (v) { + if (!gst_bit_reader_get_bits_uint8 (&br, &vA, 1)) + return FALSE; + } else + vA = 0; + + GST_LOG_OBJECT (aacparse, "v %d, vA %d", v, vA); + if (vA == 0) { + guint8 same_time, subframes, num_program, prog; + if (v == 1) { + guint32 value; + if (!gst_aac_parse_latm_get_value (aacparse, &br, &value)) + return FALSE; + } + if (!gst_bit_reader_get_bits_uint8 (&br, &same_time, 1)) + return FALSE; + if (!gst_bit_reader_get_bits_uint8 (&br, &subframes, 6)) + return FALSE; + if (!gst_bit_reader_get_bits_uint8 (&br, &num_program, 4)) + return FALSE; + GST_LOG_OBJECT (aacparse, "same_time %d, subframes %d, num_program %d", + same_time, subframes, num_program); + + for (prog = 0; prog <= num_program; ++prog) { + guint8 num_layer, layer; + if (!gst_bit_reader_get_bits_uint8 (&br, &num_layer, 3)) + return FALSE; + GST_LOG_OBJECT (aacparse, "Program %d: %d layers", prog, num_layer); + + for (layer = 0; layer <= num_layer; ++layer) { + guint8 use_same_config; + if (prog == 0 && layer == 0) { + use_same_config = 0; + } else { + if (!gst_bit_reader_get_bits_uint8 (&br, &use_same_config, 1)) + return FALSE; + } + if (!use_same_config) { + if (v == 0) { + if (!gst_aac_parse_read_loas_audio_specific_config (aacparse, &br, + sample_rate, channels, NULL)) + return FALSE; + } else { + guint32 bits, asc_len; + if (!gst_aac_parse_latm_get_value (aacparse, &br, &asc_len)) + return FALSE; + if (!gst_aac_parse_read_loas_audio_specific_config (aacparse, &br, + sample_rate, channels, &bits)) + return FALSE; + asc_len -= bits; + gst_bit_reader_skip (&br, asc_len); + } + } + } + } + GST_WARNING_OBJECT (aacparse, "More data ignored"); + } else { + GST_WARNING_OBJECT (aacparse, "Spec says \"TBD\"..."); + } + return TRUE; +} + +/** + * gst_aac_parse_loas_get_frame_len: + * @data: block of data containing a LOAS header. + * + * This function calculates LOAS frame length from the given header. + * + * Returns: size of the LOAS frame. + */ +static inline guint +gst_aac_parse_loas_get_frame_len (const guint8 * data) +{ + return (((data[1] & 0x1f) << 8) | data[2]) + 3; +} + + +/** + * gst_aac_parse_check_loas_frame: + * @aacparse: #GstAacParse. + * @data: Data to be checked. + * @avail: Amount of data passed. + * @framesize: If valid LOAS frame was found, this will be set to tell the + * found frame size in bytes. + * @needed_data: If frame was not found, this may be set to tell how much + * more data is needed in the next round to detect the frame + * reliably. This may happen when a frame header candidate + * is found but it cannot be guaranteed to be the header without + * peeking the following data. + * + * Check if the given data contains contains LOAS frame. The algorithm + * will examine LOAS frame header and calculate the frame size. Also, another + * consecutive LOAS frame header need to be present after the found frame. + * Otherwise the data is not considered as a valid LOAS frame. However, this + * "extra check" is omitted when EOS has been received. In this case it is + * enough when data[0] contains a valid LOAS header. + * + * This function may set the #needed_data to indicate that a possible frame + * candidate has been found, but more data (#needed_data bytes) is needed to + * be absolutely sure. When this situation occurs, FALSE will be returned. + * + * When a valid frame is detected, this function will use + * gst_base_parse_set_min_frame_size() function from #GstBaseParse class + * to set the needed bytes for next frame.This way next data chunk is already + * of correct size. + * + * LOAS can have three different formats, if I read the spec correctly. Only + * one of them is supported here, as the two samples I have use this one. + * + * Returns: TRUE if the given data contains a valid LOAS header. + */ +static gboolean +gst_aac_parse_check_loas_frame (GstAacParse * aacparse, + const guint8 * data, const guint avail, gboolean drain, + guint * framesize, guint * needed_data) +{ + *needed_data = 0; + + /* 3 byte header */ + if (G_UNLIKELY (avail < 3)) + return FALSE; + + if ((data[0] == 0x56) && ((data[1] & 0xe0) == 0xe0)) { + *framesize = gst_aac_parse_loas_get_frame_len (data); + GST_DEBUG_OBJECT (aacparse, "Found %u byte LOAS frame", *framesize); + + /* In EOS mode this is enough. No need to examine the data further. + We also relax the check when we have sync, on the assumption that + if we're not looking at random data, we have a much higher chance + to get the correct sync, and this avoids losing two frames when + a single bit corruption happens. */ + if (drain || !GST_BASE_PARSE_LOST_SYNC (aacparse)) { + return TRUE; + } + + if (*framesize + LOAS_MAX_SIZE > avail) { + /* We have found a possible frame header candidate, but can't be + sure since we don't have enough data to check the next frame */ + GST_DEBUG ("NEED MORE DATA: we need %d, available %d", + *framesize + LOAS_MAX_SIZE, avail); + *needed_data = *framesize + LOAS_MAX_SIZE; + gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse), + *framesize + LOAS_MAX_SIZE); + return FALSE; + } + + if ((data[*framesize] == 0x56) && ((data[*framesize + 1] & 0xe0) == 0xe0)) { + guint nextlen = gst_aac_parse_loas_get_frame_len (data + (*framesize)); + + GST_LOG ("LOAS frame found, len: %d bytes", *framesize); + gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse), + nextlen + LOAS_MAX_SIZE); + return TRUE; + } + } + return FALSE; +} + /* caller ensure sufficient data */ static inline void gst_aac_parse_parse_adts_header (GstAacParse * aacparse, const guint8 * data, @@ -412,7 +715,7 @@ gst_aac_parse_detect_stream (GstAacParse * aacparse, guint * framesize, gint * skipsize) { gboolean found = FALSE; - guint need_data = 0; + guint need_data_adts = 0, need_data_loas; guint i = 0; GST_DEBUG_OBJECT (aacparse, "Parsing header data"); @@ -421,12 +724,16 @@ gst_aac_parse_detect_stream (GstAacParse * aacparse, stream */ /* Can we even parse the header? */ - if (avail < ADTS_MAX_SIZE) + if (avail < MAX (ADTS_MAX_SIZE, LOAS_MAX_SIZE)) { + GST_DEBUG_OBJECT (aacparse, "Not enough data to check"); return FALSE; + } for (i = 0; i < avail - 4; i++) { if (((data[i] == 0xff) && ((data[i + 1] & 0xf6) == 0xf0)) || + ((data[0] == 0x56) && ((data[1] & 0xe0) == 0xe0)) || strncmp ((char *) data + i, "ADIF", 4) == 0) { + GST_DEBUG_OBJECT (aacparse, "Found ADIF signature at offset %u", i); found = TRUE; if (i) { @@ -446,7 +753,7 @@ gst_aac_parse_detect_stream (GstAacParse * aacparse, } if (gst_aac_parse_check_adts_frame (aacparse, data, avail, drain, - framesize, &need_data)) { + framesize, &need_data_adts)) { gint rate, channels; GST_INFO ("ADTS ID: %d, framesize: %d", (data[1] & 0x08) >> 3, *framesize); @@ -464,7 +771,38 @@ gst_aac_parse_detect_stream (GstAacParse * aacparse, gst_base_parse_set_syncable (GST_BASE_PARSE (aacparse), TRUE); return TRUE; - } else if (need_data) { + } + + if (gst_aac_parse_check_loas_frame (aacparse, data, avail, drain, + framesize, &need_data_loas)) { + gint rate, channels; + + GST_INFO ("LOAS, framesize: %d", *framesize); + + aacparse->header_type = DSPAAC_HEADER_LOAS; + + if (!gst_aac_parse_read_loas_config (aacparse, data, avail, &rate, + &channels, &aacparse->mpegversion)) { + GST_WARNING_OBJECT (aacparse, "Error reading LOAS config"); + return FALSE; + } + + if (rate && channels) { + gst_base_parse_set_frame_rate (GST_BASE_PARSE (aacparse), rate, + aacparse->frame_samples, 2, 2); + + GST_DEBUG ("LOAS: samplerate %d, channels %d, objtype %d, version %d", + rate, channels, aacparse->object_type, aacparse->mpegversion); + aacparse->sample_rate = rate; + aacparse->channels = channels; + } + + gst_base_parse_set_syncable (GST_BASE_PARSE (aacparse), TRUE); + + return TRUE; + } + + if (need_data_adts || need_data_loas) { /* This tells the parent class not to skip any data */ *skipsize = 0; return FALSE; @@ -612,6 +950,18 @@ gst_aac_parse_check_valid_frame (GstBaseParse * parse, needed_data); } + } else if (aacparse->header_type == DSPAAC_HEADER_LOAS) { + guint needed_data = 1024; + + ret = gst_aac_parse_check_loas_frame (aacparse, data, + size, GST_BASE_PARSE_DRAINING (parse), framesize, &needed_data); + + if (!ret) { + GST_DEBUG ("buffer didn't contain valid frame"); + gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse), + needed_data); + } + } else { GST_DEBUG ("buffer didn't contain valid frame"); gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse), @@ -632,7 +982,7 @@ gst_aac_parse_check_valid_frame (GstBaseParse * parse, * * Also determines frame overhead. * ADTS streams have a 7 byte header in each frame. MP4 and ADIF streams don't have - * a per-frame header. + * a per-frame header. LOAS has 3 bytes. * * We're making a couple of simplifying assumptions: * @@ -659,36 +1009,70 @@ gst_aac_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame) aacparse = GST_AAC_PARSE (parse); buffer = frame->buffer; - if (G_UNLIKELY (aacparse->header_type != DSPAAC_HEADER_ADTS)) - return ret; + if (aacparse->header_type == DSPAAC_HEADER_ADTS) { + /* see above */ + frame->overhead = 7; - /* see above */ - frame->overhead = 7; + data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ); + gst_aac_parse_parse_adts_header (aacparse, data, + &rate, &channels, NULL, NULL); + gst_buffer_unmap (buffer, data, size); - data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ); - gst_aac_parse_parse_adts_header (aacparse, data, - &rate, &channels, NULL, NULL); - gst_buffer_unmap (buffer, data, size); - - GST_LOG_OBJECT (aacparse, "rate: %d, chans: %d", rate, channels); + GST_LOG_OBJECT (aacparse, "rate: %d, chans: %d", rate, channels); - if (G_UNLIKELY (rate != aacparse->sample_rate - || channels != aacparse->channels)) { - GstCaps *sinkcaps; + if (G_UNLIKELY (rate != aacparse->sample_rate + || channels != aacparse->channels)) { + GstCaps *sinkcaps; - aacparse->sample_rate = rate; - aacparse->channels = channels; + aacparse->sample_rate = rate; + aacparse->channels = channels; - sinkcaps = gst_pad_get_current_caps (GST_BASE_PARSE (aacparse)->sinkpad); - if (!gst_aac_parse_set_src_caps (aacparse, sinkcaps)) { - /* If linking fails, we need to return appropriate error */ + sinkcaps = gst_pad_get_current_caps (GST_BASE_PARSE (aacparse)->sinkpad); + if (!gst_aac_parse_set_src_caps (aacparse, sinkcaps)) { + /* If linking fails, we need to return appropriate error */ + ret = GST_FLOW_NOT_LINKED; + } gst_caps_unref (sinkcaps); - ret = GST_FLOW_NOT_LINKED; + + gst_base_parse_set_frame_rate (GST_BASE_PARSE (aacparse), + aacparse->sample_rate, aacparse->frame_samples, 2, 2); } - gst_caps_unref (sinkcaps); + } else if (aacparse->header_type == DSPAAC_HEADER_LOAS) { + gboolean setcaps = FALSE; + + /* see above */ + frame->overhead = 3; + + data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ); + if (!gst_aac_parse_read_loas_config (aacparse, data, size, &rate, &channels, + NULL)) { + GST_WARNING_OBJECT (aacparse, "Error reading LOAS config"); + } else if (G_UNLIKELY (rate != aacparse->sample_rate + || channels != aacparse->channels)) { + aacparse->sample_rate = rate; + aacparse->channels = channels; + setcaps = TRUE; + GST_INFO_OBJECT (aacparse, "New LOAS config: %d Hz, %d channels", rate, + channels); + } + gst_buffer_unmap (buffer, data, size); + + /* We want to set caps both at start, and when rate/channels change. + Since only some LOAS frames have that info, we may receive frames + before knowing about rate/channels. */ + if (setcaps + || !gst_pad_has_current_caps (GST_BASE_PARSE_SRC_PAD (aacparse))) { + GstCaps *sinkcaps = + gst_pad_get_current_caps (GST_BASE_PARSE (aacparse)->sinkpad); + if (!gst_aac_parse_set_src_caps (aacparse, sinkcaps)) { + /* If linking fails, we need to return appropriate error */ + ret = GST_FLOW_NOT_LINKED; + } + gst_caps_unref (sinkcaps); - gst_base_parse_set_frame_rate (GST_BASE_PARSE (aacparse), - aacparse->sample_rate, aacparse->frame_samples, 2, 2); + gst_base_parse_set_frame_rate (GST_BASE_PARSE (aacparse), + aacparse->sample_rate, aacparse->frame_samples, 2, 2); + } } return ret; diff --git a/gst/audioparsers/gstaacparse.h b/gst/audioparsers/gstaacparse.h index 1907c2e44..11f75e683 100644 --- a/gst/audioparsers/gstaacparse.h +++ b/gst/audioparsers/gstaacparse.h @@ -45,6 +45,7 @@ G_BEGIN_DECLS * @DSPAAC_HEADER_UNKNOWN: Unknown (not recognized) header. * @DSPAAC_HEADER_ADIF: ADIF header found. * @DSPAAC_HEADER_ADTS: ADTS header found. + * @DSPAAC_HEADER_LOAS: LOAS header found. * @DSPAAC_HEADER_NONE: Raw stream, no header. * * Type header enumeration set in #header_type. @@ -54,6 +55,7 @@ typedef enum { DSPAAC_HEADER_UNKNOWN, DSPAAC_HEADER_ADIF, DSPAAC_HEADER_ADTS, + DSPAAC_HEADER_LOAS, DSPAAC_HEADER_NONE } GstAacHeaderType; diff --git a/gst/audioparsers/gstac3parse.c b/gst/audioparsers/gstac3parse.c index 26b619585..1c4b85326 100644 --- a/gst/audioparsers/gstac3parse.c +++ b/gst/audioparsers/gstac3parse.c @@ -324,7 +324,9 @@ gst_ac3_parse_frame_header_ac3 (GstAc3Parse * ac3parse, GstBuffer * buf, /* spec not quite clear here: decoder should decode if less than 8, * but seemingly only defines 6 and 8 cases */ - if (bsid > 8) { + /* Files with 9 and 10 happen, and seem to comply with the <= 8 + format, so let them through. The spec says nothing about 9 and 10 */ + if (bsid > 10) { GST_DEBUG_OBJECT (ac3parse, "unexpected bsid=%d", bsid); goto cleanup; } else if (bsid != 8 && bsid != 6) { diff --git a/gst/avi/gstavidemux.c b/gst/avi/gstavidemux.c index da0943af7..1c2384b0b 100644 --- a/gst/avi/gstavidemux.c +++ b/gst/avi/gstavidemux.c @@ -45,6 +45,10 @@ #include "config.h" #endif +/* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex + * with newer GLib versions (>= 2.31.0) */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + #include <string.h> #include <stdio.h> diff --git a/gst/avi/gstavimux.c b/gst/avi/gstavimux.c index bea410e66..f34046e60 100644 --- a/gst/avi/gstavimux.c +++ b/gst/avi/gstavimux.c @@ -190,10 +190,10 @@ static GstStaticPadTemplate audio_sink_factory = static void gst_avi_mux_pad_reset (GstAviPad * avipad, gboolean free); -static GstFlowReturn gst_avi_mux_collect_pads (GstCollectPads * pads, +static GstFlowReturn gst_avi_mux_collect_pads (GstCollectPads2 * pads, GstAviMux * avimux); -static gboolean gst_avi_mux_handle_event (GstPad * pad, GstObject * parent, - GstEvent * event); +static gboolean gst_avi_mux_handle_event (GstCollectPads2 * pad, + GstCollectData2 * data, GstEvent * event, gpointer user_data); static GstPad *gst_avi_mux_request_new_pad (GstElement * element, GstPadTemplate * templ, const gchar * name, const GstCaps * caps); static void gst_avi_mux_release_pad (GstElement * element, GstPad * pad); @@ -378,10 +378,13 @@ gst_avi_mux_init (GstAviMux * avimux) /* property */ avimux->enable_large_avi = DEFAULT_BIGFILE; - avimux->collect = gst_collect_pads_new (); - gst_collect_pads_set_function (avimux->collect, - (GstCollectPadsFunction) (GST_DEBUG_FUNCPTR (gst_avi_mux_collect_pads)), + avimux->collect = gst_collect_pads2_new (); + gst_collect_pads2_set_function (avimux->collect, + (GstCollectPads2Function) (GST_DEBUG_FUNCPTR (gst_avi_mux_collect_pads)), avimux); + gst_collect_pads2_set_event_function (avimux->collect, + (GstCollectPads2EventFunction) (GST_DEBUG_FUNCPTR + (gst_avi_mux_handle_event)), avimux); /* set to clean state */ gst_avi_mux_reset (avimux); @@ -977,15 +980,9 @@ gst_avi_mux_request_new_pad (GstElement * element, g_free (name); - avipad->collect = gst_collect_pads_add_pad (avimux->collect, - newpad, sizeof (GstAviCollectData), NULL); + avipad->collect = gst_collect_pads2_add_pad (avimux->collect, + newpad, sizeof (GstAviCollectData)); ((GstAviCollectData *) (avipad->collect))->avipad = avipad; - /* FIXME: hacked way to override/extend the event function of - * GstCollectPads; because it sets its own event function giving the - * element no access to events */ - avimux->collect_event = GST_PAD_EVENTFUNC (newpad); - gst_pad_set_event_function (newpad, - GST_DEBUG_FUNCPTR (gst_avi_mux_handle_event)); if (!gst_element_add_pad (element, newpad)) goto pad_add_failed; @@ -1038,7 +1035,7 @@ gst_avi_mux_release_pad (GstElement * element, GstPad * pad) * as it also represent number of streams present */ avipad->collect = NULL; GST_DEBUG_OBJECT (avimux, "removed pad '%s'", GST_PAD_NAME (pad)); - gst_collect_pads_remove_pad (avimux->collect, pad); + gst_collect_pads2_remove_pad (avimux->collect, pad); gst_element_remove_pad (element, pad); /* if not started yet, we can remove any sign this pad ever existed */ /* in this case _start will take care of the real pad count */ @@ -1833,12 +1830,13 @@ gst_avi_mux_restart_file (GstAviMux * avimux) /* handle events (search) */ static gboolean -gst_avi_mux_handle_event (GstPad * pad, GstObject * parent, GstEvent * event) +gst_avi_mux_handle_event (GstCollectPads2 * pads, GstCollectData2 * data, + GstEvent * event, gpointer user_data) { GstAviMux *avimux; - gboolean ret = TRUE; + gboolean ret = FALSE; - avimux = GST_AVI_MUX (parent); + avimux = GST_AVI_MUX (user_data); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_CAPS: @@ -1850,15 +1848,15 @@ gst_avi_mux_handle_event (GstPad * pad, GstObject * parent, GstEvent * event) gst_event_parse_caps (event, &caps); /* find stream data */ - collect_pad = (GstAviCollectData *) gst_pad_get_element_private (pad); + collect_pad = (GstAviCollectData *) data; g_assert (collect_pad); avipad = (GstAviVideoPad *) collect_pad->avipad; g_assert (avipad); if (avipad->parent.is_video) { - ret = gst_avi_mux_vidsink_set_caps (pad, caps); + ret = gst_avi_mux_vidsink_set_caps (GST_PAD (avipad), caps); } else { - ret = gst_avi_mux_audsink_set_caps (pad, caps); + ret = gst_avi_mux_audsink_set_caps (GST_PAD (avipad), caps); } break; } @@ -1875,10 +1873,7 @@ gst_avi_mux_handle_event (GstPad * pad, GstObject * parent, GstEvent * event) break; } - /* now GstCollectPads can take care of the rest, e.g. EOS */ - if (ret) - ret = avimux->collect_event (pad, parent, event); - + /* now GstCollectPads2 can take care of the rest, e.g. EOS */ return ret; } @@ -1909,7 +1904,7 @@ gst_avi_mux_do_buffer (GstAviMux * avimux, GstAviPad * avipad) guint flags; gsize datasize; - data = gst_collect_pads_pop (avimux->collect, avipad->collect); + data = gst_collect_pads2_pop (avimux->collect, avipad->collect); /* arrange downstream running time */ data = gst_buffer_make_writable (data); GST_BUFFER_TIMESTAMP (data) = @@ -2033,7 +2028,7 @@ gst_avi_mux_do_one_buffer (GstAviMux * avimux) if (!avipad->hdr.fcc_handler) goto not_negotiated; - buffer = gst_collect_pads_peek (avimux->collect, avipad->collect); + buffer = gst_collect_pads2_peek (avimux->collect, avipad->collect); if (!buffer) continue; time = GST_BUFFER_TIMESTAMP (buffer); @@ -2046,7 +2041,7 @@ gst_avi_mux_do_one_buffer (GstAviMux * avimux) if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) { GST_DEBUG_OBJECT (avimux, "clipping buffer on pad %s outside segment", GST_PAD_NAME (avipad->collect->pad)); - buffer = gst_collect_pads_pop (avimux->collect, avipad->collect); + buffer = gst_collect_pads2_pop (avimux->collect, avipad->collect); gst_buffer_unref (buffer); return GST_FLOW_OK; } @@ -2085,7 +2080,7 @@ not_negotiated: } static GstFlowReturn -gst_avi_mux_collect_pads (GstCollectPads * pads, GstAviMux * avimux) +gst_avi_mux_collect_pads (GstCollectPads2 * pads, GstAviMux * avimux) { GstFlowReturn res; @@ -2144,12 +2139,12 @@ gst_avi_mux_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: - gst_collect_pads_start (avimux->collect); + gst_collect_pads2_start (avimux->collect); break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: break; case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_collect_pads_stop (avimux->collect); + gst_collect_pads2_stop (avimux->collect); break; default: break; diff --git a/gst/avi/gstavimux.h b/gst/avi/gstavimux.h index 0d903928e..cb0cdb7b3 100644 --- a/gst/avi/gstavimux.h +++ b/gst/avi/gstavimux.h @@ -23,7 +23,7 @@ #include <gst/gst.h> -#include <gst/base/gstcollectpads.h> +#include <gst/base/gstcollectpads2.h> #include <gst/riff/riff-ids.h> #include "avi-ids.h" @@ -74,7 +74,7 @@ typedef GstFlowReturn (*GstAviPadHook) (GstAviMux * avi, GstAviPad * avipad, struct _GstAviPad { /* do not extend, link to it */ /* is NULL if original sink request pad has been removed */ - GstCollectData *collect; + GstCollectData2 *collect; /* type */ gboolean is_video; @@ -129,7 +129,7 @@ typedef struct _GstAviAudioPad { typedef struct _GstAviCollectData { /* extend the CollectData */ - GstCollectData collect; + GstCollectData2 collect; GstAviPad *avipad; } GstAviCollectData; @@ -143,8 +143,7 @@ struct _GstAviMux { GSList *sinkpads; /* video restricted to 1 pad */ guint video_pads, audio_pads; - GstCollectPads *collect; - GstPadEventFunction collect_event; + GstCollectPads2 *collect; /* the AVI header */ /* still some single stream video data in mux struct */ diff --git a/gst/equalizer/gstiirequalizer.c b/gst/equalizer/gstiirequalizer.c index ee7ffee22..da7f8e43c 100644 --- a/gst/equalizer/gstiirequalizer.c +++ b/gst/equalizer/gstiirequalizer.c @@ -32,6 +32,8 @@ #include "gstiirequalizer3bands.h" #include "gstiirequalizer10bands.h" +#include "gst/glib-compat-private.h" + GST_DEBUG_CATEGORY (equalizer_debug); #define GST_CAT_DEFAULT equalizer_debug diff --git a/gst/flv/gstflvdemux.c b/gst/flv/gstflvdemux.c index 324c2189c..7603dbaf8 100644 --- a/gst/flv/gstflvdemux.c +++ b/gst/flv/gstflvdemux.c @@ -34,6 +34,10 @@ #include "config.h" #endif +/* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex + * with newer GLib versions (>= 2.31.0) */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + #include "gstflvdemux.h" #include "gstflvmux.h" diff --git a/gst/flv/gstflvmux.c b/gst/flv/gstflvmux.c index a7189e2ea..2249d3044 100644 --- a/gst/flv/gstflvmux.c +++ b/gst/flv/gstflvmux.c @@ -89,7 +89,11 @@ G_DEFINE_TYPE_WITH_CODE (GstFlvMux, gst_flv_mux, GST_TYPE_ELEMENT, static void gst_flv_mux_finalize (GObject * object); static GstFlowReturn -gst_flv_mux_collected (GstCollectPads * pads, gpointer user_data); +gst_flv_mux_handle_buffer (GstCollectPads2 * pads, GstCollectData2 * cdata, + GstBuffer * buf, gpointer user_data); +static gboolean +gst_flv_mux_handle_sink_event (GstCollectPads2 * pads, GstCollectData2 * data, + GstEvent * event, gpointer user_data); static gboolean gst_flv_mux_handle_src_event (GstPad * pad, GstObject * parent, GstEvent * event); @@ -209,9 +213,13 @@ gst_flv_mux_init (GstFlvMux * mux) mux->new_tags = FALSE; - mux->collect = gst_collect_pads_new (); - gst_collect_pads_set_function (mux->collect, - (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_flv_mux_collected), mux); + mux->collect = gst_collect_pads2_new (); + gst_collect_pads2_set_buffer_function (mux->collect, + GST_DEBUG_FUNCPTR (gst_flv_mux_handle_buffer), mux); + gst_collect_pads2_set_event_function (mux->collect, + GST_DEBUG_FUNCPTR (gst_flv_mux_handle_sink_event), mux); + gst_collect_pads2_set_clip_function (mux->collect, + GST_DEBUG_FUNCPTR (gst_collect_pads2_clip_running_time), mux); gst_flv_mux_reset (GST_ELEMENT (mux)); } @@ -273,11 +281,11 @@ gst_flv_mux_handle_src_event (GstPad * pad, GstObject * parent, } static gboolean -gst_flv_mux_handle_sink_event (GstPad * pad, GstObject * parent, - GstEvent * event) +gst_flv_mux_handle_sink_event (GstCollectPads2 * pads, GstCollectData2 * data, + GstEvent * event, gpointer user_data) { - GstFlvMux *mux = GST_FLV_MUX (parent); - gboolean ret = TRUE; + GstFlvMux *mux = GST_FLV_MUX (user_data); + gboolean ret = FALSE; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_CAPS: @@ -288,13 +296,13 @@ gst_flv_mux_handle_sink_event (GstPad * pad, GstObject * parent, gst_event_parse_caps (event, &caps); /* find stream data */ - flvpad = (GstFlvPad *) gst_pad_get_element_private (pad); + flvpad = (GstFlvPad *) data; g_assert (flvpad); if (flvpad->video) { - ret = gst_flv_mux_video_pad_setcaps (pad, caps); + ret = gst_flv_mux_video_pad_setcaps (data->pad, caps); } else { - ret = gst_flv_mux_audio_pad_setcaps (pad, caps); + ret = gst_flv_mux_audio_pad_setcaps (data->pad, caps); } /* and eat */ ret = FALSE; @@ -315,10 +323,7 @@ gst_flv_mux_handle_sink_event (GstPad * pad, GstObject * parent, break; } - /* now GstCollectPads can take care of the rest, e.g. EOS */ - if (ret) - ret = mux->collect_event (pad, parent, event); - + /* now GstCollectPads2 can take care of the rest, e.g. EOS */ return ret; } @@ -567,20 +572,12 @@ gst_flv_mux_request_new_pad (GstElement * element, pad = gst_pad_new_from_template (templ, name); cpad = (GstFlvPad *) - gst_collect_pads_add_pad (mux->collect, pad, sizeof (GstFlvPad), NULL); + gst_collect_pads2_add_pad (mux->collect, pad, sizeof (GstFlvPad)); cpad->audio_codec_data = NULL; cpad->video_codec_data = NULL; gst_flv_mux_reset_pad (mux, cpad, video); - /* FIXME: hacked way to override/extend the event function of - * GstCollectPads; because it sets its own event function giving the - * element no access to events. - */ - mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (pad); - gst_pad_set_event_function (pad, - GST_DEBUG_FUNCPTR (gst_flv_mux_handle_sink_event)); - gst_pad_set_active (pad, TRUE); gst_element_add_pad (element, pad); @@ -594,7 +591,7 @@ gst_flv_mux_release_pad (GstElement * element, GstPad * pad) GstFlvPad *cpad = (GstFlvPad *) gst_pad_get_element_private (pad); gst_flv_mux_reset_pad (mux, cpad, cpad->video); - gst_collect_pads_remove_pad (mux->collect, pad); + gst_collect_pads2_remove_pad (mux->collect, pad); gst_element_remove_pad (element, pad); } @@ -677,7 +674,7 @@ gst_flv_mux_create_number_script_value (const gchar * name, gdouble value) } static GstBuffer * -gst_flv_mux_create_metadata (GstFlvMux * mux) +gst_flv_mux_create_metadata (GstFlvMux * mux, gboolean full) { const GstTagList *tags; GstBuffer *script_tag, *tmp; @@ -719,6 +716,9 @@ gst_flv_mux_create_metadata (GstFlvMux * mux) GST_WRITE_UINT32_BE (data + 1, n_tags); script_tag = gst_buffer_join (script_tag, tmp); + if (!full) + goto tags; + /* Some players expect the 'duration' to be always set. Fill it out later, after querying the pads or after getting EOS */ if (!mux->streamable) { @@ -739,6 +739,7 @@ gst_flv_mux_create_metadata (GstFlvMux * mux) GST_DEBUG_OBJECT (mux, "not preallocating index, streamable mode"); } +tags: for (i = 0; tags && i < n_tags; i++) { const gchar *tag_name = gst_structure_nth_field_name ((const GstStructure *) tags, i); @@ -777,12 +778,15 @@ gst_flv_mux_create_metadata (GstFlvMux * mux) } } + if (!full) + goto end; + if (mux->duration == GST_CLOCK_TIME_NONE) { GSList *l; guint64 dur; for (l = mux->collect->data; l; l = l->next) { - GstCollectData *cdata = l->data; + GstCollectData2 *cdata = l->data; if (gst_pad_peer_query_duration (cdata->pad, GST_FORMAT_TIME, (gint64 *) & dur) && dur != GST_CLOCK_TIME_NONE) { @@ -958,6 +962,14 @@ gst_flv_mux_create_metadata (GstFlvMux * mux) tags_written++; } +end: + + if (!tags_written) { + gst_buffer_unref (script_tag); + script_tag = NULL; + goto exit; + } + _gst_buffer_new_and_alloc (2 + 0 + 1, &tmp, &data); data[0] = 0; /* 0 byte size */ data[1] = 0; @@ -977,6 +989,7 @@ gst_flv_mux_create_metadata (GstFlvMux * mux) GST_WRITE_UINT32_BE (data + 11 + 13 + 1, tags_written); gst_buffer_unmap (script_tag, data, -1); +exit: return script_tag; } @@ -1125,7 +1138,7 @@ gst_flv_mux_write_header (GstFlvMux * mux) GstFlowReturn ret; header = gst_flv_mux_create_header (mux); - metadata = gst_flv_mux_create_metadata (mux); + metadata = gst_flv_mux_create_metadata (mux, TRUE); video_codec_data = NULL; audio_codec_data = NULL; @@ -1228,18 +1241,12 @@ gst_flv_mux_update_index (GstFlvMux * mux, GstBuffer * buffer, GstFlvPad * cpad) } static GstFlowReturn -gst_flv_mux_write_buffer (GstFlvMux * mux, GstFlvPad * cpad) +gst_flv_mux_write_buffer (GstFlvMux * mux, GstFlvPad * cpad, GstBuffer * buffer) { GstBuffer *tag; - GstBuffer *buffer = - gst_collect_pads_pop (mux->collect, (GstCollectData *) cpad); GstFlowReturn ret; - /* arrange downstream running time */ - buffer = gst_buffer_make_writable (buffer); - GST_BUFFER_TIMESTAMP (buffer) = - gst_segment_to_running_time (&cpad->collect.segment, - GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buffer)); + /* clipping function arranged for running_time */ if (!mux->streamable) gst_flv_mux_update_index (mux, buffer, cpad); @@ -1276,12 +1283,6 @@ gst_flv_mux_determine_duration (GstFlvMux * mux) } } - if (duration == GST_CLOCK_TIME_NONE) { - GST_DEBUG_OBJECT (mux, "not able to determine duration " - "from pad timestamps, assuming 0"); - return 0; - } - return duration; } @@ -1296,6 +1297,7 @@ gst_flv_mux_rewrite_header (GstFlvMux * mux) guint32 index_len, allocate_size; guint32 i, index_skip; GstSegment segment; + GstClockTime dur; if (mux->streamable) return GST_FLOW_OK; @@ -1309,9 +1311,12 @@ gst_flv_mux_rewrite_header (GstFlvMux * mux) return GST_FLOW_OK; } - /* if we were not able to determine the duration before, set it now */ - if (mux->duration == GST_CLOCK_TIME_NONE) - mux->duration = gst_flv_mux_determine_duration (mux); + /* determine duration now based on our own timestamping, + * so that it is likely many times better and consistent + * than whatever obtained by some query */ + dur = gst_flv_mux_determine_duration (mux); + if (dur != GST_CLOCK_TIME_NONE) + mux->duration = dur; /* rewrite the duration tag */ d = gst_guint64_to_gdouble (mux->duration); @@ -1420,14 +1425,13 @@ gst_flv_mux_rewrite_header (GstFlvMux * mux) } static GstFlowReturn -gst_flv_mux_collected (GstCollectPads * pads, gpointer user_data) +gst_flv_mux_handle_buffer (GstCollectPads2 * pads, GstCollectData2 * cdata, + GstBuffer * buffer, gpointer user_data) { GstFlvMux *mux = GST_FLV_MUX (user_data); GstFlvPad *best; GstClockTime best_time; GstFlowReturn ret; - GSList *sl; - gboolean eos = TRUE; if (mux->state == GST_FLV_MUX_STATE_HEADER) { GstSegment segment; @@ -1450,51 +1454,18 @@ gst_flv_mux_collected (GstCollectPads * pads, gpointer user_data) } if (mux->new_tags) { - GstBuffer *buf = gst_flv_mux_create_metadata (mux); - gst_flv_mux_push (mux, buf); + GstBuffer *buf = gst_flv_mux_create_metadata (mux, FALSE); + if (buf) + gst_flv_mux_push (mux, buf); mux->new_tags = FALSE; } - - best = NULL; - best_time = GST_CLOCK_TIME_NONE; - for (sl = mux->collect->data; sl; sl = sl->next) { - GstFlvPad *cpad = sl->data; - GstBuffer *buffer = gst_collect_pads_peek (pads, (GstCollectData *) cpad); - GstClockTime time; - - if (!buffer) - continue; - - eos = FALSE; - - time = GST_BUFFER_TIMESTAMP (buffer); - gst_buffer_unref (buffer); - - /* Use buffers without valid timestamp first */ - if (!GST_CLOCK_TIME_IS_VALID (time)) { - GST_WARNING_OBJECT (pads, "Buffer without valid timestamp"); - - best_time = cpad->last_timestamp; - best = cpad; - break; - } - - time = gst_segment_to_running_time (&cpad->collect.segment, - GST_FORMAT_TIME, time); - if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) { - GST_DEBUG_OBJECT (mux, "clipping buffer on pad %s outside segment", - GST_PAD_NAME (cpad->collect.pad)); - buffer = gst_collect_pads_pop (pads, (GstCollectData *) cpad); - gst_buffer_unref (buffer); - return GST_FLOW_OK; - } - - if (best == NULL || (GST_CLOCK_TIME_IS_VALID (best_time) - && time < best_time)) { - best = cpad; - best_time = time; - } + best = (GstFlvPad *) cdata; + if (best) { + g_assert (buffer); + best_time = GST_BUFFER_TIMESTAMP (buffer); + } else { + best_time = GST_CLOCK_TIME_NONE; } /* The FLV timestamp is an int32 field. For non-live streams error out if a @@ -1503,17 +1474,17 @@ gst_flv_mux_collected (GstCollectPads * pads, gpointer user_data) if (!mux->streamable && GST_CLOCK_TIME_IS_VALID (best_time) && best_time / GST_MSECOND > G_MAXINT32) { GST_WARNING_OBJECT (mux, "Timestamp larger than FLV supports - EOS"); - eos = TRUE; + gst_buffer_unref (buffer); + buffer = NULL; + best = NULL; } - if (!eos && best) { - return gst_flv_mux_write_buffer (mux, best); - } else if (eos) { + if (best) { + return gst_flv_mux_write_buffer (mux, best, buffer); + } else { gst_flv_mux_rewrite_header (mux); gst_pad_push_event (mux->srcpad, gst_event_new_eos ()); return GST_FLOW_EOS; - } else { - return GST_FLOW_OK; } } @@ -1565,12 +1536,12 @@ gst_flv_mux_change_state (GstElement * element, GstStateChange transition) case GST_STATE_CHANGE_NULL_TO_READY: break; case GST_STATE_CHANGE_READY_TO_PAUSED: - gst_collect_pads_start (mux->collect); + gst_collect_pads2_start (mux->collect); break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: break; case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_collect_pads_stop (mux->collect); + gst_collect_pads2_stop (mux->collect); break; default: break; diff --git a/gst/flv/gstflvmux.h b/gst/flv/gstflvmux.h index fff51b9b3..b0e10ea32 100644 --- a/gst/flv/gstflvmux.h +++ b/gst/flv/gstflvmux.h @@ -22,7 +22,7 @@ #define __GST_FLV_MUX_H__ #include <gst/gst.h> -#include <gst/base/gstcollectpads.h> +#include <gst/base/gstcollectpads2.h> G_BEGIN_DECLS @@ -39,7 +39,7 @@ G_BEGIN_DECLS typedef struct { - GstCollectData collect; + GstCollectData2 collect; gboolean video; @@ -65,11 +65,9 @@ typedef struct _GstFlvMux { GstElement element; GstPad *srcpad; - GstCollectPads *collect; + GstCollectPads2 *collect; /* <private> */ - GstPadEventFunction collect_event; - GstFlvMuxState state; gboolean have_audio; gboolean have_video; diff --git a/gst/imagefreeze/gstimagefreeze.c b/gst/imagefreeze/gstimagefreeze.c index f4c67c76f..03c425d33 100644 --- a/gst/imagefreeze/gstimagefreeze.c +++ b/gst/imagefreeze/gstimagefreeze.c @@ -41,6 +41,12 @@ #include "config.h" #endif +/* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex + * with newer GLib versions (>= 2.31.0) */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + +#include <gst/glib-compat-private.h> + #include "gstimagefreeze.h" static void gst_image_freeze_finalize (GObject * object); diff --git a/gst/interleave/interleave.c b/gst/interleave/interleave.c index c2cc2db78..e7e0b668c 100644 --- a/gst/interleave/interleave.c +++ b/gst/interleave/interleave.c @@ -227,7 +227,7 @@ static gboolean gst_interleave_sink_setcaps (GstPad * pad, GstCaps * caps); static GstCaps *gst_interleave_sink_getcaps (GstPad * pad); -static GstFlowReturn gst_interleave_collected (GstCollectPads * pads, +static GstFlowReturn gst_interleave_collected (GstCollectPads2 * pads, GstInterleave * self); static void @@ -407,9 +407,9 @@ gst_interleave_init (GstInterleave * self, GstInterleaveClass * klass) gst_element_add_pad (GST_ELEMENT (self), self->src); - self->collect = gst_collect_pads_new (); - gst_collect_pads_set_function (self->collect, - (GstCollectPadsFunction) gst_interleave_collected, self); + self->collect = gst_collect_pads2_new (); + gst_collect_pads2_set_function (self->collect, + (GstCollectPads2Function) gst_interleave_collected, self); self->input_channel_positions = g_value_array_new (0); self->channel_positions_from_input = TRUE; @@ -500,11 +500,10 @@ gst_interleave_request_new_pad (GstElement * element, GstPadTemplate * templ, gst_pad_set_getcaps_function (new_pad, GST_DEBUG_FUNCPTR (gst_interleave_sink_getcaps)); - gst_collect_pads_add_pad (self->collect, new_pad, sizeof (GstCollectData), - NULL); + gst_collect_pads2_add_pad (self->collect, new_pad, sizeof (GstCollectData2)); /* FIXME: hacked way to override/extend the event function of - * GstCollectPads; because it sets its own event function giving the + * GstCollectPads2; because it sets its own event function giving the * element no access to events */ self->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (new_pad); gst_pad_set_event_function (new_pad, @@ -550,7 +549,7 @@ not_sink_pad: could_not_add: { GST_DEBUG_OBJECT (self, "could not add pad %s", GST_PAD_NAME (new_pad)); - gst_collect_pads_remove_pad (self->collect, new_pad); + gst_collect_pads2_remove_pad (self->collect, new_pad); gst_object_unref (new_pad); return NULL; } @@ -604,7 +603,7 @@ gst_interleave_release_pad (GstElement * element, GstPad * pad) GST_OBJECT_UNLOCK (self->collect); - gst_collect_pads_remove_pad (self->collect, pad); + gst_collect_pads2_remove_pad (self->collect, pad); gst_element_remove_pad (element, pad); } @@ -626,7 +625,7 @@ gst_interleave_change_state (GstElement * element, GstStateChange transition) self->segment_position = 0; self->segment_rate = 1.0; gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED); - gst_collect_pads_start (self->collect); + gst_collect_pads2_start (self->collect); break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: break; @@ -635,11 +634,11 @@ gst_interleave_change_state (GstElement * element, GstStateChange transition) } /* Stop before calling the parent's state change function as - * GstCollectPads might take locks and we would deadlock in that + * GstCollectPads2 might take locks and we would deadlock in that * case */ if (transition == GST_STATE_CHANGE_PAUSED_TO_READY) - gst_collect_pads_stop (self->collect); + gst_collect_pads2_stop (self->collect); ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); @@ -879,7 +878,7 @@ gst_interleave_sink_event (GstPad * pad, GstEvent * event) break; } - /* now GstCollectPads can take care of the rest, e.g. EOS */ + /* now GstCollectPads2 can take care of the rest, e.g. EOS */ ret = self->collect_event (pad, event); gst_object_unref (self); @@ -1160,7 +1159,7 @@ gst_interleave_src_event (GstPad * pad, GstEvent * event) /* check if we are flushing */ if (flags & GST_SEEK_FLAG_FLUSH) { /* make sure we accept nothing anymore and return WRONG_STATE */ - gst_collect_pads_set_flushing (self->collect, TRUE); + gst_collect_pads2_set_flushing (self->collect, TRUE); /* flushing seek, start flush downstream, the flush will be done * when all pads received a FLUSH_STOP. */ @@ -1195,7 +1194,7 @@ gst_interleave_src_event (GstPad * pad, GstEvent * event) } static GstFlowReturn -gst_interleave_collected (GstCollectPads * pads, GstInterleave * self) +gst_interleave_collected (GstCollectPads2 * pads, GstInterleave * self) { guint size; GstBuffer *outbuf; @@ -1211,7 +1210,7 @@ gst_interleave_collected (GstCollectPads * pads, GstInterleave * self) g_return_val_if_fail (self->channels > 0, GST_FLOW_NOT_NEGOTIATED); g_return_val_if_fail (self->rate > 0, GST_FLOW_NOT_NEGOTIATED); - size = gst_collect_pads_available (pads); + size = gst_collect_pads2_available (pads); g_return_val_if_fail (size % width == 0, GST_FLOW_ERROR); @@ -1238,13 +1237,13 @@ gst_interleave_collected (GstCollectPads * pads, GstInterleave * self) memset (GST_BUFFER_DATA (outbuf), 0, size * self->channels); for (collected = pads->data; collected != NULL; collected = collected->next) { - GstCollectData *cdata; + GstCollectData2 *cdata; GstBuffer *inbuf; guint8 *outdata; - cdata = (GstCollectData *) collected->data; + cdata = (GstCollectData2 *) collected->data; - inbuf = gst_collect_pads_take_buffer (pads, cdata, size); + inbuf = gst_collect_pads2_take_buffer (pads, cdata, size); if (inbuf == NULL) { GST_DEBUG_OBJECT (cdata->pad, "No buffer available"); goto next; diff --git a/gst/interleave/interleave.h b/gst/interleave/interleave.h index fb3b27415..abe3439e4 100644 --- a/gst/interleave/interleave.h +++ b/gst/interleave/interleave.h @@ -27,7 +27,7 @@ #define __INTERLEAVE_H__ #include <gst/gst.h> -#include <gst/base/gstcollectpads.h> +#include <gst/base/gstcollectpads2.h> G_BEGIN_DECLS @@ -49,7 +49,7 @@ struct _GstInterleave GstElement element; /*< private >*/ - GstCollectPads *collect; + GstCollectPads2 *collect; gint channels; gint padcounter; diff --git a/gst/isomp4/gstqtmoovrecover.c b/gst/isomp4/gstqtmoovrecover.c index 678ab3bb4..3fbfec329 100644 --- a/gst/isomp4/gstqtmoovrecover.c +++ b/gst/isomp4/gstqtmoovrecover.c @@ -65,6 +65,10 @@ #include "config.h" #endif +/* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex + * with newer GLib versions (>= 2.31.0) */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + #include <glib/gstdio.h> #include <gst/gst.h> diff --git a/gst/isomp4/gstqtmux.c b/gst/isomp4/gstqtmux.c index 0c7caf6f4..510747827 100644 --- a/gst/isomp4/gstqtmux.c +++ b/gst/isomp4/gstqtmux.c @@ -226,11 +226,11 @@ static GstPad *gst_qt_mux_request_new_pad (GstElement * element, static void gst_qt_mux_release_pad (GstElement * element, GstPad * pad); /* event */ -static gboolean gst_qt_mux_sink_event (GstPad * pad, GstObject * parent, - GstEvent * event); +static gboolean gst_qt_mux_sink_event (GstCollectPads2 * pads, + GstCollectData2 * data, GstEvent * event, gpointer user_data); -static GstFlowReturn gst_qt_mux_collected (GstCollectPads * pads, - gpointer user_data); +static GstFlowReturn gst_qt_mux_handle_buffer (GstCollectPads2 * pads, + GstCollectData2 * cdata, GstBuffer * buf, gpointer user_data); static GstFlowReturn gst_qt_mux_add_buffer (GstQTMux * qtmux, GstQTPad * pad, GstBuffer * buf); @@ -482,9 +482,13 @@ gst_qt_mux_init (GstQTMux * qtmux, GstQTMuxClass * qtmux_klass) gst_element_add_pad (GST_ELEMENT (qtmux), qtmux->srcpad); qtmux->sinkpads = NULL; - qtmux->collect = gst_collect_pads_new (); - gst_collect_pads_set_function (qtmux->collect, - (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_qt_mux_collected), qtmux); + qtmux->collect = gst_collect_pads2_new (); + gst_collect_pads2_set_buffer_function (qtmux->collect, + GST_DEBUG_FUNCPTR (gst_qt_mux_handle_buffer), qtmux); + gst_collect_pads2_set_event_function (qtmux->collect, + GST_DEBUG_FUNCPTR (gst_qt_mux_sink_event), qtmux); + gst_collect_pads2_set_clip_function (qtmux->collect, + GST_DEBUG_FUNCPTR (gst_collect_pads2_clip_running_time), qtmux); /* properties set to default upon construction */ @@ -1661,7 +1665,7 @@ gst_qt_mux_start_file (GstQTMux * qtmux) gst_buffer_unref (prefix); for (walk = qtmux->sinkpads; walk && !fail; walk = g_slist_next (walk)) { - GstCollectData *cdata = (GstCollectData *) walk->data; + GstCollectData2 *cdata = (GstCollectData2 *) walk->data; GstQTPad *qpad = (GstQTPad *) cdata; /* write info for each stream */ fail = atoms_recov_write_trak_info (qtmux->moov_recov_file, qpad->trak); @@ -1761,7 +1765,7 @@ gst_qt_mux_stop_file (GstQTMux * qtmux) /* pushing last buffers for each pad */ for (walk = qtmux->collect->data; walk; walk = g_slist_next (walk)) { - GstCollectData *cdata = (GstCollectData *) walk->data; + GstCollectData2 *cdata = (GstCollectData2 *) walk->data; GstQTPad *qtpad = (GstQTPad *) cdata; /* avoid add_buffer complaining if not negotiated @@ -1853,7 +1857,7 @@ gst_qt_mux_stop_file (GstQTMux * qtmux) /* check for late streams */ first_ts = GST_CLOCK_TIME_NONE; for (walk = qtmux->collect->data; walk; walk = g_slist_next (walk)) { - GstCollectData *cdata = (GstCollectData *) walk->data; + GstCollectData2 *cdata = (GstCollectData2 *) walk->data; GstQTPad *qtpad = (GstQTPad *) cdata; if (!GST_CLOCK_TIME_IS_VALID (first_ts) || @@ -1866,7 +1870,7 @@ gst_qt_mux_stop_file (GstQTMux * qtmux) GST_TIME_ARGS (first_ts)); /* add EDTSs for late streams */ for (walk = qtmux->collect->data; walk; walk = g_slist_next (walk)) { - GstCollectData *cdata = (GstCollectData *) walk->data; + GstCollectData2 *cdata = (GstCollectData2 *) walk->data; GstQTPad *qtpad = (GstQTPad *) cdata; guint32 lateness; guint32 duration; @@ -2520,14 +2524,13 @@ not_negotiated: } static GstFlowReturn -gst_qt_mux_collected (GstCollectPads * pads, gpointer user_data) +gst_qt_mux_handle_buffer (GstCollectPads2 * pads, GstCollectData2 * cdata, + GstBuffer * buf, gpointer user_data) { GstFlowReturn ret = GST_FLOW_OK; GstQTMux *qtmux = GST_QT_MUX_CAST (user_data); - GSList *walk; GstQTPad *best_pad = NULL; - GstClockTime time, best_time = GST_CLOCK_TIME_NONE; - GstBuffer *buf; + GstClockTime best_time = GST_CLOCK_TIME_NONE; if (G_UNLIKELY (qtmux->state == GST_QT_MUX_STATE_STARTED)) { if ((ret = gst_qt_mux_start_file (qtmux)) != GST_FLOW_OK) @@ -2539,52 +2542,14 @@ gst_qt_mux_collected (GstCollectPads * pads, gpointer user_data) if (G_UNLIKELY (qtmux->state == GST_QT_MUX_STATE_EOS)) return GST_FLOW_EOS; - /* select the best buffer */ - walk = qtmux->collect->data; - while (walk) { - GstQTPad *pad; - GstCollectData *data; - - data = (GstCollectData *) walk->data; - pad = (GstQTPad *) data; - - walk = g_slist_next (walk); - - buf = gst_collect_pads_peek (pads, data); - if (buf == NULL) { - GST_LOG_OBJECT (qtmux, "Pad %s has no buffers", - GST_PAD_NAME (pad->collect.pad)); - continue; - } - time = GST_BUFFER_TIMESTAMP (buf); - gst_buffer_unref (buf); - - /* invalid should pass */ - if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (time))) { - time = - gst_segment_to_running_time (&data->segment, GST_FORMAT_TIME, time); - if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) { - GST_DEBUG_OBJECT (qtmux, "clipping buffer on pad %s outside segment", - GST_PAD_NAME (data->pad)); - buf = gst_collect_pads_pop (pads, data); - gst_buffer_unref (buf); - return GST_FLOW_OK; - } - } - - if (best_pad == NULL || !GST_CLOCK_TIME_IS_VALID (time) || - (GST_CLOCK_TIME_IS_VALID (best_time) && time < best_time)) { - best_pad = pad; - best_time = time; - } - } + best_pad = (GstQTPad *) cdata; + /* clipping already converted to running time */ if (best_pad != NULL) { + g_assert (buf); + best_time = GST_BUFFER_TIMESTAMP (buf); GST_LOG_OBJECT (qtmux, "selected pad %s with time %" GST_TIME_FORMAT, GST_PAD_NAME (best_pad->collect.pad), GST_TIME_ARGS (best_time)); - buf = gst_collect_pads_pop (pads, &best_pad->collect); - buf = gst_buffer_make_writable (buf); - GST_BUFFER_TIMESTAMP (buf) = best_time; ret = gst_qt_mux_add_buffer (qtmux, best_pad, buf); } else { ret = gst_qt_mux_stop_file (qtmux); @@ -3284,14 +3249,14 @@ refuse_renegotiation: } static gboolean -gst_qt_mux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) +gst_qt_mux_sink_event (GstCollectPads2 * pads, GstCollectData2 * data, + GstEvent * event, gpointer user_data) { - gboolean ret; GstQTMux *qtmux; guint32 avg_bitrate = 0, max_bitrate = 0; + GstPad *pad = data->pad; - qtmux = GST_QT_MUX_CAST (parent); - + qtmux = GST_QT_MUX_CAST (user_data); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_CAPS: { @@ -3316,8 +3281,9 @@ gst_qt_mux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) GST_OBJECT_LOCK (qtmux); mode = gst_tag_setter_get_tag_merge_mode (setter); - GST_DEBUG_OBJECT (qtmux, "received tag event"); gst_event_parse_tag (event, &list); + GST_DEBUG_OBJECT (qtmux, "received tag event on pad %s:%s : %" + GST_PTR_FORMAT, GST_DEBUG_PAD_NAME (pad), list); gst_tag_setter_merge_tags (setter, list, mode); GST_OBJECT_UNLOCK (qtmux); @@ -3339,9 +3305,8 @@ gst_qt_mux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) break; } - ret = qtmux->collect_event (pad, parent, event); - - return ret; + /* now GstCollectPads2 can take care of the rest, e.g. EOS */ + return FALSE; } static void @@ -3363,7 +3328,7 @@ gst_qt_mux_release_pad (GstElement * element, GstPad * pad) } } - gst_collect_pads_remove_pad (mux->collect, pad); + gst_collect_pads2_remove_pad (mux->collect, pad); } static GstPad * @@ -3407,8 +3372,8 @@ gst_qt_mux_request_new_pad (GstElement * element, newpad = gst_pad_new_from_template (templ, name); g_free (name); collect_pad = (GstQTPad *) - gst_collect_pads_add_pad (qtmux->collect, newpad, sizeof (GstQTPad), - (GstCollectDataDestroyNotify) (gst_qt_mux_pad_reset)); + gst_collect_pads2_add_pad_full (qtmux->collect, newpad, sizeof (GstQTPad), + (GstCollectData2DestroyNotify) (gst_qt_mux_pad_reset), TRUE); /* set up pad */ gst_qt_mux_pad_reset (collect_pad); collect_pad->trak = atom_trak_new (qtmux->context); @@ -3422,14 +3387,6 @@ gst_qt_mux_request_new_pad (GstElement * element, else collect_pad->set_caps = GST_DEBUG_FUNCPTR (gst_qt_mux_video_sink_set_caps); - /* FIXME: hacked way to override/extend the event function of - * GstCollectPads; because it sets its own event function giving the - * element no access to events. - */ - qtmux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad); - gst_pad_set_event_function (newpad, - GST_DEBUG_FUNCPTR (gst_qt_mux_sink_event)); - gst_pad_set_active (newpad, TRUE); gst_element_add_pad (element, newpad); @@ -3566,13 +3523,13 @@ gst_qt_mux_change_state (GstElement * element, GstStateChange transition) case GST_STATE_CHANGE_NULL_TO_READY: break; case GST_STATE_CHANGE_READY_TO_PAUSED: - gst_collect_pads_start (qtmux->collect); + gst_collect_pads2_start (qtmux->collect); qtmux->state = GST_QT_MUX_STATE_STARTED; break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: break; case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_collect_pads_stop (qtmux->collect); + gst_collect_pads2_stop (qtmux->collect); break; default: break; diff --git a/gst/isomp4/gstqtmux.h b/gst/isomp4/gstqtmux.h index fdbbb5a54..c17ff25de 100644 --- a/gst/isomp4/gstqtmux.h +++ b/gst/isomp4/gstqtmux.h @@ -44,7 +44,7 @@ #define __GST_QT_MUX_H__ #include <gst/gst.h> -#include <gst/base/gstcollectpads.h> +#include <gst/base/gstcollectpads2.h> #include "fourcc.h" #include "atoms.h" @@ -82,7 +82,7 @@ typedef GstBuffer * (*GstQTPadPrepareBufferFunc) (GstQTPad * pad, struct _GstQTPad { - GstCollectData collect; /* we extend the CollectData */ + GstCollectData2 collect; /* we extend the CollectData2 */ /* fourcc id of stream */ guint32 fourcc; @@ -145,7 +145,7 @@ struct _GstQTMux GstElement element; GstPad *srcpad; - GstCollectPads *collect; + GstCollectPads2 *collect; GSList *sinkpads; /* state */ @@ -192,9 +192,6 @@ struct _GstQTMux guint32 fragment_duration; gboolean streamable; - /* for collect pads event handling function */ - GstPadEventFunction collect_event; - /* for request pad naming */ guint video_pads, audio_pads; }; diff --git a/gst/isomp4/qtdemux.c b/gst/isomp4/qtdemux.c index 2b9053fac..b7511c5c7 100644 --- a/gst/isomp4/qtdemux.c +++ b/gst/isomp4/qtdemux.c @@ -47,6 +47,10 @@ #include "config.h" #endif +/* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex + * with newer GLib versions (>= 2.31.0) */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + #include "gst/gst-i18n-plugin.h" #include <glib/gprintf.h> @@ -5317,8 +5321,13 @@ qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl) GST_LOG_OBJECT (qtdemux, "%u timestamp blocks", stream->n_sample_times); /* make sure there's enough data */ - if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 2 * 4)) - goto corrupt_file; + if (!qt_atom_parser_has_chunks (&stream->stts, stream->n_sample_times, 8)) { + stream->n_sample_times = gst_byte_reader_get_remaining (&stream->stts) / 8; + GST_LOG_OBJECT (qtdemux, "overriding to %u timestamp blocks", + stream->n_sample_times); + if (!stream->n_sample_times) + goto corrupt_file; + } /* sync sample atom */ stream->stps_present = FALSE; diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c index 404071c8c..f5fb27267 100644 --- a/gst/matroska/matroska-demux.c +++ b/gst/matroska/matroska-demux.c @@ -49,6 +49,10 @@ #include "config.h" #endif +/* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex + * with newer GLib versions (>= 2.31.0) */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + #include <math.h> #include <string.h> #include <glib/gprintf.h> @@ -482,6 +486,8 @@ gst_matroska_demux_reset (GstElement * element) gst_buffer_unref (demux->common.cached_buffer); demux->common.cached_buffer = NULL; } + + demux->invalid_duration = FALSE; } static GstBuffer * @@ -1356,10 +1362,12 @@ gst_matroska_demux_query (GstMatroskaDemux * demux, GstPad * pad, GST_OBJECT_LOCK (demux); if (context) gst_query_set_position (query, GST_FORMAT_TIME, - context->pos - demux->stream_start_time); + MAX (context->pos, demux->stream_start_time) - + demux->stream_start_time); else gst_query_set_position (query, GST_FORMAT_TIME, - demux->common.segment.position - demux->stream_start_time); + MAX (demux->common.segment.position, demux->stream_start_time) - + demux->stream_start_time); GST_OBJECT_UNLOCK (demux); } else if (format == GST_FORMAT_DEFAULT && context && context->default_duration) { @@ -1740,8 +1748,11 @@ gst_matroska_demux_search_pos (GstMatroskaDemux * demux, GstClockTime time) otime = demux->common.segment.position; GST_OBJECT_UNLOCK (demux); + /* sanitize */ + time = MAX (time, demux->stream_start_time); + /* avoid division by zero in first estimation below */ - if (otime == 0) + if (otime <= demux->stream_start_time) otime = time; retry: @@ -1918,6 +1929,14 @@ gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux, * segment when we close the current segment. */ memcpy (&seeksegment, &demux->common.segment, sizeof (GstSegment)); + /* pull mode without index means that the actual duration is not known, + * we might be playing a file that's still being recorded + * so, invalidate our current duration, which is only a moving target, + * and should not be used to clamp anything */ + if (!demux->streaming && !demux->common.index && demux->invalid_duration) { + seeksegment.duration = GST_CLOCK_TIME_NONE; + } + if (event) { GST_DEBUG_OBJECT (demux, "configuring seek"); gst_segment_do_seek (&seeksegment, rate, format, flags, @@ -1932,6 +1951,10 @@ gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux, } } + /* restore segment duration (if any effect), + * would be determined again when parsing, but anyway ... */ + seeksegment.duration = demux->common.segment.duration; + flush = ! !(flags & GST_SEEK_FLAG_FLUSH); keyunit = ! !(flags & GST_SEEK_FLAG_KEY_UNIT); @@ -1952,7 +1975,7 @@ gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux, seeksegment.position, &demux->seek_index, &demux->seek_entry)) == NULL) { /* pull mode without index can scan later on */ - if (demux->common.index || demux->streaming) { + if (demux->streaming) { GST_DEBUG_OBJECT (demux, "No matching seek entry in index"); GST_OBJECT_UNLOCK (demux); return FALSE; @@ -1989,7 +2012,7 @@ next: GST_PAD_STREAM_LOCK (demux->common.sinkpad); /* pull mode without index can do some scanning */ - if (!demux->streaming && !demux->common.index) { + if (!demux->streaming && !entry) { /* need to stop flushing upstream as we need it next */ if (flush) gst_pad_push_event (demux->common.sinkpad, @@ -2011,9 +2034,9 @@ next: if (keyunit) { GST_DEBUG_OBJECT (demux, "seek to key unit, adjusting segment start to %" GST_TIME_FORMAT, GST_TIME_ARGS (entry->time)); - seeksegment.start = entry->time; - seeksegment.position = entry->time; - seeksegment.time = entry->time - demux->stream_start_time; + seeksegment.start = MAX (entry->time, demux->stream_start_time); + seeksegment.position = seeksegment.start; + seeksegment.time = seeksegment.start - demux->stream_start_time; } exit: @@ -3347,9 +3370,9 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, else if (GST_CLOCK_TIME_IS_VALID (segment->position)) segment_duration = segment->position - segment->start; segment->base += segment_duration / fabs (segment->rate); - segment->start = lace_time; + segment->start = MAX (lace_time, demux->stream_start_time); segment->stop = GST_CLOCK_TIME_NONE; - segment->position = lace_time - demux->stream_start_time; + segment->position = segment->start - demux->stream_start_time; /* now convey our segment notion downstream */ gst_matroska_demux_send_event (demux, gst_event_new_segment (segment)); demux->need_segment = FALSE; @@ -3535,14 +3558,15 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, GST_OBJECT_LOCK (demux); if (demux->common.segment.duration == -1 || - demux->common.segment.duration < - lace_time - demux->stream_start_time) { + demux->stream_start_time + demux->common.segment.duration < + last_stop_end) { demux->common.segment.duration = last_stop_end - demux->stream_start_time; GST_OBJECT_UNLOCK (demux); gst_element_post_message (GST_ELEMENT_CAST (demux), gst_message_new_duration (GST_OBJECT_CAST (demux), GST_FORMAT_TIME, GST_CLOCK_TIME_NONE)); + demux->invalid_duration = TRUE; } else { GST_OBJECT_UNLOCK (demux); } @@ -4475,11 +4499,13 @@ pause: /* Close the segment, i.e. update segment stop with the duration * if no stop was set */ if (GST_CLOCK_TIME_IS_VALID (demux->last_stop_end) && - !GST_CLOCK_TIME_IS_VALID (demux->common.segment.stop)) { + !GST_CLOCK_TIME_IS_VALID (demux->common.segment.stop) && + GST_CLOCK_TIME_IS_VALID (demux->common.segment.start) && + demux->last_stop_end > demux->common.segment.start) { GstSegment segment = demux->common.segment; GstEvent *event; - segment.stop = MAX (demux->last_stop_end, segment.start); + segment.stop = demux->last_stop_end; event = gst_event_new_segment (&segment); gst_matroska_demux_send_event (demux, event); } diff --git a/gst/matroska/matroska-demux.h b/gst/matroska/matroska-demux.h index 65061c1d1..0a6312ea8 100644 --- a/gst/matroska/matroska-demux.h +++ b/gst/matroska/matroska-demux.h @@ -95,6 +95,9 @@ typedef struct _GstMatroskaDemux { /* gap handling */ guint64 max_gap_time; + + /* for non-finalized files, with invalid segment duration */ + gboolean invalid_duration; } GstMatroskaDemux; typedef struct _GstMatroskaDemuxClass { diff --git a/gst/matroska/matroska-mux.c b/gst/matroska/matroska-mux.c index f02c171a1..5fe25f957 100644 --- a/gst/matroska/matroska-mux.c +++ b/gst/matroska/matroska-mux.c @@ -2,6 +2,7 @@ * (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net> * (c) 2005 Michal Benes <michal.benes@xeris.cz> * (c) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk> + * (c) 2011 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk> * * matroska-mux.c: matroska file/stream muxer * @@ -183,10 +184,14 @@ static GstStaticPadTemplate audiosink_templ = ); static GstStaticPadTemplate subtitlesink_templ = -GST_STATIC_PAD_TEMPLATE ("subtitle_%u", + GST_STATIC_PAD_TEMPLATE ("subtitle_%d", GST_PAD_SINK, GST_PAD_REQUEST, - GST_STATIC_CAPS ("subtitle/x-kate")); + GST_STATIC_CAPS ("subtitle/x-kate; " + "text/plain; application/x-ssa; application/x-ass; " + "application/x-usf; video/x-dvd-subpicture; " + "application/x-subtitle-unknown") + ); static GArray *used_uids; G_LOCK_DEFINE_STATIC (used_uids); @@ -199,8 +204,10 @@ G_DEFINE_TYPE_WITH_CODE (GstMatroskaMux, gst_matroska_mux, GST_TYPE_ELEMENT, static void gst_matroska_mux_finalize (GObject * object); /* Pads collected callback */ -static GstFlowReturn -gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data); +static GstFlowReturn gst_matroska_mux_handle_buffer (GstCollectPads2 * pads, + GstCollectData2 * data, GstBuffer * buf, gpointer user_data); +static gboolean gst_matroska_mux_handle_sink_event (GstCollectPads2 * pads, + GstCollectData2 * data, GstEvent * event, gpointer user_data); /* pad functions */ static gboolean gst_matroska_mux_handle_src_event (GstPad * pad, @@ -415,10 +422,13 @@ gst_matroska_mux_init (GstMatroskaMux * mux) gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event); gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad); - mux->collect = gst_collect_pads_new (); - gst_collect_pads_set_function (mux->collect, - (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_matroska_mux_collected), - mux); + mux->collect = gst_collect_pads2_new (); + gst_collect_pads2_set_clip_function (mux->collect, + GST_DEBUG_FUNCPTR (gst_collect_pads2_clip_running_time), mux); + gst_collect_pads2_set_buffer_function (mux->collect, + GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_buffer), mux); + gst_collect_pads2_set_event_function (mux->collect, + GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event), mux); mux->ebml_write = gst_ebml_write_new (mux->srcpad); mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA; @@ -535,12 +545,6 @@ gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full) collect_pad->track = NULL; } - /* free cached buffer */ - if (collect_pad->buffer != NULL) { - gst_buffer_unref (collect_pad->buffer); - collect_pad->buffer = NULL; - } - if (!full && type != 0) { GstMatroskaTrackContext *context; @@ -568,7 +572,6 @@ gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full) /* TODO: check default values for the context */ context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT; collect_pad->track = context; - collect_pad->buffer = NULL; collect_pad->duration = 0; collect_pad->start_ts = GST_CLOCK_TIME_NONE; collect_pad->end_ts = GST_CLOCK_TIME_NONE; @@ -665,6 +668,44 @@ gst_matroska_mux_handle_src_event (GstPad * pad, GstObject * parent, return gst_pad_event_default (pad, parent, event); } +static void +gst_matroska_mux_build_vobsub_private (GstMatroskaTrackContext * context, + const guint * clut) +{ + gchar *clutv[17]; + gchar *sclut; + gint i; + guint32 col; + gdouble y, u, v; + guint8 r, g, b; + + /* produce comma-separated list in hex format */ + for (i = 0; i < 16; ++i) { + col = clut[i]; + /* replicate vobsub's slightly off RGB conversion calculation */ + y = (((col >> 16) & 0xff) - 16) * 255 / 219; + u = ((col >> 8) & 0xff) - 128; + v = (col & 0xff) - 128; + r = CLAMP (1.0 * y + 1.4022 * u, 0, 255); + g = CLAMP (1.0 * y - 0.3456 * u - 0.7145 * v, 0, 255); + b = CLAMP (1.0 * y + 1.7710 * v, 0, 255); + clutv[i] = g_strdup_printf ("%02x%02x%02x", r, g, b); + } + clutv[i] = NULL; + sclut = g_strjoinv (",", clutv); + + /* build codec private; only palette for now */ + g_free (context->codec_priv); + context->codec_priv = (guint8 *) g_strdup_printf ("palette: %s", sclut); + /* include terminating 0 */ + context->codec_priv_size = strlen ((gchar *) context->codec_priv) + 1; + g_free (sclut); + for (i = 0; i < 16; ++i) { + g_free (clutv[i]); + } +} + + /** * gst_matroska_mux_handle_sink_event: * @pad: Pad which received the event. @@ -675,14 +716,21 @@ gst_matroska_mux_handle_src_event (GstPad * pad, GstObject * parent, * Returns: #TRUE on success. */ static gboolean -gst_matroska_mux_handle_sink_event (GstPad * pad, GstObject * parent, - GstEvent * event) +gst_matroska_mux_handle_sink_event (GstCollectPads2 * pads, + GstCollectData2 * data, GstEvent * event, gpointer user_data) { - GstMatroskaTrackContext *context; GstMatroskaPad *collect_pad; - GstMatroskaMux *mux = GST_MATROSKA_MUX (parent); + GstMatroskaTrackContext *context; + GstMatroskaMux *mux; + GstPad *pad; GstTagList *list; - gboolean ret = TRUE; + gboolean ret = FALSE; + + mux = GST_MATROSKA_MUX (user_data); + collect_pad = (GstMatroskaPad *) data; + pad = data->pad; + context = collect_pad->track; + g_assert (context); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_CAPS:{ @@ -702,11 +750,6 @@ gst_matroska_mux_handle_sink_event (GstPad * pad, GstObject * parent, GST_DEBUG_OBJECT (mux, "received tag event"); gst_event_parse_tag (event, &list); - collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad); - g_assert (collect_pad); - context = collect_pad->track; - g_assert (context); - /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */ if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) { const gchar *lang_code; @@ -735,7 +778,6 @@ gst_matroska_mux_handle_sink_event (GstPad * pad, GstObject * parent, gst_event_parse_segment (event, &segment); if (segment->format != GST_FORMAT_TIME) { - ret = FALSE; gst_event_unref (event); event = NULL; } @@ -749,6 +791,31 @@ gst_matroska_mux_handle_sink_event (GstPad * pad, GstObject * parent, gst_event_replace (&mux->force_key_unit_event, NULL); mux->force_key_unit_event = event; event = NULL; + } else if (gst_structure_has_name (structure, "application/x-gst-dvd") && + !strcmp ("dvd-spu-clut-change", + gst_structure_get_string (structure, "event"))) { + gchar name[16]; + gint i, value; + guint clut[16]; + + GST_DEBUG_OBJECT (pad, "New DVD colour table received"); + if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE) { + GST_DEBUG_OBJECT (pad, "... discarding"); + break; + } + /* first transform event data into table form */ + for (i = 0; i < 16; i++) { + g_snprintf (name, sizeof (name), "clut%02d", i); + if (!gst_structure_get_int (structure, name, &value)) { + GST_ERROR_OBJECT (mux, "dvd-spu-clut-change event did not " + "contain %s field", name); + break; + } + clut[i] = value; + } + + /* transform into private data for stream; text form */ + gst_matroska_mux_build_vobsub_private (context, clut); } break; } @@ -756,13 +823,19 @@ gst_matroska_mux_handle_sink_event (GstPad * pad, GstObject * parent, break; } - /* now GstCollectPads can take care of the rest, e.g. EOS */ - if (event) - ret = mux->collect_event (pad, parent, event); - + /* now GstCollectPads2 can take care of the rest, e.g. EOS */ return ret; } +static void +gst_matroska_mux_set_codec_id (GstMatroskaTrackContext * context, + const char *id) +{ + g_assert (context && id); + if (context->codec_id) + g_free (context->codec_id); + context->codec_id = g_strdup (id); +} /** * gst_matroska_mux_video_pad_setcaps: @@ -872,12 +945,13 @@ skip_details: /* find type */ if (!strcmp (mimetype, "video/x-raw")) { const gchar *fstr; - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED); + gst_matroska_mux_set_codec_id (context, + GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED); fstr = gst_structure_get_string (structure, "format"); if (fstr && strlen (fstr) == 4) videocontext->fourcc = GST_STR_FOURCC (fstr); } else if (!strcmp (mimetype, "image/jpeg")) { - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG); + gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_MJPEG); } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */ ||!strcmp (mimetype, "video/x-huffyuv") || !strcmp (mimetype, "video/x-divx") @@ -970,11 +1044,13 @@ skip_details: (guint8 *) bih + sizeof (gst_riff_strf_vids), -1); } - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC); + gst_matroska_mux_set_codec_id (context, + GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC); context->codec_priv = (gpointer) bih; context->codec_priv_size = size; } else if (!strcmp (mimetype, "video/x-h264")) { - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC); + gst_matroska_mux_set_codec_id (context, + GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC); if (context->codec_priv != NULL) { g_free (context->codec_priv); @@ -991,7 +1067,7 @@ skip_details: } else if (!strcmp (mimetype, "video/x-theora")) { const GValue *streamheader; - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA); + gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_THEORA); if (context->codec_priv != NULL) { g_free (context->codec_priv); @@ -1006,22 +1082,25 @@ skip_details: goto refuse_caps; } } else if (!strcmp (mimetype, "video/x-dirac")) { - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC); + gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC); } else if (!strcmp (mimetype, "video/x-vp8")) { - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8); + gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP8); } else if (!strcmp (mimetype, "video/mpeg")) { gint mpegversion; gst_structure_get_int (structure, "mpegversion", &mpegversion); switch (mpegversion) { case 1: - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1); + gst_matroska_mux_set_codec_id (context, + GST_MATROSKA_CODEC_ID_VIDEO_MPEG1); break; case 2: - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2); + gst_matroska_mux_set_codec_id (context, + GST_MATROSKA_CODEC_ID_VIDEO_MPEG2); break; case 4: - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP); + gst_matroska_mux_set_codec_id (context, + GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP); break; default: goto refuse_caps; @@ -1044,16 +1123,20 @@ skip_details: gst_structure_get_int (structure, "rmversion", &rmversion); switch (rmversion) { case 1: - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1); + gst_matroska_mux_set_codec_id (context, + GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1); break; case 2: - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2); + gst_matroska_mux_set_codec_id (context, + GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2); break; case 3: - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3); + gst_matroska_mux_set_codec_id (context, + GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3); break; case 4: - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4); + gst_matroska_mux_set_codec_id (context, + GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4); break; default: goto refuse_caps; @@ -1551,13 +1634,16 @@ gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps) switch (layer) { case 1: - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1); + gst_matroska_mux_set_codec_id (context, + GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1); break; case 2: - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2); + gst_matroska_mux_set_codec_id (context, + GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2); break; case 3: - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3); + gst_matroska_mux_set_codec_id (context, + GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3); break; default: goto refuse_caps; @@ -1620,14 +1706,16 @@ gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps) goto refuse_caps; } if (GST_AUDIO_INFO_IS_BIG_ENDIAN (&info)) - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE); + gst_matroska_mux_set_codec_id (context, + GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE); else - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE); + gst_matroska_mux_set_codec_id (context, + GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE); break; - case GST_AUDIO_FORMAT_F32LE: case GST_AUDIO_FORMAT_F64LE: - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT); + gst_matroska_mux_set_codec_id (context, + GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT); break; default: @@ -1636,11 +1724,10 @@ gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps) } audiocontext->bitdepth = GST_AUDIO_INFO_WIDTH (&info); - } else if (!strcmp (mimetype, "audio/x-vorbis")) { const GValue *streamheader; - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS); + gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_VORBIS); if (context->codec_priv != NULL) { g_free (context->codec_priv); @@ -1657,7 +1744,7 @@ gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps) } else if (!strcmp (mimetype, "audio/x-flac")) { const GValue *streamheader; - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC); + gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_FLAC); if (context->codec_priv != NULL) { g_free (context->codec_priv); context->codec_priv = NULL; @@ -1673,7 +1760,7 @@ gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps) } else if (!strcmp (mimetype, "audio/x-speex")) { const GValue *streamheader; - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX); + gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_SPEEX); if (context->codec_priv != NULL) { g_free (context->codec_priv); context->codec_priv = NULL; @@ -1687,11 +1774,11 @@ gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps) goto refuse_caps; } } else if (!strcmp (mimetype, "audio/x-ac3")) { - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3); + gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_AC3); } else if (!strcmp (mimetype, "audio/x-eac3")) { - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_EAC3); + gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_EAC3); } else if (!strcmp (mimetype, "audio/x-dts")) { - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_DTS); + gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_DTS); } else if (!strcmp (mimetype, "audio/x-tta")) { gint width; @@ -1700,7 +1787,7 @@ gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps) gst_structure_get_int (structure, "width", &width); audiocontext->bitdepth = width; - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA); + gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_TTA); } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) { gint raversion; @@ -1709,13 +1796,16 @@ gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps) gst_structure_get_int (structure, "raversion", &raversion); switch (raversion) { case 1: - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4); + gst_matroska_mux_set_codec_id (context, + GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4); break; case 2: - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8); + gst_matroska_mux_set_codec_id (context, + GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8); break; case 8: - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK); + gst_matroska_mux_set_codec_id (context, + GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK); break; default: goto refuse_caps; @@ -1816,7 +1906,7 @@ gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps) (guint8 *) codec_priv + WAVEFORMATEX_SIZE, -1); } - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM); + gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_ACM); context->codec_priv = (gpointer) codec_priv; context->codec_priv_size = codec_priv_size; } @@ -1832,6 +1922,10 @@ refuse_caps: } } +/* we probably don't have the data at start, + * so have to reserve (a maximum) space to write this at the end. + * bit spacy, but some formats can hold quite some */ +#define SUBTITLE_MAX_CODEC_PRIVATE 2048 /* must be > 128 */ /** * gst_matroska_mux_subtitle_pad_setcaps: @@ -1845,11 +1939,6 @@ refuse_caps: static gboolean gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps) { - /* FIXME: - * Consider this as boilerplate code for now. There is - * no single subtitle creation element in GStreamer, - * neither do I know how subtitling works at all. */ - /* There is now (at least) one such alement (kateenc), and I'm going to handle it here and claim it works when it can be piped back through GStreamer and VLC */ @@ -1860,6 +1949,10 @@ gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps) GstMatroskaPad *collect_pad; const gchar *mimetype; GstStructure *structure; + const GValue *value = NULL; + GstBuffer *buf = NULL; + gchar *id = NULL; + gboolean ret = TRUE; mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad)); @@ -1874,17 +1967,19 @@ gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps) structure = gst_caps_get_structure (caps, 0); mimetype = gst_structure_get_name (structure); + /* keep track of default set in request_pad */ + id = context->codec_id; + /* general setup */ scontext->check_utf8 = 1; scontext->invalid_utf8 = 0; context->default_duration = 0; - /* TODO: - other format than Kate */ - if (!strcmp (mimetype, "subtitle/x-kate")) { const GValue *streamheader; - context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE); + gst_matroska_mux_set_codec_id (context, + GST_MATROSKA_CODEC_ID_SUBTITLE_KATE); if (context->codec_priv != NULL) { g_free (context->codec_priv); @@ -1896,12 +1991,64 @@ gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps) if (!kate_streamheader_to_codecdata (streamheader, context)) { GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL), ("kate stream headers missing or malformed")); - return FALSE; - } - return TRUE; + ret = FALSE; + goto exit; + } + } else if (!strcmp (mimetype, "text/plain")) { + gst_matroska_mux_set_codec_id (context, + GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8); + } else if (!strcmp (mimetype, "application/x-ssa")) { + gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_SSA); + } else if (!strcmp (mimetype, "application/x-ass")) { + gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_ASS); + } else if (!strcmp (mimetype, "application/x-usf")) { + gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_USF); + } else if (!strcmp (mimetype, "video/x-dvd-subpicture")) { + gst_matroska_mux_set_codec_id (context, + GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB); + } else { + id = NULL; + ret = FALSE; + goto exit; } - return FALSE; + /* maybe some private data, e.g. vobsub */ + value = gst_structure_get_value (structure, "codec_data"); + if (value) + buf = gst_value_get_buffer (value); + if (buf != NULL) { + guint8 *priv_data = NULL, *priv_buffer_data; + gsize priv_data_size = 0; + + priv_buffer_data = + gst_buffer_map (buf, &priv_data_size, NULL, GST_MAP_READ); + if (priv_data_size > SUBTITLE_MAX_CODEC_PRIVATE) { + GST_WARNING_OBJECT (mux, "pad %" GST_PTR_FORMAT " subtitle private data" + " exceeded maximum (%d); discarding", pad, + SUBTITLE_MAX_CODEC_PRIVATE); + gst_buffer_unmap (buf, priv_data, priv_data_size); + return TRUE; + } + + if (context->codec_priv != NULL) + g_free (context->codec_priv); + + priv_data = g_malloc0 (priv_data_size); + memcpy (priv_data, priv_buffer_data, priv_data_size); + context->codec_priv = priv_data; + context->codec_priv_size = priv_data_size; + gst_buffer_unmap (buf, priv_buffer_data, priv_data_size); + } + + GST_DEBUG_OBJECT (pad, "codec_id %s, codec data size %u", + GST_STR_NULL (context->codec_id), context->codec_priv_size); + +exit: + /* free default if modified */ + if (id) + g_free (id); + + return ret; } @@ -1928,6 +2075,8 @@ gst_matroska_mux_request_new_pad (GstElement * element, GstMatroskaCapsFunc capsfunc = NULL; GstMatroskaTrackContext *context = NULL; gint pad_id; + gboolean locked = TRUE; + gchar *id = NULL; if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) { /* don't mix named and unnamed pads, if the pad already exists we fail when @@ -1971,6 +2120,9 @@ gst_matroska_mux_request_new_pad (GstElement * element, g_new0 (GstMatroskaTrackSubtitleContext, 1); context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE; context->name = g_strdup ("Subtitle"); + /* setcaps may only provide proper one a lot later */ + id = g_strdup ("S_SUB_UNKNOWN"); + locked = FALSE; } else { GST_WARNING_OBJECT (mux, "This is not our template!"); return NULL; @@ -1982,24 +2134,13 @@ gst_matroska_mux_request_new_pad (GstElement * element, gst_matroskamux_pad_init (newpad); collect_pad = (GstMatroskaPad *) - gst_collect_pads_add_pad (mux->collect, GST_PAD (newpad), - sizeof (GstMatroskaPad), - (GstCollectDataDestroyNotify) gst_matroska_pad_free); + gst_collect_pads2_add_pad_full (mux->collect, GST_PAD (newpad), + sizeof (GstMatroskamuxPad), + (GstCollectData2DestroyNotify) gst_matroska_pad_free, locked); collect_pad->track = context; gst_matroska_pad_reset (collect_pad, FALSE); - - /* FIXME: hacked way to override/extend the event function of - * GstCollectPads; because it sets its own event function giving the - * element no access to events. - * TODO GstCollectPads should really give its 'users' a clean chance to - * properly handle events that are not meant for collectpads itself. - * Perhaps a callback or so, though rejected (?) in #340060. - * This would allow (clean) transcoding of info from demuxer/streams - * to another muxer */ - mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad); - gst_pad_set_event_function (GST_PAD (newpad), - GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event)); + collect_pad->track->codec_id = id; collect_pad->capsfunc = capsfunc; gst_pad_set_active (GST_PAD (newpad), TRUE); @@ -2037,7 +2178,7 @@ gst_matroska_mux_release_pad (GstElement * element, GstPad * pad) mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad)); for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) { - GstCollectData *cdata = (GstCollectData *) walk->data; + GstCollectData2 *cdata = (GstCollectData2 *) walk->data; GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata; if (cdata->pad == pad) { @@ -2058,7 +2199,7 @@ gst_matroska_mux_release_pad (GstElement * element, GstPad * pad) } } - gst_collect_pads_remove_pad (mux->collect, pad); + gst_collect_pads2_remove_pad (mux->collect, pad); if (gst_element_remove_pad (element, pad)) mux->num_streams--; } @@ -2095,6 +2236,14 @@ gst_matroska_mux_track_header (GstMatroskaMux * mux, context->language); } + /* FIXME: until we have a nice way of getting the codecname + * out of the caps, I'm not going to enable this. Too much + * (useless, double, boring) work... */ + /* TODO: Use value from tags if any */ + /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME, + context->codec_name); */ + gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name); + /* type-specific stuff */ switch (context->type) { case GST_MATROSKA_TRACK_TYPE_VIDEO:{ @@ -2145,6 +2294,24 @@ gst_matroska_mux_track_header (GstMatroskaMux * mux, break; } + /* this is what we write for now and must be filled + * and remainder void'ed later on */ +#define SUBTITLE_DUMMY_SIZE (1 + 1 + 14 + 1 + 2 + SUBTITLE_MAX_CODEC_PRIVATE) + + case GST_MATROSKA_TRACK_TYPE_SUBTITLE:{ + gpointer buf; + + context->pos = ebml->pos; + /* CodecID is mandatory ... */ + gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, "S_SUB_UNKNOWN"); + /* reserve space */ + buf = g_malloc0 (SUBTITLE_MAX_CODEC_PRIVATE); + gst_ebml_write_binary (ebml, GST_EBML_ID_VOID, buf, + SUBTITLE_MAX_CODEC_PRIVATE); + g_free (buf); + /* real data has to be written at finish */ + return; + } default: /* doesn't need type-specific data */ break; @@ -2154,13 +2321,6 @@ gst_matroska_mux_track_header (GstMatroskaMux * mux, if (context->codec_priv) gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE, context->codec_priv, context->codec_priv_size); - /* FIXME: until we have a nice way of getting the codecname - * out of the caps, I'm not going to enable this. Too much - * (useless, double, boring) work... */ - /* TODO: Use value from tags if any */ - /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME, - context->codec_name); */ - gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name); } @@ -2230,7 +2390,7 @@ gst_matroska_mux_start (GstMatroskaMux * mux) if (tags != NULL && !gst_tag_list_is_empty (tags)) { guint64 master_tags, master_tag; - GST_DEBUG ("Writing tags"); + GST_DEBUG_OBJECT (mux, "Writing tags"); /* TODO: maybe limit via the TARGETS id by looking at the source pad */ mux->tags_pos = ebml->pos; @@ -2304,7 +2464,7 @@ gst_matroska_mux_start (GstMatroskaMux * mux) child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY); gst_matroska_mux_track_header (mux, collect_pad->track); gst_ebml_write_master_finish (ebml, child); - /* some remaing pad/track setup */ + /* some remaining pad/track setup */ collect_pad->default_duration_scaled = gst_util_uint64_scale (collect_pad->track->default_duration, 1, mux->time_scale); @@ -2431,7 +2591,7 @@ gst_matroska_mux_finish (GstMatroskaMux * mux) if (tags != NULL && !gst_tag_list_is_empty (tags)) { guint64 master_tags, master_tag; - GST_DEBUG ("Writing tags"); + GST_DEBUG_OBJECT (mux, "Writing tags"); /* TODO: maybe limit via the TARGETS id by looking at the source pad */ mux->tags_pos = ebml->pos; @@ -2479,16 +2639,23 @@ gst_matroska_mux_finish (GstMatroskaMux * mux) gst_ebml_write_seek (ebml, my_pos); } - /* update duration */ - /* first get the overall duration */ - /* a released track may have left a duration in here */ + /* loop tracks: + * - first get the overall duration + * (a released track may have left a duration in here) + * - write some track header data for subtitles + */ duration = mux->duration; + pos = ebml->pos; for (collected = mux->collect->data; collected; collected = g_slist_next (collected)) { GstMatroskaPad *collect_pad; GstClockTime min_duration; /* observed minimum duration */ + GstMatroskaTrackContext *context; + gint voidleft = 0, fill = 0; + gpointer codec_id; collect_pad = (GstMatroskaPad *) collected->data; + context = collect_pad->track; GST_DEBUG_OBJECT (mux, "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT @@ -2510,7 +2677,41 @@ gst_matroska_mux_finish (GstMatroskaMux * mux) if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) && duration < collect_pad->duration) duration = collect_pad->duration; - } + + if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE || !context->pos) + continue; + + again: + /* write subtitle type and possible private data */ + gst_ebml_write_seek (ebml, context->pos); + /* complex way to write ascii to account for extra filling */ + codec_id = g_malloc0 (strlen (context->codec_id) + 1 + fill); + strcpy (codec_id, context->codec_id); + gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECID, + codec_id, strlen (context->codec_id) + 1 + fill); + g_free (codec_id); + if (context->codec_priv) + gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE, + context->codec_priv, context->codec_priv_size); + voidleft = SUBTITLE_DUMMY_SIZE - (ebml->pos - context->pos); + /* void'ify; sigh, variable sized length field */ + if (voidleft == 1) { + fill = 1; + goto again; + } else if (voidleft && voidleft <= 128) + gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 2); + else if (voidleft >= 130) + gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 3); + else if (voidleft == 129) { + gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 64); + gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 63); + } + } + + /* seek back (optional, but do anyway) */ + gst_ebml_write_seek (ebml, pos); + + /* update duration */ if (duration != 0) { GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT, GST_TIME_ARGS (duration)); @@ -2533,77 +2734,6 @@ gst_matroska_mux_finish (GstMatroskaMux * mux) gst_ebml_write_master_finish (ebml, mux->segment_pos); } - -/** - * gst_matroska_mux_best_pad: - * @mux: #GstMatroskaMux - * @popped: True if at least one buffer was popped from #GstCollectPads - * - * Find a pad with the oldest data - * (data from this pad should be written first). - * - * Returns: Selected pad. - */ -static GstMatroskaPad * -gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped) -{ - GSList *collected; - GstMatroskaPad *best = NULL; - - *popped = FALSE; - for (collected = mux->collect->data; collected; - collected = g_slist_next (collected)) { - GstMatroskaPad *collect_pad; - - collect_pad = (GstMatroskaPad *) collected->data; - /* fetch a new buffer if needed */ - if (collect_pad->buffer == NULL) { - collect_pad->buffer = gst_collect_pads_pop (mux->collect, - (GstCollectData *) collect_pad); - - if (collect_pad->buffer != NULL) { - GstClockTime time; - - *popped = TRUE; - /* convert to running time */ - time = GST_BUFFER_TIMESTAMP (collect_pad->buffer); - /* invalid should pass */ - if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (time))) { - time = gst_segment_to_running_time (&collect_pad->collect.segment, - GST_FORMAT_TIME, time); - if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) { - GST_DEBUG_OBJECT (mux, "clipping buffer on pad %s outside segment", - GST_PAD_NAME (collect_pad->collect.pad)); - gst_buffer_unref (collect_pad->buffer); - collect_pad->buffer = NULL; - return NULL; - } else { - GST_LOG_OBJECT (mux, "buffer ts %" GST_TIME_FORMAT " -> %" - GST_TIME_FORMAT " running time", - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (collect_pad->buffer)), - GST_TIME_ARGS (time)); - collect_pad->buffer = - gst_buffer_make_writable (collect_pad->buffer); - GST_BUFFER_TIMESTAMP (collect_pad->buffer) = time; - } - } - } - } - - /* if we have a buffer check if it is better then the current best one */ - if (collect_pad->buffer != NULL) { - if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer) - || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer) - && GST_BUFFER_TIMESTAMP (collect_pad->buffer) < - GST_BUFFER_TIMESTAMP (best->buffer))) { - best = collect_pad; - } - } - } - - return best; -} - /** * gst_matroska_mux_buffer_header: * @track: Track context. @@ -2749,10 +2879,11 @@ gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux) * Returns: Result of the gst_pad_push issued to write the data. */ static GstFlowReturn -gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad) +gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad, + GstBuffer * buf) { GstEbmlWrite *ebml = mux->ebml_write; - GstBuffer *buf, *hdr; + GstBuffer *hdr; guint64 blockgroup; gboolean write_duration; gint16 relative_timestamp; @@ -2762,8 +2893,6 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad) GstMatroskamuxPad *pad; /* write data */ - buf = collect_pad->buffer; - collect_pad->buffer = NULL; pad = GST_MATROSKAMUX_PAD_CAST (collect_pad->collect.pad); /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */ @@ -2953,10 +3082,9 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad) } } - /** - * gst_matroska_mux_collected: - * @pads: #GstCollectPads + * gst_matroska_mux_handle_buffer: + * @pads: #GstCollectPads2 * @uuser_data: #GstMatroskaMux * * Collectpads callback. @@ -2964,12 +3092,12 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad) * Returns: #GstFlowReturn */ static GstFlowReturn -gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data) +gst_matroska_mux_handle_buffer (GstCollectPads2 * pads, GstCollectData2 * data, + GstBuffer * buf, gpointer user_data) { GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data); GstEbmlWrite *ebml = mux->ebml_write; GstMatroskaPad *best; - gboolean popped; GstFlowReturn ret = GST_FLOW_OK; GST_DEBUG_OBJECT (mux, "Collected pads"); @@ -2988,53 +3116,53 @@ gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data) mux->state = GST_MATROSKA_MUX_STATE_DATA; } - do { - /* which stream to write from? */ - best = gst_matroska_mux_best_pad (mux, &popped); + /* provided with stream to write from */ + best = (GstMatroskaPad *) data; - /* if there is no best pad, we have reached EOS */ - if (best == NULL) { - /* buffer popped, but none returned means it was clipped */ - if (popped) - break; - GST_DEBUG_OBJECT (mux, "No best pad finishing..."); - if (!mux->streamable) { - gst_matroska_mux_finish (mux); - } else { - GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish"); - } - gst_pad_push_event (mux->srcpad, gst_event_new_eos ()); - ret = GST_FLOW_EOS; - break; + /* if there is no best pad, we have reached EOS */ + if (best == NULL) { + GST_DEBUG_OBJECT (mux, "No best pad finishing..."); + if (!mux->streamable) { + gst_matroska_mux_finish (mux); + } else { + GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish"); } - GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %" - GST_TIME_FORMAT " dur %" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)), - GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer))); + gst_pad_push_event (mux->srcpad, gst_event_new_eos ()); + ret = GST_FLOW_EOS; + goto exit; + } - /* make note of first and last encountered timestamps, so we can calculate - * the actual duration later when we send an updated header on eos */ - if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) { - GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer); - GstClockTime end_ts = start_ts; + /* if we have a best stream, should also have a buffer */ + g_assert (buf); - if (GST_BUFFER_DURATION_IS_VALID (best->buffer)) - end_ts += GST_BUFFER_DURATION (best->buffer); - else if (best->track->default_duration) - end_ts += best->track->default_duration; + GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %" + GST_TIME_FORMAT " dur %" GST_TIME_FORMAT, + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), + GST_TIME_ARGS (GST_BUFFER_DURATION (buf))); - if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts) - best->end_ts = end_ts; + /* make note of first and last encountered timestamps, so we can calculate + * the actual duration later when we send an updated header on eos */ + if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { + GstClockTime start_ts = GST_BUFFER_TIMESTAMP (buf); + GstClockTime end_ts = start_ts; - if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE || - start_ts < best->start_ts)) - best->start_ts = start_ts; - } + if (GST_BUFFER_DURATION_IS_VALID (buf)) + end_ts += GST_BUFFER_DURATION (buf); + else if (best->track->default_duration) + end_ts += best->track->default_duration; + + if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts) + best->end_ts = end_ts; + + if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE || + start_ts < best->start_ts)) + best->start_ts = start_ts; + } - /* write one buffer */ - ret = gst_matroska_mux_write_data (mux, best); - } while (ret == GST_FLOW_OK && !popped); + /* write one buffer */ + ret = gst_matroska_mux_write_data (mux, best, buf); +exit: return ret; } @@ -3058,12 +3186,12 @@ gst_matroska_mux_change_state (GstElement * element, GstStateChange transition) case GST_STATE_CHANGE_NULL_TO_READY: break; case GST_STATE_CHANGE_READY_TO_PAUSED: - gst_collect_pads_start (mux->collect); + gst_collect_pads2_start (mux->collect); break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: break; case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_collect_pads_stop (mux->collect); + gst_collect_pads2_stop (mux->collect); break; default: break; diff --git a/gst/matroska/matroska-mux.h b/gst/matroska/matroska-mux.h index 4377f26b7..da9a3e7bf 100644 --- a/gst/matroska/matroska-mux.h +++ b/gst/matroska/matroska-mux.h @@ -24,7 +24,7 @@ #define __GST_MATROSKA_MUX_H__ #include <gst/gst.h> -#include <gst/base/gstcollectpads.h> +#include <gst/base/gstcollectpads2.h> #include "ebml-write.h" #include "matroska-ids.h" @@ -58,12 +58,10 @@ typedef gboolean (*GstMatroskaCapsFunc) (GstPad *pad, GstCaps *caps); /* all information needed for one matroska stream */ typedef struct { - GstCollectData collect; /* we extend the CollectData */ + GstCollectData2 collect; /* we extend the CollectData */ GstMatroskaCapsFunc capsfunc; GstMatroskaTrackContext *track; - GstBuffer *buffer; /* the queued buffer for this pad */ - guint64 duration; GstClockTime start_ts; GstClockTime end_ts; /* last timestamp + (if available) duration */ @@ -79,8 +77,7 @@ typedef struct _GstMatroskaMux { /* pads */ GstPad *srcpad; - GstCollectPads *collect; - GstPadEventFunction collect_event; + GstCollectPads2 *collect; GstEbmlWrite *ebml_write; guint num_streams, diff --git a/gst/matroska/matroska-read-common.c b/gst/matroska/matroska-read-common.c index fe07e295a..0b45855df 100644 --- a/gst/matroska/matroska-read-common.c +++ b/gst/matroska/matroska-read-common.c @@ -999,6 +999,21 @@ gst_matroska_read_common_parse_index_cuetrack (GstMatroskaReadCommon * common, DEBUG_ELEMENT_STOP (common, ebml, "CueTrackPositions", ret); + /* (e.g.) lavf typically creates entries without a block number, + * which is bogus and leads to contradictory information */ + if (common->index->len) { + GstMatroskaIndex *last_idx; + + last_idx = &g_array_index (common->index, GstMatroskaIndex, + common->index->len - 1); + if (last_idx->block == idx.block && last_idx->pos == idx.pos && + last_idx->track == idx.track && idx.time > last_idx->time) { + GST_DEBUG_OBJECT (common, "Cue entry refers to same location, " + "but has different time than previous entry; discarding"); + idx.track = 0; + } + } + if ((ret == GST_FLOW_OK || ret == GST_FLOW_EOS) && idx.pos != (guint64) - 1 && idx.track > 0) { g_array_append_val (common->index, idx); diff --git a/gst/multifile/gstmultifilesink.c b/gst/multifile/gstmultifilesink.c index 50d1f976d..af192900f 100644 --- a/gst/multifile/gstmultifilesink.c +++ b/gst/multifile/gstmultifilesink.c @@ -655,12 +655,17 @@ write_error: return GST_FLOW_ERROR; } stdio_write_error: - { - GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE, - ("Error while writing to file."), (NULL)); - gst_buffer_unmap (buffer, data, size); - return GST_FLOW_ERROR; + switch (errno) { + case ENOSPC: + GST_ELEMENT_ERROR (multifilesink, RESOURCE, NO_SPACE_LEFT, + ("Error while writing to file."), ("%s", g_strerror (errno))); + break; + default: + GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE, + ("Error while writing to file."), ("%s", g_strerror (errno))); } + gst_buffer_unmap (buffer, data, size); + return GST_FLOW_ERROR; } static gboolean diff --git a/gst/multipart/multipartmux.c b/gst/multipart/multipartmux.c index 73ddbbefd..475630485 100644 --- a/gst/multipart/multipartmux.c +++ b/gst/multipart/multipartmux.c @@ -84,7 +84,7 @@ static GstPad *gst_multipart_mux_request_new_pad (GstElement * element, static GstStateChangeReturn gst_multipart_mux_change_state (GstElement * element, GstStateChange transition); -static GstFlowReturn gst_multipart_mux_collected (GstCollectPads * pads, +static GstFlowReturn gst_multipart_mux_collected (GstCollectPads2 * pads, GstMultipartMux * mux); static void gst_multipart_mux_set_property (GObject * object, guint prop_id, @@ -148,9 +148,9 @@ gst_multipart_mux_init (GstMultipartMux * multipart_mux) multipart_mux->boundary = g_strdup (DEFAULT_BOUNDARY); - multipart_mux->collect = gst_collect_pads_new (); - gst_collect_pads_set_function (multipart_mux->collect, - (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_multipart_mux_collected), + multipart_mux->collect = gst_collect_pads2_new (); + gst_collect_pads2_set_function (multipart_mux->collect, + (GstCollectPads2Function) GST_DEBUG_FUNCPTR (gst_multipart_mux_collected), multipart_mux); } @@ -194,8 +194,8 @@ gst_multipart_mux_request_new_pad (GstElement * element, GstMultipartPadData *multipartpad; multipartpad = (GstMultipartPadData *) - gst_collect_pads_add_pad (multipart_mux->collect, newpad, - sizeof (GstMultipartPadData), NULL); + gst_collect_pads2_add_pad (multipart_mux->collect, newpad, + sizeof (GstMultipartPadData)); /* save a pointer to our data in the pad */ multipartpad->pad = newpad; @@ -340,7 +340,7 @@ gst_multipart_mux_queue_pads (GstMultipartMux * mux) /* try to make sure we have a buffer from each usable pad first */ walk = mux->collect->data; while (walk) { - GstCollectData *data = (GstCollectData *) walk->data; + GstCollectData2 *data = (GstCollectData2 *) walk->data; GstMultipartPadData *pad = (GstMultipartPadData *) data; walk = g_slist_next (walk); @@ -349,7 +349,7 @@ gst_multipart_mux_queue_pads (GstMultipartMux * mux) if (pad->buffer == NULL) { GstBuffer *buf = NULL; - buf = gst_collect_pads_pop (mux->collect, data); + buf = gst_collect_pads2_pop (mux->collect, data); /* Store timestamp with segment_start and preroll */ if (buf && GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { @@ -383,7 +383,7 @@ gst_multipart_mux_queue_pads (GstMultipartMux * mux) * 3) push both buffers on best pad, go to 1 */ static GstFlowReturn -gst_multipart_mux_collected (GstCollectPads * pads, GstMultipartMux * mux) +gst_multipart_mux_collected (GstCollectPads2 * pads, GstMultipartMux * mux) { GstMultipartPadData *best; GstFlowReturn ret = GST_FLOW_OK; @@ -605,11 +605,11 @@ gst_multipart_mux_change_state (GstElement * element, GstStateChange transition) multipart_mux->negotiated = FALSE; multipart_mux->need_segment = TRUE; GST_DEBUG_OBJECT (multipart_mux, "starting collect pads"); - gst_collect_pads_start (multipart_mux->collect); + gst_collect_pads2_start (multipart_mux->collect); break; case GST_STATE_CHANGE_PAUSED_TO_READY: GST_DEBUG_OBJECT (multipart_mux, "stopping collect pads"); - gst_collect_pads_stop (multipart_mux->collect); + gst_collect_pads2_stop (multipart_mux->collect); break; default: break; diff --git a/gst/multipart/multipartmux.h b/gst/multipart/multipartmux.h index d2711ac0e..4583b5098 100644 --- a/gst/multipart/multipartmux.h +++ b/gst/multipart/multipartmux.h @@ -23,7 +23,7 @@ #define __GST_MULTIPART_MUX__ #include <gst/gst.h> -#include <gst/base/gstcollectpads.h> +#include <gst/base/gstcollectpads2.h> #include <string.h> @@ -42,7 +42,7 @@ typedef struct _GstMultipartMuxClass GstMultipartMuxClass; /* all information needed for one multipart stream */ typedef struct { - GstCollectData collect; /* we extend the CollectData */ + GstCollectData2 collect; /* we extend the CollectData2 */ GstBuffer *buffer; /* the queued buffer for this pad */ GstClockTime timestamp; /* its timestamp, converted to running_time so that we can @@ -64,7 +64,7 @@ struct _GstMultipartMux GstPad *srcpad; /* sinkpads */ - GstCollectPads *collect; + GstCollectPads2 *collect; gint numpads; diff --git a/gst/rtpmanager/gstrtpbin.c b/gst/rtpmanager/gstrtpbin.c index 2575b224d..615fa3c41 100644 --- a/gst/rtpmanager/gstrtpbin.c +++ b/gst/rtpmanager/gstrtpbin.c @@ -129,6 +129,8 @@ #include "gstrtpsession.h" #include "gstrtpjitterbuffer.h" +#include <gst/glib-compat-private.h> + GST_DEBUG_CATEGORY_STATIC (gst_rtp_bin_debug); #define GST_CAT_DEFAULT gst_rtp_bin_debug diff --git a/gst/rtpmanager/gstrtpjitterbuffer.c b/gst/rtpmanager/gstrtpjitterbuffer.c index 0472f2c8e..e6ef09447 100644 --- a/gst/rtpmanager/gstrtpjitterbuffer.c +++ b/gst/rtpmanager/gstrtpjitterbuffer.c @@ -67,6 +67,8 @@ #include "rtpjitterbuffer.h" #include "rtpstats.h" +#include <gst/glib-compat-private.h> + GST_DEBUG_CATEGORY (rtpjitterbuffer_debug); #define GST_CAT_DEFAULT (rtpjitterbuffer_debug) diff --git a/gst/rtpmanager/gstrtpsession.c b/gst/rtpmanager/gstrtpsession.c index bb330ddd9..a5df3273b 100644 --- a/gst/rtpmanager/gstrtpsession.c +++ b/gst/rtpmanager/gstrtpsession.c @@ -114,6 +114,8 @@ #include <gst/rtp/gstrtpbuffer.h> +#include <gst/glib-compat-private.h> + #include "gstrtpbin-marshal.h" #include "gstrtpsession.h" #include "rtpsession.h" @@ -908,8 +910,13 @@ start_rtcp_thread (GstRtpSession * rtpsession) g_thread_join (rtpsession->priv->thread); /* only create a new thread if the old one was stopped. Otherwise we can * just reuse the currently running one. */ +#if !GLIB_CHECK_VERSION (2, 31, 0) rtpsession->priv->thread = g_thread_create ((GThreadFunc) rtcp_thread, rtpsession, TRUE, &error); +#else + rtpsession->priv->thread = g_thread_try_new ("rtpsession-rtcp-thread", + (GThreadFunc) rtcp_thread, rtpsession, &error); +#endif rtpsession->priv->thread_stopped = FALSE; } GST_RTP_SESSION_UNLOCK (rtpsession); diff --git a/gst/rtpmanager/gstrtpssrcdemux.c b/gst/rtpmanager/gstrtpssrcdemux.c index 2737c6d89..8a350ba65 100644 --- a/gst/rtpmanager/gstrtpssrcdemux.c +++ b/gst/rtpmanager/gstrtpssrcdemux.c @@ -44,6 +44,10 @@ #include "config.h" #endif +/* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex + * with newer GLib versions (>= 2.31.0) */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + #include <string.h> #include <gst/rtp/gstrtpbuffer.h> #include <gst/rtp/gstrtcpbuffer.h> diff --git a/gst/rtpmanager/rtpsession.c b/gst/rtpmanager/rtpsession.c index a73654657..aca25fe7a 100644 --- a/gst/rtpmanager/rtpsession.c +++ b/gst/rtpmanager/rtpsession.c @@ -22,6 +22,8 @@ #include <gst/rtp/gstrtpbuffer.h> #include <gst/rtp/gstrtcpbuffer.h> +#include <gst/glib-compat-private.h> + #include "gstrtpbin-marshal.h" #include "rtpsession.h" diff --git a/gst/rtsp/gstrtspsrc.c b/gst/rtsp/gstrtspsrc.c index 2694fc665..bbdbf7d44 100644 --- a/gst/rtsp/gstrtspsrc.c +++ b/gst/rtsp/gstrtspsrc.c @@ -81,6 +81,10 @@ #include "config.h" #endif +/* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex + * with newer GLib versions (>= 2.31.0) */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + #ifdef HAVE_UNISTD_H #include <unistd.h> #endif /* HAVE_UNISTD_H */ diff --git a/gst/smpte/gstsmpte.c b/gst/smpte/gstsmpte.c index a830e6a52..beb979fc6 100644 --- a/gst/smpte/gstsmpte.c +++ b/gst/smpte/gstsmpte.c @@ -152,7 +152,7 @@ static void gst_smpte_base_init (GstSMPTEClass * klass); static void gst_smpte_init (GstSMPTE * smpte); static void gst_smpte_finalize (GstSMPTE * smpte); -static GstFlowReturn gst_smpte_collected (GstCollectPads * pads, +static GstFlowReturn gst_smpte_collected (GstCollectPads2 * pads, GstSMPTE * smpte); static void gst_smpte_set_property (GObject * object, guint prop_id, @@ -360,15 +360,15 @@ gst_smpte_init (GstSMPTE * smpte) gst_pad_new_from_static_template (&gst_smpte_src_template, "src"); gst_element_add_pad (GST_ELEMENT (smpte), smpte->srcpad); - smpte->collect = gst_collect_pads_new (); - gst_collect_pads_set_function (smpte->collect, - (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_smpte_collected), smpte); - gst_collect_pads_start (smpte->collect); + smpte->collect = gst_collect_pads2_new (); + gst_collect_pads2_set_function (smpte->collect, + (GstCollectPads2Function) GST_DEBUG_FUNCPTR (gst_smpte_collected), smpte); + gst_collect_pads2_start (smpte->collect); - gst_collect_pads_add_pad (smpte->collect, smpte->sinkpad1, - sizeof (GstCollectData), NULL); - gst_collect_pads_add_pad (smpte->collect, smpte->sinkpad2, - sizeof (GstCollectData), NULL); + gst_collect_pads2_add_pad (smpte->collect, smpte->sinkpad1, + sizeof (GstCollectData2)); + gst_collect_pads2_add_pad (smpte->collect, smpte->sinkpad2, + sizeof (GstCollectData2)); smpte->fps = DEFAULT_PROP_FPS; smpte->type = DEFAULT_PROP_TYPE; @@ -441,7 +441,7 @@ gst_smpte_blend_i420 (guint8 * in1, guint8 * in2, guint8 * out, GstMask * mask, } static GstFlowReturn -gst_smpte_collected (GstCollectPads * pads, GstSMPTE * smpte) +gst_smpte_collected (GstCollectPads2 * pads, GstSMPTE * smpte) { GstBuffer *outbuf; GstClockTime ts; @@ -458,14 +458,14 @@ gst_smpte_collected (GstCollectPads * pads, GstSMPTE * smpte) smpte->fps_denom, smpte->fps_num); for (collected = pads->data; collected; collected = g_slist_next (collected)) { - GstCollectData *data; + GstCollectData2 *data; - data = (GstCollectData *) collected->data; + data = (GstCollectData2 *) collected->data; if (data->pad == smpte->sinkpad1) - in1 = gst_collect_pads_pop (pads, data); + in1 = gst_collect_pads2_pop (pads, data); else if (data->pad == smpte->sinkpad2) - in2 = gst_collect_pads_pop (pads, data); + in2 = gst_collect_pads2_pop (pads, data); } if (in1 == NULL) { @@ -623,11 +623,11 @@ gst_smpte_change_state (GstElement * element, GstStateChange transition) case GST_STATE_CHANGE_READY_TO_PAUSED: gst_smpte_reset (smpte); GST_LOG_OBJECT (smpte, "starting collectpads"); - gst_collect_pads_start (smpte->collect); + gst_collect_pads2_start (smpte->collect); break; case GST_STATE_CHANGE_PAUSED_TO_READY: GST_LOG_OBJECT (smpte, "stopping collectpads"); - gst_collect_pads_stop (smpte->collect); + gst_collect_pads2_stop (smpte->collect); break; default: break; diff --git a/gst/smpte/gstsmpte.h b/gst/smpte/gstsmpte.h index dc6bc6d8a..ac138f4e7 100644 --- a/gst/smpte/gstsmpte.h +++ b/gst/smpte/gstsmpte.h @@ -22,7 +22,7 @@ #define __GST_SMPTE_H__ #include <gst/gst.h> -#include <gst/base/gstcollectpads.h> +#include <gst/base/gstcollectpads2.h> G_BEGIN_DECLS @@ -49,7 +49,7 @@ struct _GstSMPTE { GstPad *srcpad, *sinkpad1, *sinkpad2; - GstCollectPads *collect; + GstCollectPads2 *collect; /* properties */ gint type; diff --git a/gst/udp/gstmultiudpsink.c b/gst/udp/gstmultiudpsink.c index a302465bd..970b4aef0 100644 --- a/gst/udp/gstmultiudpsink.c +++ b/gst/udp/gstmultiudpsink.c @@ -41,6 +41,8 @@ #include <errno.h> #include <string.h> +#include "gst/glib-compat-private.h" + GST_DEBUG_CATEGORY_STATIC (multiudpsink_debug); #define GST_CAT_DEFAULT (multiudpsink_debug) diff --git a/gst/udp/gstudpsrc.c b/gst/udp/gstudpsrc.c index 9169d6b13..d9d45e1d8 100644 --- a/gst/udp/gstudpsrc.c +++ b/gst/udp/gstudpsrc.c @@ -471,12 +471,21 @@ retry: IOCTL_SOCKET (udpsrc->sock.fd, FIONREAD, &readsize)) < 0)) goto ioctl_failed; - /* if we get here and there is nothing to read from the socket, the select got - * woken up by activity on the socket but it was not a read. We know someone - * will also do something with the socket so that we don't go into an infinite - * loop in the select(). */ + /* If we get here and the readsize is zero, then either select was woken up + * by activity that is not a read, or a poll error occurred, or a UDP packet + * was received that has no data. Since we cannot identify which case it is, + * we handle all of them. This could possibly lead to a UDP packet getting + * lost, but since UDP is not reliable, we can accept this. */ if (G_UNLIKELY (!readsize)) { + /* try to read a packet (and it will be ignored), + * in case a packet with no data arrived */ + slen = sizeof (sa); + recvfrom (udpsrc->sock.fd, (char *) &slen, 0, 0, &sa.sa, &slen); + + /* clear any error, in case a poll error occurred */ clear_error (udpsrc); + + /* poll again */ goto retry; } diff --git a/gst/videocrop/gstaspectratiocrop.c b/gst/videocrop/gstaspectratiocrop.c index eb0ece2bf..1be97fa79 100644 --- a/gst/videocrop/gstaspectratiocrop.c +++ b/gst/videocrop/gstaspectratiocrop.c @@ -43,6 +43,8 @@ #include "gstaspectratiocrop.h" +#include "gst/glib-compat-private.h" + GST_DEBUG_CATEGORY_STATIC (aspect_ratio_crop_debug); #define GST_CAT_DEFAULT aspect_ratio_crop_debug diff --git a/gst/videomixer/videomixer2.c b/gst/videomixer/videomixer2.c index e72031d5f..b8d419ae9 100644 --- a/gst/videomixer/videomixer2.c +++ b/gst/videomixer/videomixer2.c @@ -86,6 +86,10 @@ #include "config.h" #endif +/* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex + * with newer GLib versions (>= 2.31.0) */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + #include <string.h> #include "videomixer2.h" diff --git a/gst/wavparse/gstwavparse.c b/gst/wavparse/gstwavparse.c index e494b2fd4..1306a9a26 100644 --- a/gst/wavparse/gstwavparse.c +++ b/gst/wavparse/gstwavparse.c @@ -49,6 +49,11 @@ #ifdef HAVE_CONFIG_H #include "config.h" #endif + +/* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex + * with newer GLib versions (>= 2.31.0) */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + #include <string.h> #include <math.h> @@ -85,6 +90,19 @@ static void gst_wavparse_loop (GstPad * pad); static gboolean gst_wavparse_srcpad_event (GstPad * pad, GstObject * parent, GstEvent * event); +static void gst_wavparse_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_wavparse_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +#define DEFAULT_IGNORE_LENGTH FALSE + +enum +{ + PROP_0, + PROP_IGNORE_LENGTH, +}; + static GstStaticPadTemplate sink_template_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, @@ -113,6 +131,27 @@ gst_wavparse_class_init (GstWavParseClass * klass) object_class->dispose = gst_wavparse_dispose; + object_class->set_property = gst_wavparse_set_property; + object_class->get_property = gst_wavparse_get_property; + + /** + * GstWavParse:ignore-length + * + * This selects whether the length found in a data chunk + * should be ignored. This may be useful for streamed audio + * where the length is unknown until the end of streaming, + * and various software/hardware just puts some random value + * in there and hopes it doesn't break too much. + * + * Since: 0.10.36 + */ + g_object_class_install_property (object_class, PROP_IGNORE_LENGTH, + g_param_spec_boolean ("ignore-length", + "Ignore length", + "Ignore length from the Wave header", + DEFAULT_IGNORE_LENGTH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + gstelement_class->change_state = gst_wavparse_change_state; gstelement_class->send_event = gst_wavparse_send_event; @@ -212,37 +251,6 @@ gst_wavparse_init (GstWavParse * wavparse) gst_element_add_pad (GST_ELEMENT_CAST (wavparse), wavparse->srcpad); } -/* Compute (value * nom) % denom, avoiding overflow. This can be used - * to perform ceiling or rounding division together with - * gst_util_uint64_scale[_int]. */ -#define uint64_scale_modulo(val, nom, denom) \ - ((val % denom) * (nom % denom) % denom) - -/* Like gst_util_uint64_scale, but performs ceiling division. */ -static guint64 -uint64_ceiling_scale_int (guint64 val, gint num, gint denom) -{ - guint64 result = gst_util_uint64_scale_int (val, num, denom); - - if (uint64_scale_modulo (val, num, denom) == 0) - return result; - else - return result + 1; -} - -/* Like gst_util_uint64_scale, but performs ceiling division. */ -static guint64 -uint64_ceiling_scale (guint64 val, guint64 num, guint64 denom) -{ - guint64 result = gst_util_uint64_scale (val, num, denom); - - if (uint64_scale_modulo (val, num, denom) == 0) - return result; - else - return result + 1; -} - - /* FIXME: why is that not in use? */ #if 0 static void @@ -531,7 +539,7 @@ gst_wavparse_fmt (GstWavParse * wav) gst_element_add_pad (GST_ELEMENT_CAST (wav), wav->srcpad); gst_element_no_more_pads (GST_ELEMENT_CAST (wav)); - GST_DEBUG ("frequency %d, channels %d", wav->rate, wav->channels); + GST_DEBUG ("frequency %u, channels %u", wav->rate, wav->channels); return TRUE; @@ -572,8 +580,8 @@ gst_wavparse_other (GstWavParse * wav) GST_WARNING_OBJECT (wav, "could not peek head"); return FALSE; } - GST_DEBUG_OBJECT (wav, "got tag (%08x) %4.4s, length %d", tag, - (gchar *) & tag, length); + GST_DEBUG_OBJECT (wav, "got tag (%08x) %4.4s, length %u", tag, + (const gchar *) &tag, length); switch (tag) { case GST_RIFF_TAG_LIST: @@ -724,12 +732,12 @@ gst_wavparse_time_to_bytepos (GstWavParse * wav, gint64 ts, gint64 * bytepos) } if (wav->bps > 0) { - *bytepos = uint64_ceiling_scale (ts, (guint64) wav->bps, GST_SECOND); + *bytepos = gst_util_uint64_scale_ceil (ts, (guint64) wav->bps, GST_SECOND); return TRUE; } else if (wav->fact) { guint64 bps = gst_util_uint64_scale_int (wav->datasize, wav->rate, wav->fact); - *bytepos = uint64_ceiling_scale (ts, bps, GST_SECOND); + *bytepos = gst_util_uint64_scale_ceil (ts, bps, GST_SECOND); return TRUE; } @@ -1008,7 +1016,7 @@ gst_wavparse_peek_chunk_info (GstWavParse * wav, guint32 * tag, guint32 * size) *size = GST_READ_UINT32_LE (data + 4); gst_adapter_unmap (wav->adapter); - GST_DEBUG ("Next chunk size is %d bytes, type %" GST_FOURCC_FORMAT, *size, + GST_DEBUG ("Next chunk size is %u bytes, type %" GST_FOURCC_FORMAT, *size, GST_FOURCC_ARGS (*tag)); return TRUE; @@ -1038,7 +1046,7 @@ gst_wavparse_peek_chunk (GstWavParse * wav, guint32 * tag, guint32 * size) * so we throw poor man's exception, which can be caught if caller really * wants to handle 0 size chunk */ if (!(*size) || (*size) >= (1 << 30)) { - GST_INFO ("Invalid/unexpected chunk size %d for tag %" GST_FOURCC_FORMAT, + GST_INFO ("Invalid/unexpected chunk size %u for tag %" GST_FOURCC_FORMAT, *size, GST_FOURCC_ARGS (*tag)); /* chain should give up */ wav->abort_buffering = TRUE; @@ -1073,12 +1081,14 @@ gst_wavparse_calculate_duration (GstWavParse * wav) if (wav->bps > 0) { GST_INFO_OBJECT (wav, "Got datasize %" G_GUINT64_FORMAT, wav->datasize); wav->duration = - uint64_ceiling_scale (wav->datasize, GST_SECOND, (guint64) wav->bps); + gst_util_uint64_scale_ceil (wav->datasize, GST_SECOND, + (guint64) wav->bps); GST_INFO_OBJECT (wav, "Got duration (bps) %" GST_TIME_FORMAT, GST_TIME_ARGS (wav->duration)); return TRUE; } else if (wav->fact) { - wav->duration = uint64_ceiling_scale_int (GST_SECOND, wav->fact, wav->rate); + wav->duration = + gst_util_uint64_scale_int_ceil (GST_SECOND, wav->fact, wav->rate); GST_INFO_OBJECT (wav, "Got duration (fact) %" GST_TIME_FORMAT, GST_TIME_ARGS (wav->duration)); return TRUE; @@ -1211,12 +1221,12 @@ gst_wavparse_stream_headers (GstWavParse * wav) { /* Note: workaround for mp2/mp3 embedded in wav, that relies on the * bitrate inside the mpeg stream */ - GST_INFO ("resetting bps from %d to 0 for mp2/3", wav->av_bps); + GST_INFO ("resetting bps from %u to 0 for mp2/3", wav->av_bps); wav->bps = 0; break; } case GST_RIFF_WAVE_FORMAT_PCM: - if (wav->blockalign > wav->channels * (guint) ceil (wav->depth / 8.0)) + if (wav->blockalign > wav->channels * ((wav->depth + 7) / 8)) goto invalid_blockalign; /* fall through */ default: @@ -1298,7 +1308,11 @@ gst_wavparse_stream_headers (GstWavParse * wav) */ switch (tag) { case GST_RIFF_TAG_data:{ - GST_DEBUG_OBJECT (wav, "Got 'data' TAG, size : %d", size); + GST_DEBUG_OBJECT (wav, "Got 'data' TAG, size : %u", size); + if (wav->ignore_length) { + GST_DEBUG_OBJECT (wav, "Ignoring length"); + size = 0; + } if (wav->streaming) { gst_adapter_flush (wav->adapter, 8); gotdata = TRUE; @@ -1323,7 +1337,7 @@ gst_wavparse_stream_headers (GstWavParse * wav) /* We will continue parsing tags 'till end */ wav->offset += size; } - GST_DEBUG_OBJECT (wav, "datasize = %d", size); + GST_DEBUG_OBJECT (wav, "datasize = %u", size); break; } case GST_RIFF_TAG_fact:{ @@ -1337,7 +1351,7 @@ gst_wavparse_stream_headers (GstWavParse * wav) /* need more data */ goto exit; } - GST_DEBUG_OBJECT (wav, "need %d, available %d; ignoring chunk", + GST_DEBUG_OBJECT (wav, "need %u, available %u; ignoring chunk", data_size, size); break; } @@ -1385,7 +1399,7 @@ gst_wavparse_stream_headers (GstWavParse * wav) /* need more data */ goto exit; } - GST_DEBUG_OBJECT (wav, "need %d, available %d; ignoring chunk", + GST_DEBUG_OBJECT (wav, "need %u, available %u; ignoring chunk", data_size, size); break; } @@ -1520,18 +1534,20 @@ gst_wavparse_stream_headers (GstWavParse * wav) wav->bps = (guint32) gst_util_uint64_scale ((guint64) wav->rate, wav->datasize, (guint64) wav->fact); - GST_INFO_OBJECT (wav, "calculated bps : %d, enabling VBR", wav->bps); + GST_INFO_OBJECT (wav, "calculated bps : %u, enabling VBR", wav->bps); #endif wav->vbr = TRUE; } if (gst_wavparse_calculate_duration (wav)) { gst_segment_init (&wav->segment, GST_FORMAT_TIME); - wav->segment.duration = wav->duration; + if (!wav->ignore_length) + wav->segment.duration = wav->duration; } else { /* no bitrate, let downstream peer do the math, we'll feed it bytes. */ gst_segment_init (&wav->segment, GST_FORMAT_BYTES); - wav->segment.duration = wav->datasize; + if (!wav->ignore_length) + wav->segment.duration = wav->datasize; } /* now we have all the info to perform a pending seek if any, if no @@ -1558,7 +1574,7 @@ gst_wavparse_stream_headers (GstWavParse * wav) if (wav->blockalign > 0) wav->max_buf_size -= (wav->max_buf_size % wav->blockalign); - GST_DEBUG_OBJECT (wav, "max buffer size %d", wav->max_buf_size); + GST_DEBUG_OBJECT (wav, "max buffer size %u", wav->max_buf_size); return GST_FLOW_OK; @@ -1607,7 +1623,7 @@ invalid_blockalign: { GST_ELEMENT_ERROR (wav, STREAM, FAILED, (NULL), ("Stream claims blockalign = %u, which is more than %u - invalid data", - wav->blockalign, wav->channels * (guint) ceil (wav->depth / 8.0))); + wav->blockalign, wav->channels * ((wav->depth + 7) / 8))); goto fail; } invalid_bps: @@ -1626,7 +1642,7 @@ no_bytes_per_sample: unknown_format: { GST_ELEMENT_ERROR (wav, STREAM, TYPE_NOT_FOUND, (NULL), - ("No caps found for format 0x%x, %d channels, %d Hz", + ("No caps found for format 0x%x, %u channels, %u Hz", wav->format, wav->channels, wav->rate)); goto fail; } @@ -1818,13 +1834,13 @@ iterate_adapter: if (G_UNLIKELY (extra)) { extra = wav->bytes_per_sample - extra; if (extra <= avail) { - GST_DEBUG_OBJECT (wav, "flushing %d bytes to sample boundary", extra); + GST_DEBUG_OBJECT (wav, "flushing %u bytes to sample boundary", extra); gst_adapter_flush (wav->adapter, extra); wav->offset += extra; wav->dataleft -= extra; goto iterate_adapter; } else { - GST_DEBUG_OBJECT (wav, "flushing %d bytes", avail); + GST_DEBUG_OBJECT (wav, "flushing %u bytes", avail); gst_adapter_clear (wav->adapter); wav->offset += avail; wav->dataleft -= avail; @@ -1833,7 +1849,7 @@ iterate_adapter: } if (avail < desired) { - GST_LOG_OBJECT (wav, "Got only %d bytes of data from the sinkpad", avail); + GST_LOG_OBJECT (wav, "Got only %u bytes of data from the sinkpad", avail); return GST_FLOW_OK; } @@ -1885,9 +1901,10 @@ iterate_adapter: if (wav->bps > 0) { /* and timestamps if we have a bitrate, be careful for overflows */ - timestamp = uint64_ceiling_scale (pos, GST_SECOND, (guint64) wav->bps); + timestamp = + gst_util_uint64_scale_ceil (pos, GST_SECOND, (guint64) wav->bps); next_timestamp = - uint64_ceiling_scale (nextpos, GST_SECOND, (guint64) wav->bps); + gst_util_uint64_scale_ceil (nextpos, GST_SECOND, (guint64) wav->bps); duration = next_timestamp - timestamp; /* update current running segment position */ @@ -1897,8 +1914,8 @@ iterate_adapter: guint64 bps = gst_util_uint64_scale_int (wav->datasize, wav->rate, wav->fact); /* and timestamps if we have a bitrate, be careful for overflows */ - timestamp = uint64_ceiling_scale (pos, GST_SECOND, bps); - next_timestamp = uint64_ceiling_scale (nextpos, GST_SECOND, bps); + timestamp = gst_util_uint64_scale_ceil (pos, GST_SECOND, bps); + next_timestamp = gst_util_uint64_scale_ceil (nextpos, GST_SECOND, bps); duration = next_timestamp - timestamp; } else { /* no bitrate, all we know is that the first sample has timestamp 0, all @@ -2191,10 +2208,12 @@ gst_wavparse_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) if (bps) { if (start >= 0) start = - uint64_ceiling_scale (start, GST_SECOND, (guint64) wav->bps); + gst_util_uint64_scale_ceil (start, GST_SECOND, + (guint64) wav->bps); if (stop >= 0) stop = - uint64_ceiling_scale (stop, GST_SECOND, (guint64) wav->bps); + gst_util_uint64_scale_ceil (stop, GST_SECOND, + (guint64) wav->bps); } } } else { @@ -2319,13 +2338,14 @@ gst_wavparse_pad_convert (GstPad * pad, "src=%" G_GINT64_FORMAT ", offset=%" G_GINT64_FORMAT, src_value, wavparse->offset); if (wavparse->bps > 0) - *dest_value = uint64_ceiling_scale (src_value, GST_SECOND, + *dest_value = gst_util_uint64_scale_ceil (src_value, GST_SECOND, (guint64) wavparse->bps); else if (wavparse->fact) { - guint64 bps = uint64_ceiling_scale_int (wavparse->datasize, + guint64 bps = gst_util_uint64_scale_int_ceil (wavparse->datasize, wavparse->rate, wavparse->fact); - *dest_value = uint64_ceiling_scale_int (src_value, GST_SECOND, bps); + *dest_value = + gst_util_uint64_scale_int_ceil (src_value, GST_SECOND, bps); } else { res = FALSE; } @@ -2438,6 +2458,11 @@ gst_wavparse_pad_query (GstPad * pad, GstObject * parent, GstQuery * query) gint64 duration = 0; GstFormat format; + if (wav->ignore_length) { + res = FALSE; + break; + } + gst_query_parse_duration (query, &format, NULL); switch (format) { @@ -2626,6 +2651,43 @@ gst_wavparse_change_state (GstElement * element, GstStateChange transition) return ret; } +static void +gst_wavparse_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstWavParse *self; + + g_return_if_fail (GST_IS_WAVPARSE (object)); + self = GST_WAVPARSE (object); + + switch (prop_id) { + case PROP_IGNORE_LENGTH: + self->ignore_length = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); + } + +} + +static void +gst_wavparse_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstWavParse *self; + + g_return_if_fail (GST_IS_WAVPARSE (object)); + self = GST_WAVPARSE (object); + + switch (prop_id) { + case PROP_IGNORE_LENGTH: + g_value_set_boolean (value, self->ignore_length); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); + } +} + static gboolean plugin_init (GstPlugin * plugin) { diff --git a/gst/wavparse/gstwavparse.h b/gst/wavparse/gstwavparse.h index 54d0a0fd5..82838e4cb 100644 --- a/gst/wavparse/gstwavparse.h +++ b/gst/wavparse/gstwavparse.h @@ -116,6 +116,8 @@ struct _GstWavParse { gboolean first; /* discont after seek */ gboolean discont; + + gboolean ignore_length; }; struct _GstWavParseClass { diff --git a/po/LINGUAS b/po/LINGUAS index 44f55d97e..73d8b85b2 100644 --- a/po/LINGUAS +++ b/po/LINGUAS @@ -1 +1 @@ -af az bg ca cs da de el en_GB es eu fi fr gl hu id it ja lt lv mt nb nl or pl pt_BR ro ru sk sl sq sr sv tr uk vi zh_CN zh_HK zh_TW +af az bg ca cs da de el en_GB eo es eu fi fr gl hu id it ja lt lv mt nb nl or pl pt_BR ro ru sk sl sq sr sv tr uk vi zh_CN zh_HK zh_TW diff --git a/po/eo.po b/po/eo.po new file mode 100644 index 000000000..73f84a393 --- /dev/null +++ b/po/eo.po @@ -0,0 +1,832 @@ +# Esperanto translation for gst-plugins-good. +# Copyright (C) 2011 Free Software Foundation, Inc. +# This file is distributed under the same license as the gst-plugins-good package. +# Kristjan SCHMIDT <kristjan.schmidt@googlemail.com>, 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: gst-plugins-good 0.10.28.2\n" +"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/\n" +"POT-Creation-Date: 2011-04-16 16:25+0100\n" +"PO-Revision-Date: 2011-06-04 21:48+0100\n" +"Last-Translator: Kristjan SCHMIDT <kristjan.schmidt@googlemail.com>\n" +"Language-Team: Esperanto <translation-team-eo@lists.sourceforge.net>\n" +"Language: eo\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: ext/esd/esdsink.c:253 ext/esd/esdsink.c:358 +msgid "Could not establish connection to sound server" +msgstr "" + +#: ext/esd/esdsink.c:260 +msgid "Failed to query sound server capabilities" +msgstr "" + +#. TRANSLATORS: 'song title' by 'artist name' +#: ext/pulse/pulsesink.c:2686 +#, c-format +msgid "'%s' by '%s'" +msgstr "'%s' de '%s'" + +#: ext/flac/gstflacdec.c:1117 ext/libpng/gstpngdec.c:349 +#: ext/libpng/gstpngdec.c:360 ext/libpng/gstpngdec.c:559 +#: ext/wavpack/gstwavpackparse.c:1170 gst/avi/gstavidemux.c:5203 +msgid "Internal data stream error." +msgstr "Interna datumflu-eraro." + +#: ext/jpeg/gstjpegdec.c:286 +msgid "Failed to decode JPEG image" +msgstr "" + +#: ext/shout2/gstshout2.c:578 +msgid "Could not connect to server" +msgstr "Ne eblis konekti al servilo" + +#: ext/soup/gstsouphttpsrc.c:871 +msgid "Server does not support seeking." +msgstr "" + +#: ext/soup/gstsouphttpsrc.c:1078 +msgid "Could not resolve server name." +msgstr "" + +#: ext/soup/gstsouphttpsrc.c:1084 +msgid "Could not establish connection to server." +msgstr "" + +#: ext/soup/gstsouphttpsrc.c:1089 +msgid "Secure connection setup failed." +msgstr "" + +#: ext/soup/gstsouphttpsrc.c:1094 +msgid "A network error occured, or the server closed the connection unexpectedly." +msgstr "" + +#: ext/soup/gstsouphttpsrc.c:1100 +msgid "Server sent bad data." +msgstr "" + +#: ext/soup/gstsouphttpsrc.c:1249 +msgid "No URL set." +msgstr "" + +#: gst/avi/gstavimux.c:1810 +msgid "No or invalid input audio, AVI stream will be corrupt." +msgstr "" + +#: gst/quicktime/qtdemux.c:519 gst/quicktime/qtdemux.c:523 +msgid "This file contains no playable streams." +msgstr "" + +#: gst/quicktime/qtdemux.c:558 gst/quicktime/qtdemux.c:4009 +#: gst/quicktime/qtdemux.c:4071 gst/quicktime/qtdemux.c:4219 +msgid "This file is invalid and cannot be played." +msgstr "" + +#: gst/quicktime/qtdemux.c:2439 gst/quicktime/qtdemux.c:2515 +#: gst/quicktime/qtdemux.c:2558 gst/quicktime/qtdemux.c:4798 +#: gst/quicktime/qtdemux.c:4805 gst/quicktime/qtdemux.c:5391 +#: gst/quicktime/qtdemux.c:5817 gst/quicktime/qtdemux.c:5824 +#: gst/quicktime/qtdemux.c:7305 +msgid "This file is corrupt and cannot be played." +msgstr "" + +#: gst/quicktime/qtdemux.c:2647 +msgid "Invalid atom size." +msgstr "" + +#: gst/quicktime/qtdemux.c:2716 +msgid "This file is incomplete and cannot be played." +msgstr "" + +#: gst/quicktime/qtdemux.c:4994 +msgid "The video in this file might not play correctly." +msgstr "" + +#: gst/quicktime/qtdemux.c:7334 +#, c-format +msgid "This file contains too many streams. Only playing first %d" +msgstr "" + +#: gst/rtsp/gstrtspsrc.c:5187 +msgid "No supported stream was found. You might need to install a GStreamer RTSP extension plugin for Real media streams." +msgstr "" + +#: gst/rtsp/gstrtspsrc.c:5192 +msgid "No supported stream was found. You might need to allow more transport protocols or may otherwise be missing the right GStreamer RTSP extension plugin." +msgstr "" + +#: gst/wavparse/gstwavparse.c:2103 +msgid "Internal data flow error." +msgstr "" + +#: sys/oss/gstossmixertrack.c:98 sys/oss4/oss4-mixer.c:722 +#: sys/sunaudio/gstsunaudiomixertrack.c:69 +msgid "Volume" +msgstr "Laŭteco" + +#: sys/oss/gstossmixertrack.c:99 sys/oss4/oss4-mixer.c:735 +msgid "Bass" +msgstr "Baso" + +#: sys/oss/gstossmixertrack.c:100 sys/oss4/oss4-mixer.c:736 +msgid "Treble" +msgstr "" + +#: sys/oss/gstossmixertrack.c:101 +msgid "Synth" +msgstr "Sintezilo" + +#: sys/oss/gstossmixertrack.c:102 sys/oss4/oss4-mixer.c:750 +msgid "PCM" +msgstr "" + +#: sys/oss/gstossmixertrack.c:103 +msgid "Speaker" +msgstr "Parolilo" + +#: sys/oss/gstossmixertrack.c:104 +msgid "Line-in" +msgstr "" + +#: sys/oss/gstossmixertrack.c:105 sys/oss4/oss4-mixer.c:741 +msgid "Microphone" +msgstr "Mikrofono" + +#: sys/oss/gstossmixertrack.c:106 +msgid "CD" +msgstr "KD" + +#: sys/oss/gstossmixertrack.c:107 +msgid "Mixer" +msgstr "Miksilo" + +#: sys/oss/gstossmixertrack.c:108 +msgid "PCM-2" +msgstr "" + +#: sys/oss/gstossmixertrack.c:109 +msgid "Record" +msgstr "Registri" + +#: sys/oss/gstossmixertrack.c:110 +msgid "In-gain" +msgstr "" + +#: sys/oss/gstossmixertrack.c:111 +msgid "Out-gain" +msgstr "" + +#: sys/oss/gstossmixertrack.c:112 +msgid "Line-1" +msgstr "" + +#: sys/oss/gstossmixertrack.c:113 +msgid "Line-2" +msgstr "" + +#: sys/oss/gstossmixertrack.c:114 +msgid "Line-3" +msgstr "" + +#: sys/oss/gstossmixertrack.c:115 +msgid "Digital-1" +msgstr "" + +#: sys/oss/gstossmixertrack.c:116 +msgid "Digital-2" +msgstr "" + +#: sys/oss/gstossmixertrack.c:117 +msgid "Digital-3" +msgstr "" + +#: sys/oss/gstossmixertrack.c:118 +msgid "Phone-in" +msgstr "" + +#: sys/oss/gstossmixertrack.c:119 +msgid "Phone-out" +msgstr "" + +#: sys/oss/gstossmixertrack.c:120 +msgid "Video" +msgstr "Video" + +#: sys/oss/gstossmixertrack.c:121 +msgid "Radio" +msgstr "Radio" + +#: sys/oss/gstossmixertrack.c:122 sys/oss4/oss4-mixer.c:764 +#: sys/sunaudio/gstsunaudiomixertrack.c:71 +msgid "Monitor" +msgstr "Ekrano" + +#: sys/oss/gstosssink.c:399 sys/oss4/oss4-sink.c:494 +#: sys/oss4/oss4-source.c:361 +msgid "Could not open audio device for playback. Device is being used by another application." +msgstr "Ne eblis malfermi la sonaparaton por reproduktado. Ĝi estas uzate de alia aplikaĵo." + +#: sys/oss/gstosssink.c:406 sys/oss4/oss4-sink.c:504 +#: sys/oss4/oss4-source.c:371 +msgid "Could not open audio device for playback. You don't have permission to open the device." +msgstr "" + +#: sys/oss/gstosssink.c:414 sys/oss4/oss4-sink.c:515 +#: sys/oss4/oss4-source.c:382 +msgid "Could not open audio device for playback." +msgstr "Ne eblis malfermi la sonaparaton por reproduktado." + +#: sys/oss/gstosssrc.c:370 +msgid "Could not open audio device for recording. You don't have permission to open the device." +msgstr "" + +#: sys/oss/gstosssrc.c:378 +msgid "Could not open audio device for recording." +msgstr "Ne eblis malfermi sonaparaton por registrado." + +#: sys/oss4/oss4-mixer.c:302 +msgid "Could not open audio device for mixer control handling." +msgstr "" + +#: sys/oss4/oss4-mixer.c:316 +msgid "Could not open audio device for mixer control handling. This version of the Open Sound System is not supported by this element." +msgstr "" + +#: sys/oss4/oss4-mixer.c:723 +msgid "Master" +msgstr "Ĉefe" + +#: sys/oss4/oss4-mixer.c:724 +msgid "Front" +msgstr "Antaŭe" + +#: sys/oss4/oss4-mixer.c:725 +msgid "Rear" +msgstr "Malantaŭe" + +#: sys/oss4/oss4-mixer.c:726 +msgid "Headphones" +msgstr "Kaptelefono" + +#: sys/oss4/oss4-mixer.c:727 +msgid "Center" +msgstr "Centre" + +#: sys/oss4/oss4-mixer.c:728 +msgid "LFE" +msgstr "" + +#: sys/oss4/oss4-mixer.c:729 +msgid "Surround" +msgstr "Ĉirkaŭe" + +#: sys/oss4/oss4-mixer.c:730 +msgid "Side" +msgstr "Flanke" + +#: sys/oss4/oss4-mixer.c:731 sys/sunaudio/gstsunaudiomixertrack.c:72 +msgid "Built-in Speaker" +msgstr "" + +#: sys/oss4/oss4-mixer.c:732 sys/sunaudio/gstsunaudiomixertrack.c:76 +msgid "AUX 1 Out" +msgstr "" + +#: sys/oss4/oss4-mixer.c:733 sys/sunaudio/gstsunaudiomixertrack.c:77 +msgid "AUX 2 Out" +msgstr "" + +#: sys/oss4/oss4-mixer.c:734 +msgid "AUX Out" +msgstr "" + +#: sys/oss4/oss4-mixer.c:737 +msgid "3D Depth" +msgstr "" + +#: sys/oss4/oss4-mixer.c:738 +msgid "3D Center" +msgstr "" + +#: sys/oss4/oss4-mixer.c:739 +msgid "3D Enhance" +msgstr "" + +#: sys/oss4/oss4-mixer.c:740 +msgid "Telephone" +msgstr "" + +#: sys/oss4/oss4-mixer.c:742 sys/sunaudio/gstsunaudiomixertrack.c:74 +msgid "Line Out" +msgstr "" + +#: sys/oss4/oss4-mixer.c:743 sys/oss4/oss4-mixer.c:744 +msgid "Line In" +msgstr "" + +#: sys/oss4/oss4-mixer.c:745 +msgid "Internal CD" +msgstr "" + +#: sys/oss4/oss4-mixer.c:746 +msgid "Video In" +msgstr "" + +#: sys/oss4/oss4-mixer.c:747 +msgid "AUX 1 In" +msgstr "" + +#: sys/oss4/oss4-mixer.c:748 +msgid "AUX 2 In" +msgstr "" + +#: sys/oss4/oss4-mixer.c:749 +msgid "AUX In" +msgstr "" + +#: sys/oss4/oss4-mixer.c:751 sys/oss4/oss4-mixer.c:752 +msgid "Record Gain" +msgstr "" + +#: sys/oss4/oss4-mixer.c:753 +msgid "Output Gain" +msgstr "" + +#: sys/oss4/oss4-mixer.c:754 +msgid "Microphone Boost" +msgstr "" + +#: sys/oss4/oss4-mixer.c:755 +msgid "Loopback" +msgstr "" + +#: sys/oss4/oss4-mixer.c:756 +msgid "Diagnostic" +msgstr "" + +#: sys/oss4/oss4-mixer.c:757 +msgid "Bass Boost" +msgstr "" + +#: sys/oss4/oss4-mixer.c:758 +msgid "Playback Ports" +msgstr "" + +#: sys/oss4/oss4-mixer.c:759 +msgid "Input" +msgstr "Enigo" + +#: sys/oss4/oss4-mixer.c:760 sys/oss4/oss4-mixer.c:761 +msgid "Record Source" +msgstr "" + +#: sys/oss4/oss4-mixer.c:762 +msgid "Monitor Source" +msgstr "" + +#: sys/oss4/oss4-mixer.c:763 +msgid "Keyboard Beep" +msgstr "" + +#: sys/oss4/oss4-mixer.c:765 +msgid "Simulate Stereo" +msgstr "" + +#: sys/oss4/oss4-mixer.c:766 sys/oss4/oss4-mixer.c:786 +msgid "Stereo" +msgstr "Dukanale" + +#: sys/oss4/oss4-mixer.c:767 +msgid "Surround Sound" +msgstr "Ĉirkaŭa sono" + +#: sys/oss4/oss4-mixer.c:768 +msgid "Microphone Gain" +msgstr "" + +#: sys/oss4/oss4-mixer.c:769 +msgid "Speaker Source" +msgstr "" + +#: sys/oss4/oss4-mixer.c:770 +msgid "Microphone Source" +msgstr "" + +#: sys/oss4/oss4-mixer.c:771 +msgid "Jack" +msgstr "" + +#: sys/oss4/oss4-mixer.c:772 +msgid "Center / LFE" +msgstr "" + +#: sys/oss4/oss4-mixer.c:773 +msgid "Stereo Mix" +msgstr "" + +#: sys/oss4/oss4-mixer.c:774 +msgid "Mono Mix" +msgstr "" + +#: sys/oss4/oss4-mixer.c:775 +msgid "Input Mix" +msgstr "" + +#: sys/oss4/oss4-mixer.c:776 +msgid "SPDIF In" +msgstr "" + +#: sys/oss4/oss4-mixer.c:777 sys/sunaudio/gstsunaudiomixertrack.c:75 +msgid "SPDIF Out" +msgstr "" + +#: sys/oss4/oss4-mixer.c:778 +msgid "Microphone 1" +msgstr "Mikrofono 1" + +#: sys/oss4/oss4-mixer.c:779 +msgid "Microphone 2" +msgstr "Mikrofono 2" + +#: sys/oss4/oss4-mixer.c:780 +msgid "Digital Out" +msgstr "" + +#: sys/oss4/oss4-mixer.c:781 +msgid "Digital In" +msgstr "" + +#: sys/oss4/oss4-mixer.c:782 +msgid "HDMI" +msgstr "" + +#: sys/oss4/oss4-mixer.c:783 +msgid "Modem" +msgstr "Modemo" + +#: sys/oss4/oss4-mixer.c:784 +msgid "Handset" +msgstr "" + +#: sys/oss4/oss4-mixer.c:785 +msgid "Other" +msgstr "Alia" + +#: sys/oss4/oss4-mixer.c:787 +msgid "None" +msgstr "Neniu" + +#: sys/oss4/oss4-mixer.c:788 +msgid "On" +msgstr "Enŝaltite" + +#: sys/oss4/oss4-mixer.c:789 +msgid "Off" +msgstr "Elŝaltite" + +#: sys/oss4/oss4-mixer.c:790 +msgid "Mute" +msgstr "Silentigi" + +#: sys/oss4/oss4-mixer.c:791 +msgid "Fast" +msgstr "Rapide" + +#. TRANSLATORS: "Very Low" is a quality setting here +#: sys/oss4/oss4-mixer.c:793 +msgid "Very Low" +msgstr "Tre malalte" + +#. TRANSLATORS: "Low" is a quality setting here +#: sys/oss4/oss4-mixer.c:795 +msgid "Low" +msgstr "Malalte" + +#. TRANSLATORS: "Medium" is a quality setting here +#: sys/oss4/oss4-mixer.c:797 +msgid "Medium" +msgstr "Meze" + +#. TRANSLATORS: "High" is a quality setting here +#: sys/oss4/oss4-mixer.c:799 +msgid "High" +msgstr "Alte" + +#. TRANSLATORS: "Very High" is a quality setting here +#: sys/oss4/oss4-mixer.c:801 sys/oss4/oss4-mixer.c:802 +msgid "Very High" +msgstr "Tre alte" + +#. TRANSLATORS: "Production" is a quality setting here +#: sys/oss4/oss4-mixer.c:804 +msgid "Production" +msgstr "" + +#: sys/oss4/oss4-mixer.c:805 +msgid "Front Panel Microphone" +msgstr "" + +#: sys/oss4/oss4-mixer.c:806 +msgid "Front Panel Line In" +msgstr "" + +#: sys/oss4/oss4-mixer.c:807 +msgid "Front Panel Headphones" +msgstr "" + +#: sys/oss4/oss4-mixer.c:808 +msgid "Front Panel Line Out" +msgstr "" + +#: sys/oss4/oss4-mixer.c:809 +msgid "Green Connector" +msgstr "" + +#: sys/oss4/oss4-mixer.c:810 +msgid "Pink Connector" +msgstr "" + +#: sys/oss4/oss4-mixer.c:811 +msgid "Blue Connector" +msgstr "" + +#: sys/oss4/oss4-mixer.c:812 +msgid "White Connector" +msgstr "" + +#: sys/oss4/oss4-mixer.c:813 +msgid "Black Connector" +msgstr "" + +#: sys/oss4/oss4-mixer.c:814 +msgid "Gray Connector" +msgstr "" + +#: sys/oss4/oss4-mixer.c:815 +msgid "Orange Connector" +msgstr "" + +#: sys/oss4/oss4-mixer.c:816 +msgid "Red Connector" +msgstr "" + +#: sys/oss4/oss4-mixer.c:817 +msgid "Yellow Connector" +msgstr "" + +#: sys/oss4/oss4-mixer.c:818 +msgid "Green Front Panel Connector" +msgstr "" + +#: sys/oss4/oss4-mixer.c:819 +msgid "Pink Front Panel Connector" +msgstr "" + +#: sys/oss4/oss4-mixer.c:820 +msgid "Blue Front Panel Connector" +msgstr "" + +#: sys/oss4/oss4-mixer.c:821 +msgid "White Front Panel Connector" +msgstr "" + +#: sys/oss4/oss4-mixer.c:822 +msgid "Black Front Panel Connector" +msgstr "" + +#: sys/oss4/oss4-mixer.c:823 +msgid "Gray Front Panel Connector" +msgstr "" + +#: sys/oss4/oss4-mixer.c:824 +msgid "Orange Front Panel Connector" +msgstr "" + +#: sys/oss4/oss4-mixer.c:825 +msgid "Red Front Panel Connector" +msgstr "" + +#: sys/oss4/oss4-mixer.c:826 +msgid "Yellow Front Panel Connector" +msgstr "" + +#: sys/oss4/oss4-mixer.c:827 +msgid "Spread Output" +msgstr "" + +#: sys/oss4/oss4-mixer.c:828 +msgid "Downmix" +msgstr "" + +#: sys/oss4/oss4-mixer.c:872 +msgid "Virtual Mixer Input" +msgstr "" + +#: sys/oss4/oss4-mixer.c:874 +msgid "Virtual Mixer Output" +msgstr "" + +#: sys/oss4/oss4-mixer.c:876 +msgid "Virtual Mixer Channels" +msgstr "" + +#. TRANSLATORS: name + number of a volume mixer control +#: sys/oss4/oss4-mixer.c:927 +#, c-format +msgid "%s %d Function" +msgstr "%s %d funkcio" + +#. TRANSLATORS: name of a volume mixer control +#: sys/oss4/oss4-mixer.c:934 +#, c-format +msgid "%s Function" +msgstr "%s funcio" + +#: sys/oss4/oss4-sink.c:524 sys/oss4/oss4-source.c:392 +msgid "Could not open audio device for playback. This version of the Open Sound System is not supported by this element." +msgstr "" + +#: sys/oss4/oss4-sink.c:640 +msgid "Playback is not supported by this audio device." +msgstr "" + +#: sys/oss4/oss4-sink.c:647 +msgid "Audio playback error." +msgstr "" + +#: sys/oss4/oss4-source.c:514 +msgid "Recording is not supported by this audio device." +msgstr "" + +#: sys/oss4/oss4-source.c:521 +msgid "Error recording from audio device." +msgstr "" + +#: sys/sunaudio/gstsunaudiomixertrack.c:70 +msgid "Gain" +msgstr "" + +#: sys/sunaudio/gstsunaudiomixertrack.c:73 +msgid "Headphone" +msgstr "" + +#: sys/v4l2/gstv4l2src.c:887 +#, c-format +msgid "Error reading %d bytes from device '%s'." +msgstr "" + +#: sys/v4l2/gstv4l2src.c:913 +#, c-format +msgid "Got unexpected frame size of %u instead of %u." +msgstr "" + +#: sys/v4l2/gstv4l2src.c:931 +#, c-format +msgid "Error reading %d bytes on device '%s'." +msgstr "" + +#: sys/v4l2/v4l2_calls.c:99 +#, c-format +msgid "Error getting capabilities for device '%s': It isn't a v4l2 driver. Check if it is a v4l1 driver." +msgstr "" + +#: sys/v4l2/v4l2_calls.c:139 +#, c-format +msgid "Failed to query attributes of input %d in device %s" +msgstr "" + +#: sys/v4l2/v4l2_calls.c:169 +#, c-format +msgid "Failed to get setting of tuner %d on device '%s'." +msgstr "" + +#: sys/v4l2/v4l2_calls.c:213 +#, c-format +msgid "Failed to query norm on device '%s'." +msgstr "" + +#: sys/v4l2/v4l2_calls.c:264 sys/v4l2/v4l2_calls.c:347 +#, c-format +msgid "Failed getting controls attributes on device '%s'." +msgstr "" + +#: sys/v4l2/v4l2_calls.c:492 +#, c-format +msgid "Cannot identify device '%s'." +msgstr "" + +#: sys/v4l2/v4l2_calls.c:499 +#, c-format +msgid "This isn't a device '%s'." +msgstr "Tio ne estas '%s'-aparato." + +#: sys/v4l2/v4l2_calls.c:506 +#, c-format +msgid "Could not open device '%s' for reading and writing." +msgstr "" + +#: sys/v4l2/v4l2_calls.c:513 +#, c-format +msgid "Device '%s' is not a capture device." +msgstr "" + +#: sys/v4l2/v4l2_calls.c:522 +#, c-format +msgid "Device '%s' is not a output device." +msgstr "" + +#: sys/v4l2/v4l2_calls.c:622 +#, c-format +msgid "Failed to set norm for device '%s'." +msgstr "" + +#: sys/v4l2/v4l2_calls.c:660 +#, c-format +msgid "Failed to get current tuner frequency for device '%s'." +msgstr "" + +#: sys/v4l2/v4l2_calls.c:702 +#, c-format +msgid "Failed to set current tuner frequency for device '%s' to %lu Hz." +msgstr "" + +#: sys/v4l2/v4l2_calls.c:736 +#, c-format +msgid "Failed to get signal strength for device '%s'." +msgstr "" + +#: sys/v4l2/v4l2_calls.c:772 +#, c-format +msgid "Failed to get value for control %d on device '%s'." +msgstr "" + +#: sys/v4l2/v4l2_calls.c:807 +#, c-format +msgid "Failed to set value %d for control %d on device '%s'." +msgstr "" + +#: sys/v4l2/v4l2_calls.c:839 +#, c-format +msgid "Failed to get current input on device '%s'. May be it is a radio device" +msgstr "" + +#: sys/v4l2/v4l2_calls.c:864 +#, c-format +msgid "Failed to set input %d on device %s." +msgstr "" + +#: sys/v4l2/v4l2_calls.c:896 +#, c-format +msgid "Failed to get current output on device '%s'. May be it is a radio device" +msgstr "" + +#: sys/v4l2/v4l2_calls.c:921 +#, c-format +msgid "Failed to set output %d on device %s." +msgstr "" + +#: sys/v4l2/v4l2src_calls.c:81 +#, c-format +msgid "Could not enqueue buffers in device '%s'." +msgstr "" + +#: sys/v4l2/v4l2src_calls.c:197 +#, c-format +msgid "Failed trying to get video frames from device '%s'." +msgstr "" + +#: sys/v4l2/v4l2src_calls.c:199 +#, c-format +msgid "Failed after %d tries. device %s. system error: %s" +msgstr "" + +#: sys/v4l2/v4l2src_calls.c:239 +#, c-format +msgid "Could not get parameters on device '%s'" +msgstr "" + +#: sys/v4l2/v4l2src_calls.c:267 +msgid "Video input device did not accept new frame rate setting." +msgstr "" + +#: sys/v4l2/v4l2src_calls.c:339 +#, c-format +msgid "Could not map buffers from device '%s'" +msgstr "" + +#: sys/v4l2/v4l2src_calls.c:347 +#, c-format +msgid "The driver of device '%s' does not support any known capture method." +msgstr "" + +#: sys/ximage/gstximagesrc.c:719 +msgid "Changing resolution at runtime is not yet supported." +msgstr "" + +#: sys/ximage/gstximagesrc.c:733 +msgid "Cannot operate without a clock" +msgstr "" diff --git a/sys/Makefile.am b/sys/Makefile.am index ab08d046d..7ccd9e77a 100644 --- a/sys/Makefile.am +++ b/sys/Makefile.am @@ -23,6 +23,12 @@ else DIRECTSOUND_DIR= endif +if USE_WAVEFORM +WAVEFORM_DIR=waveform +else +WAVEFORM_DIR= +endif + if USE_SUNAUDIO SUNAUDIO_DIR=sunaudio else @@ -77,7 +83,7 @@ else XIMAGE_DIR= endif -SUBDIRS=$(DIRECTSOUND_DIR) $(OSS_DIR) $(OSS4_DIR) $(OSX_AUDIO_DIR) $(OSX_VIDEO_DIR) $(SUNAUDIO_DIR) $(V4L2_DIR) $(XIMAGE_DIR) +SUBDIRS=$(DIRECTSOUND_DIR) $(WAVEFORM_DIR) $(OSS_DIR) $(OSS4_DIR) $(OSX_AUDIO_DIR) $(OSX_VIDEO_DIR) $(SUNAUDIO_DIR) $(V4L2_DIR) $(XIMAGE_DIR) DIST_SUBDIRS=directsound oss oss4 osxaudio osxvideo sunaudio v4l2 waveform ximage diff --git a/sys/oss4/oss4-mixer.c b/sys/oss4/oss4-mixer.c index e190987d6..e384452ec 100644 --- a/sys/oss4/oss4-mixer.c +++ b/sys/oss4/oss4-mixer.c @@ -54,6 +54,7 @@ #include <gst/interfaces/mixer.h> #include <gst/gst-i18n-plugin.h> +#include "gst/glib-compat-private.h" #include <glib/gprintf.h> @@ -541,8 +542,13 @@ gst_oss4_mixer_start_watch_task (GstOss4Mixer * mixer) mixer->watch_cond = g_cond_new (); mixer->watch_shutdown = FALSE; +#if !GLIB_CHECK_VERSION (2, 31, 0) mixer->watch_thread = g_thread_create (gst_oss4_mixer_watch_thread, gst_object_ref (mixer), TRUE, &err); +#else + mixer->watch_thread = g_thread_try_new ("oss4-mixer-thread", + gst_oss4_mixer_watch_thread, gst_object_ref (mixer), &err); +#endif if (mixer->watch_thread == NULL) { GST_ERROR_OBJECT (mixer, "Could not create watch thread: %s", err->message); diff --git a/sys/v4l2/gstv4l2bufferpool.c b/sys/v4l2/gstv4l2bufferpool.c index 6b9074522..d25d7ae69 100644 --- a/sys/v4l2/gstv4l2bufferpool.c +++ b/sys/v4l2/gstv4l2bufferpool.c @@ -40,6 +40,7 @@ #include "gstv4l2sink.h" #include "v4l2_calls.h" #include "gst/gst-i18n-plugin.h" +#include <gst/glib-compat-private.h> /* videodev2.h is not versioned and we can't easily check for the presence * of enum values at compile time, but the V4L2_CAP_VIDEO_OUTPUT_OVERLAY define diff --git a/sys/v4l2/gstv4l2videooverlay.c b/sys/v4l2/gstv4l2videooverlay.c index 3f28fdcd8..6ac15cdf4 100644 --- a/sys/v4l2/gstv4l2videooverlay.c +++ b/sys/v4l2/gstv4l2videooverlay.c @@ -39,6 +39,7 @@ #include "v4l2_calls.h" #include "gst/gst-i18n-plugin.h" +#include <gst/glib-compat-private.h> struct _GstV4l2Xv { diff --git a/sys/waveform/Makefile.am b/sys/waveform/Makefile.am index a9562a818..9a9fac3d7 100644 --- a/sys/waveform/Makefile.am +++ b/sys/waveform/Makefile.am @@ -1,19 +1,15 @@ plugin_LTLIBRARIES = libgstwaveformsink.la -# FIXME: Replace DIRECTSOUND CFLAGS+LIBS with waveform related ones and fix -# the configure.ac + sys/Makefile.am to get this stuff building in MingW -# For now, it's just disted for use in the VS builds. - libgstwaveformsink_la_SOURCES = gstwaveformsink.c gstwaveformplugin.c libgstwaveformsink_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) \ - $(GST_PLUGINS_BASE_CFLAGS) $(DIRECTSOUND_CFLAGS) + $(GST_PLUGINS_BASE_CFLAGS) libgstwaveformsink_la_LIBADD = \ $(GST_PLUGINS_BASE_LIBS) \ -lgstaudio-$(GST_MAJORMINOR) -lgstinterfaces-$(GST_MAJORMINOR) \ $(GST_BASE_LIBS) \ $(GST_LIBS) \ - $(DIRECTSOUND_LIBS) -libgstwaveformsink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(DIRECTSOUND_LDFLAGS) + -lwinmm +libgstwaveformsink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstwaveformsink_la_LIBTOOLFLAGS = --tag=disable-static noinst_HEADERS = gstwaveformsink.h diff --git a/sys/waveform/gstwaveformsink.c b/sys/waveform/gstwaveformsink.c index 36542c182..e4b7016bb 100644 --- a/sys/waveform/gstwaveformsink.c +++ b/sys/waveform/gstwaveformsink.c @@ -49,10 +49,6 @@ GST_DEBUG_CATEGORY_STATIC (waveformsink_debug); -static void gst_waveform_sink_base_init (gpointer g_class); -static void gst_waveform_sink_class_init (GstWaveFormSinkClass * klass); -static void gst_waveform_sink_init (GstWaveFormSink * wfsink, - GstWaveFormSinkClass * g_class); static void gst_waveform_sink_finalise (GObject * object); static void gst_waveform_sink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); @@ -154,7 +150,7 @@ static void gst_waveform_sink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { - GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (object); + /* GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (object); */ switch (prop_id) { default: @@ -167,7 +163,7 @@ static void gst_waveform_sink_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { - GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (object); + /* GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (object); */ switch (prop_id) { default: @@ -348,7 +344,7 @@ gst_waveform_sink_getcaps (GstBaseSink * bsink) static gboolean gst_waveform_sink_open (GstAudioSink * asink) { - GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink); + /* GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink); */ /* nothing to do here as the device needs to be opened with the format we will use */ @@ -448,7 +444,7 @@ gst_waveform_sink_unprepare (GstAudioSink * asink) static gboolean gst_waveform_sink_close (GstAudioSink * asink) { - GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink); + /* GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink); */ return TRUE; } diff --git a/sys/ximage/gstximagesrc.c b/sys/ximage/gstximagesrc.c index 91e3f1dbe..41559c5d5 100644 --- a/sys/ximage/gstximagesrc.c +++ b/sys/ximage/gstximagesrc.c @@ -49,6 +49,8 @@ #include <gst/gst.h> #include <gst/gst-i18n-plugin.h> +#include "gst/glib-compat-private.h" + GST_DEBUG_CATEGORY_STATIC (gst_debug_ximage_src); #define GST_CAT_DEFAULT gst_debug_ximage_src diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index 426e6196b..ec2bbb499 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -147,6 +147,7 @@ check_PROGRAMS = \ elements/shapewipe \ elements/spectrum \ elements/udpsink \ + elements/udpsrc \ elements/videocrop \ elements/videofilter \ elements/y4menc \ @@ -296,6 +297,9 @@ elements_sunaudio_LDADD = \ $(GST_PLUGINS_BASE_LIBS) -lgstinterfaces-@GST_MAJORMINOR@ \ $(LDADD) +elements_udpsrc_CFLAGS = $(AM_CFLAGS) $(GIO_CFLAGS) +elements_udpsrc_LDADD = $(LDADD) $(GIO_LIBS) + elements_videocrop_LDADD = $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) -lgstvideo-$(GST_MAJORMINOR) $(LDADD) elements_videocrop_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(CFLAGS) $(AM_CFLAGS) diff --git a/tests/check/elements/.gitignore b/tests/check/elements/.gitignore index c868fc81e..1e2c8b2e4 100644 --- a/tests/check/elements/.gitignore +++ b/tests/check/elements/.gitignore @@ -53,6 +53,7 @@ souphttpsrc spectrum sunaudio udpsink +udpsrc videocrop videofilter wavpackdec diff --git a/tests/check/elements/qtmux.c b/tests/check/elements/qtmux.c index 5ceb3d15b..84614a265 100644 --- a/tests/check/elements/qtmux.c +++ b/tests/check/elements/qtmux.c @@ -28,6 +28,8 @@ #include <unistd.h> #endif +#include <glib/gstdio.h> + #include <gst/check/gstcheck.h> #include <gst/pbutils/encoding-profile.h> @@ -830,7 +832,12 @@ test_average_bitrate_custom (const gchar * elementname, (guint) gst_util_uint64_scale_round ((guint64) total_bytes, (guint64) 8 * GST_SECOND, (guint64) total_duration); fail_unless (bitrate == expected); + gst_tag_list_free (taglist); } + + /* delete file */ + g_unlink (location); + g_free (location); } GST_START_TEST (test_average_bitrate) diff --git a/tests/check/elements/rgvolume.c b/tests/check/elements/rgvolume.c index 50dab6a5b..93a336ca2 100644 --- a/tests/check/elements/rgvolume.c +++ b/tests/check/elements/rgvolume.c @@ -49,6 +49,8 @@ static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", GST_STATIC_CAPS (RG_VOLUME_CAPS_TEMPLATE_STRING) ); +static GstBuffer *test_buffer_new (gfloat value); + /* gstcheck sets up a chain function that appends buffers to a global list. * This is our equivalent of that for event handling. */ static gboolean @@ -79,6 +81,37 @@ setup_rgvolume (void) } static void +send_newsegment_and_empty_buffer (void) +{ + GstBuffer *buf; + GstEvent *ev; + GstSegment segment; + + fail_unless (g_list_length (events) == 0); + + gst_segment_init (&segment, GST_FORMAT_TIME); + ev = gst_event_new_segment (&segment); + fail_unless (gst_pad_push_event (mysrcpad, ev), + "Pushing newsegment event failed"); + + buf = test_buffer_new (0.0); + GST_BUFFER_SIZE (buf) = 0; + GST_BUFFER_DURATION (buf) = 0; + GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET (buf); + fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK); + + fail_unless (g_list_length (events) == 1); + fail_unless (events->data == ev); + gst_mini_object_unref ((GstMiniObject *) events->data); + events = g_list_remove (events, ev); + + fail_unless (g_list_length (buffers) == 1); + fail_unless (buffers->data == buf); + gst_mini_object_unref ((GstMiniObject *) buffers->data); + buffers = g_list_remove (buffers, buf); +} + +static void cleanup_rgvolume (GstElement * element) { GST_DEBUG ("cleanup_rgvolume"); @@ -292,6 +325,8 @@ GST_START_TEST (test_events) set_playing_state (element); + send_newsegment_and_empty_buffer (); + tag_list = gst_tag_list_new_empty (); gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE, GST_TAG_TRACK_GAIN, +4.95, GST_TAG_TRACK_PEAK, 0.59463, @@ -340,6 +375,8 @@ GST_START_TEST (test_simple) "pre-amp", -6.00, "fallback-gain", +1.23, NULL); set_playing_state (element); + send_newsegment_and_empty_buffer (); + tag_list = gst_tag_list_new_empty (); gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE, GST_TAG_TRACK_GAIN, -3.45, GST_TAG_TRACK_PEAK, 1.0, @@ -380,6 +417,8 @@ GST_START_TEST (test_fallback_gain) "pre-amp", -6.00, "fallback-gain", -3.00, NULL); set_playing_state (element); + send_newsegment_and_empty_buffer (); + tag_list = gst_tag_list_new_empty (); gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE, GST_TAG_TRACK_GAIN, +3.5, GST_TAG_TRACK_PEAK, 1.0, @@ -423,6 +462,8 @@ GST_START_TEST (test_fallback_track) "pre-amp", -6.00, "fallback-gain", +1.23, NULL); set_playing_state (element); + send_newsegment_and_empty_buffer (); + tag_list = gst_tag_list_new_empty (); gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE, GST_TAG_TRACK_GAIN, +2.11, GST_TAG_TRACK_PEAK, 1.0, NULL); @@ -448,6 +489,8 @@ GST_START_TEST (test_fallback_album) "pre-amp", -6.00, "fallback-gain", +1.23, NULL); set_playing_state (element); + send_newsegment_and_empty_buffer (); + tag_list = gst_tag_list_new_empty (); gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE, GST_TAG_ALBUM_GAIN, +3.73, GST_TAG_ALBUM_PEAK, 1.0, NULL); @@ -470,6 +513,8 @@ GST_START_TEST (test_headroom) "pre-amp", +0.00, "fallback-gain", +1.23, NULL); set_playing_state (element); + send_newsegment_and_empty_buffer (); + tag_list = gst_tag_list_new_empty (); gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE, GST_TAG_TRACK_GAIN, +3.50, GST_TAG_TRACK_PEAK, 1.0, NULL); @@ -512,6 +557,8 @@ GST_START_TEST (test_reference_level) "headroom", +0.00, "pre-amp", +0.00, "fallback-gain", +1.23, NULL); set_playing_state (element); + send_newsegment_and_empty_buffer (); + tag_list = gst_tag_list_new_empty (); gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE, GST_TAG_TRACK_GAIN, 0.00, GST_TAG_TRACK_PEAK, 0.2, diff --git a/tests/check/elements/souphttpsrc.c b/tests/check/elements/souphttpsrc.c index 6a7e85244..a21ab8ff6 100644 --- a/tests/check/elements/souphttpsrc.c +++ b/tests/check/elements/souphttpsrc.c @@ -23,6 +23,8 @@ # include "config.h" #endif +#include <stdlib.h> + #include <glib.h> #include <glib/gprintf.h> #include <libsoup/soup-address.h> @@ -444,8 +446,11 @@ souphttpsrc_suite (void) TCase *tc_chain, *tc_internet; g_type_init (); + +#if !GLIB_CHECK_VERSION (2, 31, 0) if (!g_thread_supported ()) g_thread_init (NULL); +#endif s = suite_create ("souphttpsrc"); tc_chain = tcase_create ("general"); @@ -453,7 +458,7 @@ souphttpsrc_suite (void) suite_add_tcase (s, tc_chain); run_server (&http_port, &https_port); - g_atexit (stop_server); + atexit (stop_server); tcase_add_test (tc_chain, test_first_buffer_has_offset); tcase_add_test (tc_chain, test_redirect_yes); tcase_add_test (tc_chain, test_redirect_no); diff --git a/tests/check/elements/udpsrc.c b/tests/check/elements/udpsrc.c new file mode 100644 index 000000000..d84334a3e --- /dev/null +++ b/tests/check/elements/udpsrc.c @@ -0,0 +1,120 @@ +/* GStreamer UDP source unit tests + * Copyright (C) 2011 Tim-Philipp Müller <tim centricular net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include <gst/check/gstcheck.h> +#include <gio/gio.h> +#include <stdlib.h> +#include <unistd.h> + +static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +GST_START_TEST (test_udpsrc_empty_packet) +{ + GstElement *udpsrc; + GSocket *socket; + GstPad *sinkpad; + int port = 0; + + udpsrc = gst_check_setup_element ("udpsrc"); + fail_unless (udpsrc != NULL); + g_object_set (udpsrc, "port", 0, NULL); + + sinkpad = gst_check_setup_sink_pad_by_name (udpsrc, &sinktemplate, "src"); + fail_unless (sinkpad != NULL); + gst_pad_set_active (sinkpad, TRUE); + + gst_element_set_state (udpsrc, GST_STATE_PLAYING); + g_object_get (udpsrc, "port", &port, NULL); + GST_INFO ("udpsrc port = %d", port); + + socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, + G_SOCKET_PROTOCOL_UDP, NULL); + + if (socket != NULL) { + GSocketAddress *sa; + GInetAddress *ia; + gchar *s; + + ia = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4); + s = g_inet_address_to_string (ia); + GST_LOG ("inet address %s", s); + g_free (s); + sa = g_inet_socket_address_new (ia, port); + + if (g_socket_send_to (socket, sa, "HeLL0", 0, NULL, NULL) == 0) { + GST_INFO ("sent 0 bytes"); + if (g_socket_send_to (socket, sa, "HeLL0", 6, NULL, NULL) == 6) { + GstBuffer *buf; + guint len; + + GST_INFO ("sent 6 bytes"); + + g_usleep (G_USEC_PER_SEC / 2); + + len = g_list_length (buffers); + GST_INFO ("%u buffers", len); + fail_unless (len == 1 || len == 2); + + /* last buffer should be our HeLL0 string */ + buf = GST_BUFFER (g_list_nth_data (buffers, len - 1)); + fail_unless_equals_int (GST_BUFFER_SIZE (buf), 6); + fail_unless_equals_string ((gchar *) GST_BUFFER_DATA (buf), "HeLL0"); + + /* if there's another buffer, it should be 0 bytes */ + if (len == 2) { + buf = GST_BUFFER (g_list_nth_data (buffers, 0)); + fail_unless_equals_int (GST_BUFFER_SIZE (buf), 0); + } + } else { + GST_WARNING ("send_to(6 bytes) failed"); + } + } else { + GST_WARNING ("send_to(0 bytes) failed"); + } + + g_object_unref (sa); + g_object_unref (ia); + } else { + GST_WARNING ("Could not create IPv4 UDP socket for unit test"); + } + + gst_element_set_state (udpsrc, GST_STATE_NULL); + + gst_check_teardown_pad_by_name (udpsrc, "src"); + gst_check_teardown_element (udpsrc); + + g_object_unref (socket); +} + +GST_END_TEST; + +static Suite * +udpsrc_suite (void) +{ + Suite *s = suite_create ("udpsrc"); + TCase *tc_chain = tcase_create ("udpsrc"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_udpsrc_empty_packet); + return s; +} + +GST_CHECK_MAIN (udpsrc) diff --git a/tests/files/Makefile.am b/tests/files/Makefile.am index a0a282937..ae08e1a0f 100644 --- a/tests/files/Makefile.am +++ b/tests/files/Makefile.am @@ -7,6 +7,7 @@ EXTRA_DIST = \ id3-577468-unsynced-tag.tag \ id3-588148-unsynced-v24.tag \ pcm16sine.flv \ + pinknoise-vorbis.mkv \ test-cert.pem \ test-key.pem diff --git a/tests/icles/equalizer-test.c b/tests/icles/equalizer-test.c index e8126fad3..fc6d5273e 100644 --- a/tests/icles/equalizer-test.c +++ b/tests/icles/equalizer-test.c @@ -179,8 +179,10 @@ main (int argc, char **argv) GstPad *eq_sinkpad; gchar *uri; +#if !GLIB_CHECK_VERSION (2, 31, 0) if (!g_thread_supported ()) g_thread_init (NULL); +#endif /* command line option parsing */ ctx = g_option_context_new ("FILENAME"); diff --git a/tests/icles/gdkpixbufsink-test.c b/tests/icles/gdkpixbufsink-test.c index c334326e0..8ada8614b 100644 --- a/tests/icles/gdkpixbufsink-test.c +++ b/tests/icles/gdkpixbufsink-test.c @@ -330,8 +330,10 @@ main (int argc, char **argv) GOptionContext *ctx; GError *opt_err = NULL; +#if !GLIB_CHECK_VERSION (2, 31, 0) if (!g_thread_supported ()) g_thread_init (NULL); +#endif gtk_init (&argc, &argv); diff --git a/tests/icles/test-oss4.c b/tests/icles/test-oss4.c index f15875d7b..233e891fd 100644 --- a/tests/icles/test-oss4.c +++ b/tests/icles/test-oss4.c @@ -233,8 +233,10 @@ main (int argc, char **argv) GOptionContext *ctx; GError *err = NULL; +#if !GLIB_CHECK_VERSION (2, 31, 0) if (!g_thread_supported ()) g_thread_init (NULL); +#endif ctx = g_option_context_new (""); g_option_context_add_main_entries (ctx, options, NULL); diff --git a/tests/icles/v4l2src-test.c b/tests/icles/v4l2src-test.c index 02d005dca..5020c5e7f 100644 --- a/tests/icles/v4l2src-test.c +++ b/tests/icles/v4l2src-test.c @@ -491,7 +491,13 @@ main (int argc, char *argv[]) gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING); loop = g_main_loop_new (NULL, FALSE); - if (!(input_thread = g_thread_create (read_user, source, TRUE, NULL))) { +#if !GLIB_CHECK_VERSION (2, 31, 0) + input_thread = g_thread_create (read_user, source, TRUE, NULL); +#else + input_thread = g_thread_try_new ("v4l2src-test", read_user, source, NULL); +#endif + + if (input_thread == NULL) { fprintf (stderr, "error: g_thread_create return NULL"); return -1; } diff --git a/tests/icles/videocrop-test.c b/tests/icles/videocrop-test.c index 0106e6d41..de8963b3f 100644 --- a/tests/icles/videocrop-test.c +++ b/tests/icles/videocrop-test.c @@ -193,8 +193,10 @@ main (int argc, char **argv) GstCaps *filter_caps = NULL; GList *caps_list, *l; +#if !GLIB_CHECK_VERSION (2, 31, 0) if (!g_thread_supported ()) g_thread_init (NULL); +#endif /* command line option parsing */ ctx = g_option_context_new (""); |