mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-12 02:15:31 +00:00
ext/flac/gstflacdec.*: Send segments from the streaming thread. Fixes #502187.
Original commit message from CVS: * ext/flac/gstflacdec.c: (gst_flac_dec_metadata_callback), (gst_flac_dec_write): * ext/flac/gstflacdec.h: Send segments from the streaming thread. Fixes #502187. Fix segment seeking and a bunch of other seeking cases.
This commit is contained in:
parent
7951e1cceb
commit
981460c22b
3 changed files with 151 additions and 78 deletions
|
@ -1,3 +1,11 @@
|
|||
2007-12-14 Wim Taymans <wim.taymans@collabora.co.uk>
|
||||
|
||||
* ext/flac/gstflacdec.c: (gst_flac_dec_metadata_callback),
|
||||
(gst_flac_dec_write):
|
||||
* ext/flac/gstflacdec.h:
|
||||
Send segments from the streaming thread. Fixes #502187.
|
||||
Fix segment seeking and a bunch of other seeking cases.
|
||||
|
||||
2007-12-14 Tim-Philipp Müller <tim at centricular dot net>
|
||||
|
||||
* gst/id3demux/id3v2frames.c: (parse_url_link_frame):
|
||||
|
|
|
@ -86,8 +86,6 @@ static gboolean gst_flac_dec_sink_activate_pull (GstPad * sinkpad,
|
|||
gboolean active);
|
||||
static gboolean gst_flac_dec_sink_activate_push (GstPad * sinkpad,
|
||||
gboolean active);
|
||||
static void gst_flac_dec_send_newsegment (GstFlacDec * flacdec,
|
||||
gboolean update);
|
||||
static gboolean gst_flac_dec_sink_event (GstPad * pad, GstEvent * event);
|
||||
static GstFlowReturn gst_flac_dec_chain (GstPad * pad, GstBuffer * buf);
|
||||
static void gst_flac_dec_reset_decoders (GstFlacDec * flacdec);
|
||||
|
@ -575,10 +573,23 @@ gst_flac_dec_metadata_callback (GstFlacDec * flacdec,
|
|||
GST_DEBUG_OBJECT (flacdec, "total samples = %" G_GINT64_FORMAT, samples);
|
||||
|
||||
if (samples > 0) {
|
||||
gint64 duration;
|
||||
|
||||
gst_segment_set_duration (&flacdec->segment, GST_FORMAT_DEFAULT,
|
||||
samples);
|
||||
if (flacdec->segment.stop == -1)
|
||||
flacdec->segment.stop = samples;
|
||||
|
||||
/* convert duration to time */
|
||||
duration = gst_util_uint64_scale_int (samples, GST_SECOND,
|
||||
flacdec->sample_rate);
|
||||
|
||||
/* fixme, at this time we could seek to the queued seek event if we have
|
||||
* any */
|
||||
if (flacdec->start_segment)
|
||||
gst_event_unref (flacdec->start_segment);
|
||||
flacdec->start_segment =
|
||||
gst_event_new_new_segment_full (FALSE,
|
||||
flacdec->segment.rate, flacdec->segment.applied_rate,
|
||||
GST_FORMAT_TIME, 0, duration, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -856,6 +867,7 @@ gst_flac_dec_write (GstFlacDec * flacdec, const FLAC__Frame * frame,
|
|||
guint channels = frame->header.channels;
|
||||
guint samples = frame->header.blocksize;
|
||||
guint j, i;
|
||||
GstClockTime next;
|
||||
|
||||
switch (depth) {
|
||||
case 8:
|
||||
|
@ -904,9 +916,13 @@ gst_flac_dec_write (GstFlacDec * flacdec, const FLAC__Frame * frame,
|
|||
flacdec->sample_rate = frame->header.sample_rate;
|
||||
}
|
||||
|
||||
if (flacdec->need_newsegment) {
|
||||
gst_flac_dec_send_newsegment (flacdec, FALSE);
|
||||
flacdec->need_newsegment = FALSE;
|
||||
if (flacdec->close_segment) {
|
||||
gst_pad_push_event (flacdec->srcpad, flacdec->close_segment);
|
||||
flacdec->close_segment = NULL;
|
||||
}
|
||||
if (flacdec->start_segment) {
|
||||
gst_pad_push_event (flacdec->srcpad, flacdec->start_segment);
|
||||
flacdec->start_segment = NULL;
|
||||
}
|
||||
|
||||
ret = gst_pad_alloc_buffer_and_set_caps (flacdec->srcpad,
|
||||
|
@ -929,9 +945,11 @@ gst_flac_dec_write (GstFlacDec * flacdec, const FLAC__Frame * frame,
|
|||
gst_util_uint64_scale_int (flacdec->segment.last_stop, GST_SECOND,
|
||||
frame->header.sample_rate);
|
||||
|
||||
GST_BUFFER_DURATION (outbuf) =
|
||||
gst_util_uint64_scale_int (samples, GST_SECOND,
|
||||
frame->header.sample_rate);
|
||||
/* get next timestamp to calculate the duration */
|
||||
next = gst_util_uint64_scale_int (flacdec->segment.last_stop + samples,
|
||||
GST_SECOND, frame->header.sample_rate);
|
||||
|
||||
GST_BUFFER_DURATION (outbuf) = next - GST_BUFFER_TIMESTAMP (outbuf);
|
||||
|
||||
if (depth == 8) {
|
||||
gint8 *outbuffer = (gint8 *) GST_BUFFER_DATA (outbuf);
|
||||
|
@ -968,6 +986,11 @@ gst_flac_dec_write (GstFlacDec * flacdec, const FLAC__Frame * frame,
|
|||
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
|
||||
GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)));
|
||||
|
||||
if (flacdec->discont) {
|
||||
outbuf = gst_buffer_make_metadata_writable (outbuf);
|
||||
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
|
||||
flacdec->discont = FALSE;
|
||||
}
|
||||
ret = gst_pad_push (flacdec->srcpad, outbuf);
|
||||
} else {
|
||||
GST_DEBUG ("not pushing %d samples at offset %" G_GINT64_FORMAT
|
||||
|
@ -1177,14 +1200,13 @@ segment_done_and_pause:
|
|||
|
||||
goto pause;
|
||||
}
|
||||
|
||||
eos_and_pause:
|
||||
{
|
||||
GST_DEBUG_OBJECT (flacdec, "sending EOS event");
|
||||
flacdec->running = FALSE;
|
||||
gst_pad_push_event (flacdec->srcpad, gst_event_new_eos ());
|
||||
/* fall through to pause */
|
||||
}
|
||||
|
||||
pause:
|
||||
{
|
||||
GST_DEBUG_OBJECT (flacdec, "pausing");
|
||||
|
@ -1667,37 +1689,6 @@ done:
|
|||
return res;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_flac_dec_send_newsegment (GstFlacDec * flacdec, gboolean update)
|
||||
{
|
||||
GstSegment *s = &flacdec->segment;
|
||||
GstFormat target_format = GST_FORMAT_TIME;
|
||||
gint64 stop_time = -1;
|
||||
gint64 start_time = 0;
|
||||
|
||||
/* segment is in DEFAULT format, but we want to send a TIME newsegment */
|
||||
if (!gst_flac_dec_convert_src (flacdec->srcpad, GST_FORMAT_DEFAULT,
|
||||
s->start, &target_format, &start_time)) {
|
||||
GST_WARNING_OBJECT (flacdec, "failed to convert segment start %lld to TIME",
|
||||
s->start);
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->stop != -1 && !gst_flac_dec_convert_src (flacdec->srcpad,
|
||||
GST_FORMAT_DEFAULT, s->stop, &target_format, &stop_time)) {
|
||||
GST_WARNING_OBJECT (flacdec, "failed to convert segment stop to TIME");
|
||||
return;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (flacdec, "sending newsegment from %" GST_TIME_FORMAT
|
||||
" to %" GST_TIME_FORMAT, GST_TIME_ARGS (start_time),
|
||||
GST_TIME_ARGS (stop_time));
|
||||
|
||||
gst_pad_push_event (flacdec->srcpad,
|
||||
gst_event_new_new_segment (update, s->rate, GST_FORMAT_TIME,
|
||||
start_time, stop_time, start_time));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_flac_dec_handle_seek_event (GstFlacDec * flacdec, GstEvent * event)
|
||||
{
|
||||
|
@ -1710,7 +1701,7 @@ gst_flac_dec_handle_seek_event (GstFlacDec * flacdec, GstEvent * event)
|
|||
gboolean only_update = FALSE;
|
||||
gboolean flush;
|
||||
gdouble rate;
|
||||
gint64 start;
|
||||
gint64 start, last_stop;
|
||||
gint64 stop;
|
||||
|
||||
if (flacdec->seekable_decoder == NULL) {
|
||||
|
@ -1752,23 +1743,29 @@ gst_flac_dec_handle_seek_event (GstFlacDec * flacdec, GstEvent * event)
|
|||
flush = ((seek_flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH);
|
||||
|
||||
if (flush) {
|
||||
/* flushing seek, clear the pipeline of stuff, we need a newsegment after
|
||||
* this. */
|
||||
GST_DEBUG_OBJECT (flacdec, "flushing");
|
||||
gst_pad_push_event (flacdec->srcpad, gst_event_new_flush_start ());
|
||||
} else {
|
||||
/* non flushing seek, pause the task */
|
||||
GST_DEBUG_OBJECT (flacdec, "stopping task");
|
||||
gst_pad_stop_task (flacdec->sinkpad);
|
||||
}
|
||||
|
||||
/* acquire the stream lock, this either happens when the streaming thread
|
||||
* stopped because of the flush or when the task is paused after the loop
|
||||
* function finished an iteration, which can never happen when it's blocked
|
||||
* downstream in PAUSED, for example */
|
||||
GST_PAD_STREAM_LOCK (flacdec->sinkpad);
|
||||
|
||||
/* operate on segment copy until we know the seek worked */
|
||||
/* operate on segment copy until we know the seek worked. The idea is that
|
||||
* when the seek fails, we want to continue with what we were doing without
|
||||
* having the segment being updated. */
|
||||
segment = flacdec->segment;
|
||||
|
||||
if (segment.duration > 0) {
|
||||
if (start != -1)
|
||||
start = CLAMP (start, 0, segment.duration - 1);
|
||||
if (stop != -1)
|
||||
stop = CLAMP (stop, 0, segment.duration - 1);
|
||||
}
|
||||
|
||||
/* update the segment with the seek values, last_stop will contain the new
|
||||
* position we should seek to */
|
||||
gst_segment_set_seek (&segment, rate, GST_FORMAT_DEFAULT,
|
||||
seek_flags, start_type, start, stop_type, stop, &only_update);
|
||||
|
||||
|
@ -1779,54 +1776,113 @@ gst_flac_dec_handle_seek_event (GstFlacDec * flacdec, GstEvent * event)
|
|||
GST_TIME_ARGS (segment.stop * GST_SECOND / flacdec->sample_rate));
|
||||
|
||||
GST_DEBUG_OBJECT (flacdec, "performing seek to sample %" G_GINT64_FORMAT,
|
||||
segment.start);
|
||||
segment.last_stop);
|
||||
|
||||
/* mark ourselves as seeking because the above lines will trigger some
|
||||
* callbacks that need to behave differently when seeking */
|
||||
flacdec->seeking = TRUE;
|
||||
|
||||
seek_ok =
|
||||
#ifdef LEGACY_FLAC
|
||||
FLAC__seekable_stream_decoder_seek_absolute (flacdec->seekable_decoder,
|
||||
segment.start);
|
||||
segment.last_stop);
|
||||
#else
|
||||
FLAC__stream_decoder_seek_absolute (flacdec->seekable_decoder,
|
||||
segment.start);
|
||||
segment.last_stop);
|
||||
#endif
|
||||
|
||||
flacdec->seeking = FALSE;
|
||||
|
||||
if (flush) {
|
||||
gst_pad_push_event (flacdec->srcpad, gst_event_new_flush_stop ());
|
||||
if (!seek_ok) {
|
||||
/* seek failed, segment was not updated and we start streaming again with
|
||||
* the previous segment values */
|
||||
GST_WARNING_OBJECT (flacdec, "seek failed");
|
||||
} else if (!flush && flacdec->running) {
|
||||
/* we are running the current segment and doing a non-flushing seek,
|
||||
* close the segment first based on the last_stop. */
|
||||
GST_DEBUG_OBJECT (flacdec, "closing running segment %" G_GINT64_FORMAT
|
||||
" to %" G_GINT64_FORMAT, segment.start, segment.last_stop);
|
||||
|
||||
/* convert the old segment values to time to close the old segment */
|
||||
start = gst_util_uint64_scale_int (flacdec->segment.start, GST_SECOND,
|
||||
flacdec->sample_rate);
|
||||
last_stop =
|
||||
gst_util_uint64_scale_int (flacdec->segment.last_stop, GST_SECOND,
|
||||
flacdec->sample_rate);
|
||||
|
||||
/* queue the segment for sending in the stream thread, start and time are
|
||||
* always the same. */
|
||||
if (flacdec->close_segment)
|
||||
gst_event_unref (flacdec->close_segment);
|
||||
flacdec->close_segment =
|
||||
gst_event_new_new_segment_full (TRUE,
|
||||
flacdec->segment.rate, flacdec->segment.applied_rate, GST_FORMAT_TIME,
|
||||
start, last_stop, start);
|
||||
}
|
||||
|
||||
if (seek_ok) {
|
||||
/* seek succeeded, copy new segment values */
|
||||
flacdec->segment = segment;
|
||||
gst_flac_dec_send_newsegment (flacdec, FALSE);
|
||||
flacdec->segment.last_stop = segment.start;
|
||||
|
||||
GST_DEBUG_OBJECT (flacdec, "seek successful");
|
||||
|
||||
/* notify start of new segment */
|
||||
if ((flacdec->segment.flags & GST_SEEK_FLAG_SEGMENT) != 0) {
|
||||
GstMessage *msg;
|
||||
gint64 start_time = 0;
|
||||
|
||||
start_time = gst_util_uint64_scale_int (segment.start, GST_SECOND,
|
||||
flacdec->sample_rate);
|
||||
|
||||
msg = gst_message_new_segment_start (GST_OBJECT (flacdec),
|
||||
GST_FORMAT_TIME, start_time);
|
||||
gst_element_post_message (GST_ELEMENT (flacdec), msg);
|
||||
}
|
||||
} else {
|
||||
GST_WARNING_OBJECT (flacdec, "seek failed");
|
||||
}
|
||||
|
||||
/* convert the (new) segment values to time, we will need them to generate the
|
||||
* new segment events. */
|
||||
start = gst_util_uint64_scale_int (flacdec->segment.start, GST_SECOND,
|
||||
flacdec->sample_rate);
|
||||
last_stop = gst_util_uint64_scale_int (flacdec->segment.last_stop, GST_SECOND,
|
||||
flacdec->sample_rate);
|
||||
|
||||
/* for deriving a stop position for the playback segment from the seek
|
||||
* segment, we must take the duration when the stop is not set */
|
||||
if (flacdec->segment.stop != -1)
|
||||
stop = gst_util_uint64_scale_int (flacdec->segment.stop, GST_SECOND,
|
||||
flacdec->sample_rate);
|
||||
else
|
||||
stop = gst_util_uint64_scale_int (flacdec->segment.duration, GST_SECOND,
|
||||
flacdec->sample_rate);
|
||||
|
||||
/* notify start of new segment when we were asked to do so. */
|
||||
if (segment.flags & GST_SEEK_FLAG_SEGMENT) {
|
||||
/* last_stop contains the position we start from */
|
||||
gst_element_post_message (GST_ELEMENT (flacdec),
|
||||
gst_message_new_segment_start (GST_OBJECT (flacdec),
|
||||
GST_FORMAT_TIME, last_stop));
|
||||
}
|
||||
|
||||
/* if the seek was ok or (when it failed) we are flushing, we need to send out
|
||||
* a new segment. If we did not flush and the seek failed, we simply do
|
||||
* nothing here and continue where we were. */
|
||||
if (seek_ok || flush) {
|
||||
GST_DEBUG_OBJECT (flacdec, "Creating newsegment from %" G_GINT64_FORMAT
|
||||
" to %" G_GINT64_FORMAT, segment.last_stop, stop);
|
||||
/* now replace the old segment so that we send it in the stream thread the
|
||||
* next time it is scheduled. */
|
||||
if (flacdec->start_segment)
|
||||
gst_event_unref (flacdec->start_segment);
|
||||
flacdec->start_segment =
|
||||
gst_event_new_new_segment_full (FALSE,
|
||||
segment.rate, segment.applied_rate, GST_FORMAT_TIME,
|
||||
last_stop, stop, last_stop);
|
||||
}
|
||||
|
||||
/* prepare for streaming again, start with a flush stop when we issued a flush
|
||||
* start before. */
|
||||
if (flush) {
|
||||
GST_DEBUG_OBJECT (flacdec, "flushing stop");
|
||||
gst_pad_push_event (flacdec->srcpad, gst_event_new_flush_stop ());
|
||||
}
|
||||
|
||||
/* we'll generate a discont on the next buffer */
|
||||
flacdec->discont = TRUE;
|
||||
/* the task is running again now */
|
||||
flacdec->running = TRUE;
|
||||
gst_pad_start_task (flacdec->sinkpad,
|
||||
(GstTaskFunction) gst_flac_dec_loop, flacdec->sinkpad);
|
||||
|
||||
GST_PAD_STREAM_UNLOCK (flacdec->sinkpad);
|
||||
|
||||
return TRUE;
|
||||
return seek_ok;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
@ -1879,6 +1935,8 @@ gst_flac_dec_sink_activate_push (GstPad * sinkpad, gboolean active)
|
|||
static gboolean
|
||||
gst_flac_dec_sink_activate_pull (GstPad * sinkpad, gboolean active)
|
||||
{
|
||||
gboolean res;
|
||||
|
||||
if (active) {
|
||||
GstFlacDec *flacdec;
|
||||
|
||||
|
@ -1886,11 +1944,14 @@ gst_flac_dec_sink_activate_pull (GstPad * sinkpad, gboolean active)
|
|||
|
||||
flacdec->offset = 0;
|
||||
gst_flac_dec_setup_seekable_decoder (flacdec);
|
||||
return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_flac_dec_loop,
|
||||
flacdec->running = TRUE;
|
||||
|
||||
res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_flac_dec_loop,
|
||||
sinkpad);
|
||||
} else {
|
||||
return gst_pad_stop_task (sinkpad);
|
||||
res = gst_pad_stop_task (sinkpad);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
|
|
|
@ -71,6 +71,10 @@ struct _GstFlacDec {
|
|||
|
||||
GstSegment segment; /* the currently configured segment, in
|
||||
* samples/audio frames (DEFAULT format) */
|
||||
gboolean running;
|
||||
gboolean discont;
|
||||
GstEvent *close_segment;
|
||||
GstEvent *start_segment;
|
||||
|
||||
GstFlowReturn last_flow; /* the last flow return received from either
|
||||
* gst_pad_push or gst_pad_buffer_alloc */
|
||||
|
|
Loading…
Reference in a new issue