mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-17 11:45:25 +00:00
audiobasesink: Re-work GAP buffer and trick-mode handling
In trickmode no-audio mode, or when receiving a GAP buffer, discard the contents and render as a GAP event instead. Make sure when rendering a gap event that the ring buffer will restart on PAUSED->PLAYING by setting the eos_rendering flag. This mostly reverts commit 8557ee and replaces it. The problem with the previous approach is that it hangs in wait_preroll() on a PLAYING-PAUSED transition because it doesn't commit state properly. https://bugzilla.gnome.org/show_bug.cgi?id=735666
This commit is contained in:
parent
d3f6d2b887
commit
ca231ce321
1 changed files with 69 additions and 82 deletions
|
@ -182,9 +182,6 @@ static GstCaps *gst_audio_base_sink_fixate (GstBaseSink * bsink,
|
|||
static gboolean gst_audio_base_sink_query_pad (GstBaseSink * bsink,
|
||||
GstQuery * query);
|
||||
|
||||
static GstFlowReturn
|
||||
gst_audio_base_sink_render_samples (GstBaseSink * bsink,
|
||||
GstBuffer * buf, GstClockTime time, GstClockTime duration);
|
||||
|
||||
/* static guint gst_audio_base_sink_signals[LAST_SIGNAL] = { 0 }; */
|
||||
|
||||
|
@ -1032,6 +1029,7 @@ gst_audio_base_sink_wait_event (GstBaseSink * bsink, GstEvent * event)
|
|||
{
|
||||
GstAudioBaseSink *sink = GST_AUDIO_BASE_SINK (bsink);
|
||||
GstFlowReturn ret;
|
||||
gboolean clear_force_start_flag = FALSE;
|
||||
|
||||
/* For both gap and EOS events, make sure the ringbuffer is running
|
||||
* before trying to wait on the event! */
|
||||
|
@ -1046,6 +1044,9 @@ gst_audio_base_sink_wait_event (GstBaseSink * bsink, GstEvent * event)
|
|||
}
|
||||
|
||||
gst_audio_base_sink_force_start (sink);
|
||||
/* Make sure the ringbuffer will start again if interrupted during event_wait() */
|
||||
g_atomic_int_set (&sink->eos_rendering, 1);
|
||||
clear_force_start_flag = TRUE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -1053,15 +1054,9 @@ gst_audio_base_sink_wait_event (GstBaseSink * bsink, GstEvent * event)
|
|||
|
||||
ret = GST_BASE_SINK_CLASS (parent_class)->wait_event (bsink, event);
|
||||
if (ret != GST_FLOW_OK)
|
||||
return ret;
|
||||
goto done;
|
||||
|
||||
switch (GST_EVENT_TYPE (event)) {
|
||||
case GST_EVENT_GAP:{
|
||||
GstClockTime ts, dur;
|
||||
gst_event_parse_gap (event, &ts, &dur);
|
||||
gst_audio_base_sink_render_samples (bsink, NULL, ts, dur);
|
||||
break;
|
||||
}
|
||||
case GST_EVENT_EOS:
|
||||
/* now wait till we played everything */
|
||||
gst_audio_base_sink_drain (sink);
|
||||
|
@ -1069,6 +1064,10 @@ gst_audio_base_sink_wait_event (GstBaseSink * bsink, GstEvent * event)
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
done:
|
||||
if (clear_force_start_flag)
|
||||
g_atomic_int_set (&sink->eos_rendering, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1601,12 +1600,10 @@ gst_audio_base_sink_get_alignment (GstAudioBaseSink * sink,
|
|||
return align;
|
||||
}
|
||||
|
||||
/* buf may be NULL, to render silence/skip */
|
||||
static GstFlowReturn
|
||||
gst_audio_base_sink_render_samples (GstBaseSink * bsink,
|
||||
GstBuffer * buf, GstClockTime time, GstClockTime duration)
|
||||
gst_audio_base_sink_render (GstBaseSink * bsink, GstBuffer * buf)
|
||||
{
|
||||
GstClockTime stop, render_start, render_stop, sample_offset;
|
||||
GstClockTime time, stop, render_start, render_stop, sample_offset;
|
||||
GstClockTimeDiff sync_offset, ts_offset;
|
||||
GstAudioBaseSinkClass *bclass;
|
||||
GstAudioBaseSink *sink;
|
||||
|
@ -1615,7 +1612,8 @@ gst_audio_base_sink_render_samples (GstBaseSink * bsink,
|
|||
guint64 ctime, cstop;
|
||||
gsize offset;
|
||||
GstMapInfo info;
|
||||
guint samples = 0, written;
|
||||
gsize size;
|
||||
guint samples, written;
|
||||
gint bpf, rate;
|
||||
gint accum;
|
||||
gint out_samples;
|
||||
|
@ -1654,7 +1652,7 @@ gst_audio_base_sink_render_samples (GstBaseSink * bsink,
|
|||
|
||||
/* Before we go on, let's see if we need to payload the data. If yes, we also
|
||||
* need to unref the output buffer before leaving. */
|
||||
if (buf && bclass->payload) {
|
||||
if (bclass->payload) {
|
||||
out = bclass->payload (sink, buf);
|
||||
|
||||
if (!out)
|
||||
|
@ -1666,26 +1664,36 @@ gst_audio_base_sink_render_samples (GstBaseSink * bsink,
|
|||
bpf = GST_AUDIO_INFO_BPF (&ringbuf->spec.info);
|
||||
rate = GST_AUDIO_INFO_RATE (&ringbuf->spec.info);
|
||||
|
||||
if (buf) {
|
||||
gsize size = gst_buffer_get_size (buf);
|
||||
if (G_UNLIKELY (size % bpf) != 0)
|
||||
goto wrong_size;
|
||||
size = gst_buffer_get_size (buf);
|
||||
if (G_UNLIKELY (size % bpf) != 0)
|
||||
goto wrong_size;
|
||||
|
||||
samples = size / bpf;
|
||||
} else if (duration != GST_CLOCK_TIME_NONE) {
|
||||
/* Convert the passed duration to a number of samples */
|
||||
samples = gst_util_uint64_scale_int (duration, rate, GST_SECOND);
|
||||
}
|
||||
samples = size / bpf;
|
||||
out_samples = samples;
|
||||
|
||||
/* Last ditch attempt to ensure that we only place silence if
|
||||
* we are in trickmode no-audio mode by dropping the buffer contents.
|
||||
* We then continue calculations based on the passed times/duration */
|
||||
time = GST_BUFFER_TIMESTAMP (buf);
|
||||
|
||||
/* Last ditch attempt to ensure that we only play silence if
|
||||
* we are in trickmode no-audio mode (or if a buffer is marked as a GAP)
|
||||
* by dropping the buffer contents and rendering as a gap event instead */
|
||||
if (G_UNLIKELY ((bsink->segment.flags & GST_SEGMENT_FLAG_TRICKMODE_NO_AUDIO)
|
||||
|| (buf && GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_GAP)))) {
|
||||
GstClockTime duration;
|
||||
GstEvent *event;
|
||||
GstBaseSinkClass *bclass;
|
||||
GST_DEBUG_OBJECT (bsink,
|
||||
"Received GAP or ignoring audio for trickplay. Dropping contents");
|
||||
buf = NULL;
|
||||
|
||||
duration = gst_util_uint64_scale_int (samples, GST_SECOND, rate);
|
||||
event = gst_event_new_gap (time, duration);
|
||||
|
||||
bclass = GST_BASE_SINK_GET_CLASS (bsink);
|
||||
ret = bclass->wait_event (bsink, event);
|
||||
gst_event_unref (event);
|
||||
|
||||
/* Ensure we'll resync on the next buffer as if discont */
|
||||
sink->next_sample = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (sink,
|
||||
|
@ -1700,8 +1708,8 @@ gst_audio_base_sink_render_samples (GstBaseSink * bsink,
|
|||
if (!GST_CLOCK_TIME_IS_VALID (time)) {
|
||||
render_start = gst_audio_base_sink_get_offset (sink);
|
||||
render_stop = render_start + samples;
|
||||
GST_DEBUG_OBJECT (sink, "Samples have no timestamp."
|
||||
" Using render_start=%" G_GUINT64_FORMAT, render_start);
|
||||
GST_DEBUG_OBJECT (sink, "Buffer of size %" G_GSIZE_FORMAT " has no time."
|
||||
" Using render_start=%" G_GUINT64_FORMAT, size, render_start);
|
||||
/* we don't have a start so we don't know stop either */
|
||||
stop = -1;
|
||||
goto no_align;
|
||||
|
@ -1892,12 +1900,10 @@ gst_audio_base_sink_render_samples (GstBaseSink * bsink,
|
|||
}
|
||||
|
||||
/* always resync after a discont */
|
||||
if (G_LIKELY (buf)) {
|
||||
if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT) ||
|
||||
GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_RESYNC))) {
|
||||
GST_DEBUG_OBJECT (sink, "resync after discont/resync");
|
||||
goto no_align;
|
||||
}
|
||||
if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT) ||
|
||||
GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_RESYNC))) {
|
||||
GST_DEBUG_OBJECT (sink, "resync after discont/resync");
|
||||
goto no_align;
|
||||
}
|
||||
|
||||
/* resync when we don't know what to align the sample with */
|
||||
|
@ -1937,48 +1943,37 @@ no_align:
|
|||
/* we need to accumulate over different runs for when we get interrupted */
|
||||
accum = 0;
|
||||
align_next = TRUE;
|
||||
if (G_LIKELY (buf)) {
|
||||
gst_buffer_map (buf, &info, GST_MAP_READ);
|
||||
do {
|
||||
written =
|
||||
gst_audio_ring_buffer_commit (ringbuf, &sample_offset,
|
||||
info.data + offset, samples, out_samples, &accum);
|
||||
gst_buffer_map (buf, &info, GST_MAP_READ);
|
||||
do {
|
||||
written =
|
||||
gst_audio_ring_buffer_commit (ringbuf, &sample_offset,
|
||||
info.data + offset, samples, out_samples, &accum);
|
||||
|
||||
GST_DEBUG_OBJECT (sink, "wrote %u of %u", written, samples);
|
||||
/* if we wrote all, we're done */
|
||||
if (G_LIKELY (written == samples))
|
||||
break;
|
||||
GST_DEBUG_OBJECT (sink, "wrote %u of %u", written, samples);
|
||||
/* if we wrote all, we're done */
|
||||
if (G_LIKELY (written == samples))
|
||||
break;
|
||||
|
||||
/* else something interrupted us and we wait for preroll. */
|
||||
if ((ret = gst_base_sink_wait_preroll (bsink)) != GST_FLOW_OK)
|
||||
goto stopping;
|
||||
/* else something interrupted us and we wait for preroll. */
|
||||
if ((ret = gst_base_sink_wait_preroll (bsink)) != GST_FLOW_OK)
|
||||
goto stopping;
|
||||
|
||||
/* if we got interrupted, we cannot assume that the next sample should
|
||||
* be aligned to this one */
|
||||
align_next = FALSE;
|
||||
/* if we got interrupted, we cannot assume that the next sample should
|
||||
* be aligned to this one */
|
||||
align_next = FALSE;
|
||||
|
||||
/* update the output samples. FIXME, this will just skip them when pausing
|
||||
* during trick mode */
|
||||
if (out_samples > written) {
|
||||
out_samples -= written;
|
||||
accum = 0;
|
||||
} else
|
||||
break;
|
||||
/* update the output samples. FIXME, this will just skip them when pausing
|
||||
* during trick mode */
|
||||
if (out_samples > written) {
|
||||
out_samples -= written;
|
||||
accum = 0;
|
||||
} else
|
||||
break;
|
||||
|
||||
samples -= written;
|
||||
offset += written * bpf;
|
||||
} while (TRUE);
|
||||
gst_buffer_unmap (buf, &info);
|
||||
} else {
|
||||
GstClockTime buffer_time = ringbuf->spec.buffer_time * GST_USECOND;
|
||||
|
||||
GST_DEBUG_OBJECT (sink, "Discarded audio content. Starting ringbuffer");
|
||||
gst_audio_base_sink_force_start (sink);
|
||||
/* Wait until the time when we would have written out this timestamp */
|
||||
if (sink->priv->eos_time != -1 && sink->priv->eos_time > buffer_time)
|
||||
gst_base_sink_wait (GST_BASE_SINK (sink),
|
||||
sink->priv->eos_time - buffer_time, NULL);
|
||||
}
|
||||
samples -= written;
|
||||
offset += written * bpf;
|
||||
} while (TRUE);
|
||||
gst_buffer_unmap (buf, &info);
|
||||
|
||||
if (G_LIKELY (align_next))
|
||||
sink->next_sample = sample_offset;
|
||||
|
@ -2055,14 +2050,6 @@ sync_latency_failed:
|
|||
}
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_audio_base_sink_render (GstBaseSink * bsink, GstBuffer * buf)
|
||||
{
|
||||
GstClockTime ts = GST_BUFFER_TIMESTAMP (buf);
|
||||
GstClockTime dur = GST_BUFFER_DURATION (buf);
|
||||
return gst_audio_base_sink_render_samples (bsink, buf, ts, dur);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_audio_base_sink_create_ringbuffer:
|
||||
* @sink: a #GstAudioBaseSink.
|
||||
|
|
Loading…
Reference in a new issue