diff --git a/gst/playback/gstplaybin2.c b/gst/playback/gstplaybin2.c index 00c2a792fb..5d523e4bdd 100644 --- a/gst/playback/gstplaybin2.c +++ b/gst/playback/gstplaybin2.c @@ -492,6 +492,7 @@ enum PROP_CONNECTION_SPEED, PROP_BUFFER_SIZE, PROP_BUFFER_DURATION, + PROP_AV_OFFSET, PROP_LAST }; @@ -786,6 +787,20 @@ gst_play_bin_class_init (GstPlayBinClass * klass) "Buffer duration when buffering network streams", -1, G_MAXINT64, DEFAULT_BUFFER_DURATION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * GstPlayBin2:av-offset: + * + * Control the synchronisation offset between the audio and video streams. + * Positive values make the audio ahead of the video and negative values make + * the audio go behind the video. + * + * Since: 0.10.30 + */ + g_object_class_install_property (gobject_klass, PROP_AV_OFFSET, + g_param_spec_int64 ("av-offset", "AV Offset", + "The synchronisation offset between audio and video in nanoseconds", + G_MININT64, G_MAXINT64, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * GstPlayBin2::about-to-finish @@ -1777,6 +1792,10 @@ gst_play_bin_set_property (GObject * object, guint prop_id, case PROP_BUFFER_DURATION: playbin->buffer_duration = g_value_get_int64 (value); break; + case PROP_AV_OFFSET: + gst_play_sink_set_av_offset (playbin->playsink, + g_value_get_int64 (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1945,6 +1964,10 @@ gst_play_bin_get_property (GObject * object, guint prop_id, GValue * value, g_value_set_int64 (value, playbin->buffer_duration); GST_OBJECT_UNLOCK (playbin); break; + case PROP_AV_OFFSET: + g_value_set_int64 (value, + gst_play_sink_get_av_offset (playbin->playsink)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -2410,8 +2433,8 @@ _playsink_sink_event_probe_cb (GstPad * pad, GstEvent * event, if (format != GST_FORMAT_TIME) data->group->selector[data->type].group_start_accum = GST_CLOCK_TIME_NONE; - else if (!GST_CLOCK_TIME_IS_VALID (data->group->selector[data-> - type].group_start_accum)) + else if (!GST_CLOCK_TIME_IS_VALID (data->group->selector[data->type]. + group_start_accum)) data->group->selector[data->type].group_start_accum = segment->accum; } else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) { gst_segment_init (&data->playbin->segments[index], GST_FORMAT_UNDEFINED); @@ -2943,8 +2966,8 @@ autoplug_continue_cb (GstElement * element, GstPad * pad, GstCaps * caps, GstPad *text_sinkpad = NULL; text_sink = - (group->playbin->text_sink) ? gst_object_ref (group-> - playbin->text_sink) : NULL; + (group->playbin->text_sink) ? gst_object_ref (group->playbin-> + text_sink) : NULL; if (text_sink) text_sinkpad = gst_element_get_static_pad (text_sink, "sink"); diff --git a/gst/playback/gstplaysink.c b/gst/playback/gstplaysink.c index 4b7dcbec71..6b9873f59d 100644 --- a/gst/playback/gstplaysink.c +++ b/gst/playback/gstplaysink.c @@ -62,6 +62,7 @@ typedef struct gboolean sink_volume; /* if the volume was provided by the sink */ GstElement *mute; /* element with the mute property */ GstElement *sink; + GstElement *ts_offset; } GstPlayAudioChain; typedef struct @@ -81,6 +82,7 @@ typedef struct GstElement *scale; GstElement *sink; gboolean async; + GstElement *ts_offset; } GstPlayVideoChain; typedef struct @@ -168,6 +170,7 @@ struct _GstPlaySink gint count; gboolean volume_changed; /* volume/mute changed while no audiochain */ gboolean mute_changed; /* ... has been created yet */ + gint64 av_offset; }; struct _GstPlaySinkClass @@ -215,6 +218,7 @@ enum PROP_SUBTITLE_ENCODING, PROP_VIS_PLUGIN, PROP_FRAME, + PROP_AV_OFFSET, PROP_LAST }; @@ -247,6 +251,8 @@ static void notify_volume_cb (GObject * object, GParamSpec * pspec, static void notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink); +static void update_av_offset (GstPlaySink * playsink); + /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */ G_DEFINE_TYPE (GstPlaySink, gst_play_sink, GST_TYPE_BIN); @@ -312,7 +318,6 @@ gst_play_sink_class_init (GstPlaySinkClass * klass) GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * GstPlaySink:frame: - * @playsink: a #GstPlaySink * * Get the currently rendered or prerolled frame in the video sink. * The #GstCaps on the buffer will describe the format of the buffer. @@ -323,6 +328,20 @@ gst_play_sink_class_init (GstPlaySinkClass * klass) gst_param_spec_mini_object ("frame", "Frame", "The last frame (NULL = no video available)", GST_TYPE_BUFFER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * GstPlaySink:av-offset: + * + * Control the synchronisation offset between the audio and video streams. + * Positive values make the audio ahead of the video and negative values make + * the audio go behind the video. + * + * Since: 0.10.30 + */ + g_object_class_install_property (gobject_klass, PROP_AV_OFFSET, + g_param_spec_int64 ("av-offset", "AV Offset", + "The synchronisation offset between audio and video in nanoseconds", + G_MININT64, G_MAXINT64, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass, @@ -1168,6 +1187,11 @@ gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async, chain->async = TRUE; } + /* find ts-offset element */ + chain->ts_offset = + gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset", + G_TYPE_INT64); + /* create a bin to hold objects, as we create them we add them to this bin so * that when something goes wrong we only need to unref the bin */ chain->chain.bin = gst_bin_new ("vbin"); @@ -1313,6 +1337,11 @@ setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async, if (ret == GST_STATE_CHANGE_FAILURE) return FALSE; + /* find ts-offset element */ + chain->ts_offset = + gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset", + G_TYPE_INT64); + /* if we can disable async behaviour of the sink, we can avoid adding a * queue for the audio chain. */ elem = @@ -1588,6 +1617,11 @@ gen_audio_chain (GstPlaySink * playsink, gboolean raw, gboolean queue) prev = NULL; } + /* find ts-offset element */ + chain->ts_offset = + gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset", + G_TYPE_INT64); + /* check if the sink, or something within the sink, has the volume property. * If it does we don't need to add a volume element. */ elem = @@ -1791,6 +1825,11 @@ setup_audio_chain (GstPlaySink * playsink, gboolean raw, gboolean queue) if (ret == GST_STATE_CHANGE_FAILURE) return FALSE; + /* find ts-offset element */ + chain->ts_offset = + gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset", + G_TYPE_INT64); + /* check if the sink, or something within the sink, has the volume property. * If it does we don't need to add a volume element. */ elem = @@ -2318,7 +2357,7 @@ gst_play_sink_reconfigure (GstPlaySink * playsink) if (playsink->text_pad && !playsink->textchain) gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL); } - + update_av_offset (playsink); do_async_done (playsink); GST_PLAY_SINK_UNLOCK (playsink); @@ -2439,6 +2478,46 @@ gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink) return result; } +static void +update_av_offset (GstPlaySink * playsink) +{ + gint64 av_offset; + GstPlayAudioChain *achain; + GstPlayVideoChain *vchain; + + av_offset = playsink->av_offset; + achain = (GstPlayAudioChain *) playsink->audiochain; + vchain = (GstPlayVideoChain *) playsink->videochain; + + if (achain && vchain && achain->ts_offset && vchain->ts_offset) { + g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL); + g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL); + } else { + GST_LOG_OBJECT (playsink, "no ts_offset elements"); + } +} + +void +gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset) +{ + GST_PLAY_SINK_LOCK (playsink); + playsink->av_offset = av_offset; + update_av_offset (playsink); + GST_PLAY_SINK_UNLOCK (playsink); +} + +gint64 +gst_play_sink_get_av_offset (GstPlaySink * playsink) +{ + gint64 result; + + GST_PLAY_SINK_LOCK (playsink); + result = playsink->av_offset; + GST_PLAY_SINK_UNLOCK (playsink); + + return result; +} + /** * gst_play_sink_get_last_frame: * @playsink: a #GstPlaySink @@ -2969,6 +3048,9 @@ gst_play_sink_set_property (GObject * object, guint prop_id, case PROP_VIS_PLUGIN: gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value)); break; + case PROP_AV_OFFSET: + gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec); break; @@ -3004,6 +3086,9 @@ gst_play_sink_get_property (GObject * object, guint prop_id, case PROP_FRAME: gst_value_take_buffer (value, gst_play_sink_get_last_frame (playsink)); break; + case PROP_AV_OFFSET: + g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec); break; diff --git a/gst/playback/gstplaysink.h b/gst/playback/gstplaysink.h index 49bb7b50e7..dc3a45b793 100644 --- a/gst/playback/gstplaysink.h +++ b/gst/playback/gstplaysink.h @@ -91,6 +91,9 @@ gchar * gst_play_sink_get_font_desc (GstPlaySink *playsink); void gst_play_sink_set_subtitle_encoding (GstPlaySink *playsink, const gchar * encoding); gchar * gst_play_sink_get_subtitle_encoding (GstPlaySink *playsink); +void gst_play_sink_set_av_offset (GstPlaySink *playsink, gint64 av_offset); +gint64 gst_play_sink_get_av_offset (GstPlaySink *playsink); + GstBuffer * gst_play_sink_get_last_frame (GstPlaySink * playsink); GstBuffer * gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps);