playbin2: add av-offset property

Add av-offset property to control the audio and video sync offset. This can be
used to to manually correct badly synced streams.

See #620529
This commit is contained in:
Wim Taymans 2010-06-07 14:38:42 +02:00
parent 39b68dc2a8
commit ecc9a28152
3 changed files with 117 additions and 6 deletions

View file

@ -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");

View file

@ -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;

View file

@ -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);