mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-04-26 06:54:49 +00:00
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:
parent
39b68dc2a8
commit
ecc9a28152
3 changed files with 117 additions and 6 deletions
|
@ -492,6 +492,7 @@ enum
|
||||||
PROP_CONNECTION_SPEED,
|
PROP_CONNECTION_SPEED,
|
||||||
PROP_BUFFER_SIZE,
|
PROP_BUFFER_SIZE,
|
||||||
PROP_BUFFER_DURATION,
|
PROP_BUFFER_DURATION,
|
||||||
|
PROP_AV_OFFSET,
|
||||||
PROP_LAST
|
PROP_LAST
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -786,6 +787,20 @@ gst_play_bin_class_init (GstPlayBinClass * klass)
|
||||||
"Buffer duration when buffering network streams",
|
"Buffer duration when buffering network streams",
|
||||||
-1, G_MAXINT64, DEFAULT_BUFFER_DURATION,
|
-1, G_MAXINT64, DEFAULT_BUFFER_DURATION,
|
||||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
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
|
* GstPlayBin2::about-to-finish
|
||||||
|
@ -1777,6 +1792,10 @@ gst_play_bin_set_property (GObject * object, guint prop_id,
|
||||||
case PROP_BUFFER_DURATION:
|
case PROP_BUFFER_DURATION:
|
||||||
playbin->buffer_duration = g_value_get_int64 (value);
|
playbin->buffer_duration = g_value_get_int64 (value);
|
||||||
break;
|
break;
|
||||||
|
case PROP_AV_OFFSET:
|
||||||
|
gst_play_sink_set_av_offset (playbin->playsink,
|
||||||
|
g_value_get_int64 (value));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
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);
|
g_value_set_int64 (value, playbin->buffer_duration);
|
||||||
GST_OBJECT_UNLOCK (playbin);
|
GST_OBJECT_UNLOCK (playbin);
|
||||||
break;
|
break;
|
||||||
|
case PROP_AV_OFFSET:
|
||||||
|
g_value_set_int64 (value,
|
||||||
|
gst_play_sink_get_av_offset (playbin->playsink));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
|
@ -2410,8 +2433,8 @@ _playsink_sink_event_probe_cb (GstPad * pad, GstEvent * event,
|
||||||
|
|
||||||
if (format != GST_FORMAT_TIME)
|
if (format != GST_FORMAT_TIME)
|
||||||
data->group->selector[data->type].group_start_accum = GST_CLOCK_TIME_NONE;
|
data->group->selector[data->type].group_start_accum = GST_CLOCK_TIME_NONE;
|
||||||
else if (!GST_CLOCK_TIME_IS_VALID (data->group->selector[data->
|
else if (!GST_CLOCK_TIME_IS_VALID (data->group->selector[data->type].
|
||||||
type].group_start_accum))
|
group_start_accum))
|
||||||
data->group->selector[data->type].group_start_accum = segment->accum;
|
data->group->selector[data->type].group_start_accum = segment->accum;
|
||||||
} else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
|
} else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
|
||||||
gst_segment_init (&data->playbin->segments[index], GST_FORMAT_UNDEFINED);
|
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;
|
GstPad *text_sinkpad = NULL;
|
||||||
|
|
||||||
text_sink =
|
text_sink =
|
||||||
(group->playbin->text_sink) ? gst_object_ref (group->
|
(group->playbin->text_sink) ? gst_object_ref (group->playbin->
|
||||||
playbin->text_sink) : NULL;
|
text_sink) : NULL;
|
||||||
if (text_sink)
|
if (text_sink)
|
||||||
text_sinkpad = gst_element_get_static_pad (text_sink, "sink");
|
text_sinkpad = gst_element_get_static_pad (text_sink, "sink");
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ typedef struct
|
||||||
gboolean sink_volume; /* if the volume was provided by the sink */
|
gboolean sink_volume; /* if the volume was provided by the sink */
|
||||||
GstElement *mute; /* element with the mute property */
|
GstElement *mute; /* element with the mute property */
|
||||||
GstElement *sink;
|
GstElement *sink;
|
||||||
|
GstElement *ts_offset;
|
||||||
} GstPlayAudioChain;
|
} GstPlayAudioChain;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
|
@ -81,6 +82,7 @@ typedef struct
|
||||||
GstElement *scale;
|
GstElement *scale;
|
||||||
GstElement *sink;
|
GstElement *sink;
|
||||||
gboolean async;
|
gboolean async;
|
||||||
|
GstElement *ts_offset;
|
||||||
} GstPlayVideoChain;
|
} GstPlayVideoChain;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
|
@ -168,6 +170,7 @@ struct _GstPlaySink
|
||||||
gint count;
|
gint count;
|
||||||
gboolean volume_changed; /* volume/mute changed while no audiochain */
|
gboolean volume_changed; /* volume/mute changed while no audiochain */
|
||||||
gboolean mute_changed; /* ... has been created yet */
|
gboolean mute_changed; /* ... has been created yet */
|
||||||
|
gint64 av_offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstPlaySinkClass
|
struct _GstPlaySinkClass
|
||||||
|
@ -215,6 +218,7 @@ enum
|
||||||
PROP_SUBTITLE_ENCODING,
|
PROP_SUBTITLE_ENCODING,
|
||||||
PROP_VIS_PLUGIN,
|
PROP_VIS_PLUGIN,
|
||||||
PROP_FRAME,
|
PROP_FRAME,
|
||||||
|
PROP_AV_OFFSET,
|
||||||
PROP_LAST
|
PROP_LAST
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -247,6 +251,8 @@ static void notify_volume_cb (GObject * object, GParamSpec * pspec,
|
||||||
static void notify_mute_cb (GObject * object, GParamSpec * pspec,
|
static void notify_mute_cb (GObject * object, GParamSpec * pspec,
|
||||||
GstPlaySink * playsink);
|
GstPlaySink * playsink);
|
||||||
|
|
||||||
|
static void update_av_offset (GstPlaySink * playsink);
|
||||||
|
|
||||||
/* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
|
/* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
|
||||||
|
|
||||||
G_DEFINE_TYPE (GstPlaySink, gst_play_sink, GST_TYPE_BIN);
|
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));
|
GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
/**
|
/**
|
||||||
* GstPlaySink:frame:
|
* GstPlaySink:frame:
|
||||||
* @playsink: a #GstPlaySink
|
|
||||||
*
|
*
|
||||||
* Get the currently rendered or prerolled frame in the video sink.
|
* Get the currently rendered or prerolled frame in the video sink.
|
||||||
* The #GstCaps on the buffer will describe the format of the buffer.
|
* 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",
|
gst_param_spec_mini_object ("frame", "Frame",
|
||||||
"The last frame (NULL = no video available)",
|
"The last frame (NULL = no video available)",
|
||||||
GST_TYPE_BUFFER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
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_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
|
||||||
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
|
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;
|
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
|
/* 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 */
|
* that when something goes wrong we only need to unref the bin */
|
||||||
chain->chain.bin = gst_bin_new ("vbin");
|
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)
|
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||||
return FALSE;
|
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
|
/* if we can disable async behaviour of the sink, we can avoid adding a
|
||||||
* queue for the audio chain. */
|
* queue for the audio chain. */
|
||||||
elem =
|
elem =
|
||||||
|
@ -1588,6 +1617,11 @@ gen_audio_chain (GstPlaySink * playsink, gboolean raw, gboolean queue)
|
||||||
prev = NULL;
|
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.
|
/* 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. */
|
* If it does we don't need to add a volume element. */
|
||||||
elem =
|
elem =
|
||||||
|
@ -1791,6 +1825,11 @@ setup_audio_chain (GstPlaySink * playsink, gboolean raw, gboolean queue)
|
||||||
if (ret == GST_STATE_CHANGE_FAILURE)
|
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||||
return FALSE;
|
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.
|
/* 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. */
|
* If it does we don't need to add a volume element. */
|
||||||
elem =
|
elem =
|
||||||
|
@ -2318,7 +2357,7 @@ gst_play_sink_reconfigure (GstPlaySink * playsink)
|
||||||
if (playsink->text_pad && !playsink->textchain)
|
if (playsink->text_pad && !playsink->textchain)
|
||||||
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
|
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
|
||||||
}
|
}
|
||||||
|
update_av_offset (playsink);
|
||||||
do_async_done (playsink);
|
do_async_done (playsink);
|
||||||
GST_PLAY_SINK_UNLOCK (playsink);
|
GST_PLAY_SINK_UNLOCK (playsink);
|
||||||
|
|
||||||
|
@ -2439,6 +2478,46 @@ gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
|
||||||
return result;
|
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:
|
* gst_play_sink_get_last_frame:
|
||||||
* @playsink: a #GstPlaySink
|
* @playsink: a #GstPlaySink
|
||||||
|
@ -2969,6 +3048,9 @@ gst_play_sink_set_property (GObject * object, guint prop_id,
|
||||||
case PROP_VIS_PLUGIN:
|
case PROP_VIS_PLUGIN:
|
||||||
gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
|
gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
|
||||||
break;
|
break;
|
||||||
|
case PROP_AV_OFFSET:
|
||||||
|
gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
|
||||||
break;
|
break;
|
||||||
|
@ -3004,6 +3086,9 @@ gst_play_sink_get_property (GObject * object, guint prop_id,
|
||||||
case PROP_FRAME:
|
case PROP_FRAME:
|
||||||
gst_value_take_buffer (value, gst_play_sink_get_last_frame (playsink));
|
gst_value_take_buffer (value, gst_play_sink_get_last_frame (playsink));
|
||||||
break;
|
break;
|
||||||
|
case PROP_AV_OFFSET:
|
||||||
|
g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -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);
|
void gst_play_sink_set_subtitle_encoding (GstPlaySink *playsink, const gchar * encoding);
|
||||||
gchar * gst_play_sink_get_subtitle_encoding (GstPlaySink *playsink);
|
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_get_last_frame (GstPlaySink * playsink);
|
||||||
GstBuffer * gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps);
|
GstBuffer * gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue