mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 16:50:47 +00:00
matroskademux: support (pull mode) negative seek rate
This commit is contained in:
parent
95e38e59a2
commit
9157c262ba
3 changed files with 169 additions and 48 deletions
|
@ -408,6 +408,11 @@ gst_matroska_demux_reset (GstElement * element)
|
|||
demux->cluster_time = GST_CLOCK_TIME_NONE;
|
||||
demux->cluster_offset = 0;
|
||||
|
||||
demux->from_offset = -1;
|
||||
demux->to_offset = G_MAXINT64;
|
||||
demux->seek_index = NULL;
|
||||
demux->seek_entry = 0;
|
||||
|
||||
if (demux->close_segment) {
|
||||
gst_event_unref (demux->close_segment);
|
||||
demux->close_segment = NULL;
|
||||
|
@ -2019,8 +2024,8 @@ gst_matroska_index_seek_find (GstMatroskaIndex * i1, GstClockTime * time,
|
|||
|
||||
static GstMatroskaIndex *
|
||||
gst_matroskademux_do_index_seek (GstMatroskaDemux * demux,
|
||||
GstMatroskaTrackContext * track, gint64 seek_pos, gint64 segment_stop,
|
||||
gboolean keyunit)
|
||||
GstMatroskaTrackContext * track, gint64 seek_pos, GArray ** _index,
|
||||
gint * _entry_index)
|
||||
{
|
||||
GstMatroskaIndex *entry = NULL;
|
||||
GArray *index;
|
||||
|
@ -2043,6 +2048,11 @@ gst_matroskademux_do_index_seek (GstMatroskaDemux * demux,
|
|||
if (entry == NULL)
|
||||
entry = &g_array_index (index, GstMatroskaIndex, 0);
|
||||
|
||||
if (_index)
|
||||
*_index = index;
|
||||
if (_entry_index)
|
||||
*_entry_index = entry - (GstMatroskaIndex *) index->data;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
@ -2155,6 +2165,57 @@ gst_matroska_demux_get_seek_track (GstMatroskaDemux * demux,
|
|||
return track;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_matroska_demux_move_to_entry (GstMatroskaDemux * demux,
|
||||
GstMatroskaIndex * entry, gboolean reset)
|
||||
{
|
||||
gint i;
|
||||
|
||||
GST_OBJECT_LOCK (demux);
|
||||
|
||||
/* seek (relative to matroska segment) */
|
||||
if (gst_ebml_read_seek (GST_EBML_READ (demux),
|
||||
entry->pos + demux->ebml_segment_start) != GST_FLOW_OK) {
|
||||
GST_DEBUG_OBJECT (demux, "Failed to seek to offset %" G_GUINT64_FORMAT,
|
||||
entry->pos + demux->ebml_segment_start);
|
||||
goto seek_error;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (demux, "Seeked to offset %" G_GUINT64_FORMAT ", block %d, "
|
||||
"time %" GST_TIME_FORMAT, entry->pos + demux->ebml_segment_start,
|
||||
entry->block, GST_TIME_ARGS (entry->time));
|
||||
|
||||
/* update the time */
|
||||
g_assert (demux->src->len == demux->num_streams);
|
||||
for (i = 0; i < demux->src->len; i++) {
|
||||
GstMatroskaTrackContext *context = g_ptr_array_index (demux->src, i);
|
||||
context->pos = entry->time;
|
||||
context->set_discont = TRUE;
|
||||
context->last_flow = GST_FLOW_OK;
|
||||
context->eos = FALSE;
|
||||
context->from_time = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
demux->segment.last_stop = entry->time;
|
||||
demux->seek_block = entry->block;
|
||||
demux->last_stop_end = GST_CLOCK_TIME_NONE;
|
||||
|
||||
if (reset) {
|
||||
demux->from_offset = -1;
|
||||
demux->to_offset = G_MAXINT64;
|
||||
}
|
||||
|
||||
GST_OBJECT_UNLOCK (demux);
|
||||
|
||||
return TRUE;
|
||||
|
||||
seek_error:
|
||||
{
|
||||
GST_OBJECT_UNLOCK (demux);
|
||||
GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), ("Got a seek error"));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux,
|
||||
GstPad * pad, GstEvent * event)
|
||||
|
@ -2166,7 +2227,6 @@ gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux,
|
|||
gboolean flush, keyunit;
|
||||
gdouble rate;
|
||||
gint64 cur, stop;
|
||||
gint i;
|
||||
GstMatroskaTrackContext *track = NULL;
|
||||
GstSegment seeksegment = { 0, };
|
||||
gboolean update;
|
||||
|
@ -2185,12 +2245,6 @@ gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux,
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
/* cannot yet do backwards playback */
|
||||
if (rate <= 0.0) {
|
||||
GST_DEBUG_OBJECT (demux, "Can only seek with positive rate");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* copy segment, we need this because we still need the old
|
||||
* segment when we close the current segment. */
|
||||
memcpy (&seeksegment, &demux->segment, sizeof (GstSegment));
|
||||
|
@ -2205,9 +2259,9 @@ gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux,
|
|||
|
||||
/* check sanity before we start flushing and all that */
|
||||
GST_OBJECT_LOCK (demux);
|
||||
if ((entry =
|
||||
gst_matroskademux_do_index_seek (demux, track,
|
||||
seeksegment.last_stop, -1, FALSE)) == NULL) {
|
||||
if ((entry = gst_matroskademux_do_index_seek (demux, track,
|
||||
seeksegment.last_stop, &demux->seek_index, &demux->seek_entry)) ==
|
||||
NULL) {
|
||||
GST_DEBUG_OBJECT (demux, "No matching seek entry in index");
|
||||
GST_OBJECT_UNLOCK (demux);
|
||||
return FALSE;
|
||||
|
@ -2233,19 +2287,6 @@ gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux,
|
|||
GST_DEBUG_OBJECT (demux, "Waiting for streaming to stop");
|
||||
GST_PAD_STREAM_LOCK (demux->sinkpad);
|
||||
|
||||
GST_OBJECT_LOCK (demux);
|
||||
|
||||
/* seek (relative to matroska segment) */
|
||||
if (gst_ebml_read_seek (GST_EBML_READ (demux),
|
||||
entry->pos + demux->ebml_segment_start) != GST_FLOW_OK) {
|
||||
GST_DEBUG_OBJECT (demux, "Failed to seek to offset %" G_GUINT64_FORMAT,
|
||||
entry->pos + demux->ebml_segment_start);
|
||||
goto seek_error;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (demux, "Seeked to offset %" G_GUINT64_FORMAT, entry->pos +
|
||||
demux->ebml_segment_start);
|
||||
|
||||
if (keyunit) {
|
||||
GST_DEBUG_OBJECT (demux, "seek to key unit, adjusting segment start to %"
|
||||
GST_TIME_FORMAT, GST_TIME_ARGS (entry->time));
|
||||
|
@ -2254,8 +2295,6 @@ gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux,
|
|||
seeksegment.time = entry->time;
|
||||
}
|
||||
|
||||
GST_OBJECT_UNLOCK (demux);
|
||||
|
||||
if (flush) {
|
||||
GST_DEBUG_OBJECT (demux, "Stopping flush");
|
||||
gst_pad_push_event (demux->sinkpad, gst_event_new_flush_stop ());
|
||||
|
@ -2279,6 +2318,10 @@ gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux,
|
|||
memcpy (&demux->segment, &seeksegment, sizeof (GstSegment));
|
||||
GST_OBJECT_UNLOCK (demux);
|
||||
|
||||
/* update some (segment) state */
|
||||
if (!gst_matroska_demux_move_to_entry (demux, entry, TRUE))
|
||||
goto seek_error;
|
||||
|
||||
/* notify start of new segment */
|
||||
if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
|
||||
GstMessage *msg;
|
||||
|
@ -2293,22 +2336,9 @@ gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux,
|
|||
gst_event_unref (demux->new_segment);
|
||||
demux->new_segment = gst_event_new_new_segment_full (FALSE,
|
||||
demux->segment.rate, demux->segment.applied_rate, demux->segment.format,
|
||||
demux->segment.last_stop, demux->segment.stop, demux->segment.time);
|
||||
demux->segment.start, demux->segment.stop, demux->segment.time);
|
||||
GST_OBJECT_UNLOCK (demux);
|
||||
|
||||
/* update the time */
|
||||
g_assert (demux->src->len == demux->num_streams);
|
||||
for (i = 0; i < demux->src->len; i++) {
|
||||
GstMatroskaTrackContext *context = g_ptr_array_index (demux->src, i);
|
||||
context->pos = entry->time;
|
||||
context->set_discont = TRUE;
|
||||
context->last_flow = GST_FLOW_OK;
|
||||
context->eos = FALSE;
|
||||
}
|
||||
demux->segment.last_stop = entry->time;
|
||||
demux->seek_block = entry->block;
|
||||
demux->last_stop_end = GST_CLOCK_TIME_NONE;
|
||||
|
||||
/* restart our task since it might have been stopped when we did the
|
||||
* flush. */
|
||||
demux->segment_running = TRUE;
|
||||
|
@ -2359,6 +2389,55 @@ gst_matroska_demux_handle_src_event (GstPad * pad, GstEvent * event)
|
|||
return res;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_matroska_demux_seek_to_previous_keyframe (GstMatroskaDemux * demux)
|
||||
{
|
||||
GstFlowReturn ret = GST_FLOW_UNEXPECTED;
|
||||
gboolean done = TRUE;
|
||||
gint i;
|
||||
|
||||
g_return_val_if_fail (demux->seek_index, GST_FLOW_UNEXPECTED);
|
||||
g_return_val_if_fail (demux->seek_entry < demux->seek_index->len,
|
||||
GST_FLOW_UNEXPECTED);
|
||||
|
||||
GST_DEBUG_OBJECT (demux, "locating previous keyframe");
|
||||
|
||||
if (!demux->seek_entry) {
|
||||
GST_DEBUG_OBJECT (demux, "no earlier index entry");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (i = 0; i < demux->src->len; i++) {
|
||||
GstMatroskaTrackContext *stream = g_ptr_array_index (demux->src, i);
|
||||
|
||||
GST_DEBUG_OBJECT (demux, "segment start %" GST_TIME_FORMAT
|
||||
", stream %d at %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (demux->segment.start), stream->index,
|
||||
GST_TIME_ARGS (stream->from_time));
|
||||
if (GST_CLOCK_TIME_IS_VALID (stream->from_time)) {
|
||||
if (stream->from_time > demux->segment.start) {
|
||||
GST_DEBUG_OBJECT (demux, "stream %d not finished yet", stream->index);
|
||||
done = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
GstMatroskaIndex *entry;
|
||||
|
||||
entry = &g_array_index (demux->seek_index, GstMatroskaIndex,
|
||||
--demux->seek_entry);
|
||||
if (!gst_matroska_demux_move_to_entry (demux, entry, FALSE))
|
||||
goto exit;
|
||||
demux->to_offset = demux->from_offset;
|
||||
demux->from_offset = -1;
|
||||
ret = GST_FLOW_OK;
|
||||
}
|
||||
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_matroska_demux_parse_header (GstMatroskaDemux * demux)
|
||||
{
|
||||
|
@ -4099,6 +4178,9 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
|
|||
gint64 time = 0;
|
||||
gint flags = 0;
|
||||
gint64 referenceblock = 0;
|
||||
gint64 offset;
|
||||
|
||||
offset = demux->parent.offset;
|
||||
|
||||
while (ret == GST_FLOW_OK) {
|
||||
if (!is_simpleblock) {
|
||||
|
@ -4409,16 +4491,20 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
|
|||
GST_DEBUG_OBJECT (demux,
|
||||
"Stream %d after segment stop %" GST_TIME_FORMAT, stream->index,
|
||||
GST_TIME_ARGS (demux->segment.stop));
|
||||
stream->eos = TRUE;
|
||||
ret = GST_FLOW_OK;
|
||||
/* combine flows */
|
||||
ret = gst_matroska_demux_combine_flows (demux, stream, ret);
|
||||
goto done;
|
||||
gst_buffer_unref (sub);
|
||||
goto eos;
|
||||
}
|
||||
if (offset >= demux->to_offset) {
|
||||
GST_DEBUG_OBJECT (demux, "Stream %d after playback section",
|
||||
stream->index);
|
||||
gst_buffer_unref (sub);
|
||||
goto eos;
|
||||
}
|
||||
|
||||
/* handle gaps, e.g. non-zero start-time, or an cue index entry
|
||||
* that landed us with timestamps not quite intended */
|
||||
if (GST_CLOCK_TIME_IS_VALID (demux->segment.last_stop)) {
|
||||
if (GST_CLOCK_TIME_IS_VALID (demux->segment.last_stop) &&
|
||||
demux->segment.rate > 0.0) {
|
||||
GstClockTimeDiff diff;
|
||||
|
||||
/* only send newsegments with increasing start times,
|
||||
|
@ -4511,6 +4597,12 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
|
|||
stream->set_discont = FALSE;
|
||||
}
|
||||
|
||||
/* reverse playback book-keeping */
|
||||
if (!GST_CLOCK_TIME_IS_VALID (stream->from_time))
|
||||
stream->from_time = lace_time;
|
||||
if (demux->from_offset == -1)
|
||||
demux->from_offset = offset;
|
||||
|
||||
GST_DEBUG_OBJECT (demux,
|
||||
"Pushing lace %d, data of size %d for stream %d, time=%"
|
||||
GST_TIME_FORMAT " and duration=%" GST_TIME_FORMAT, n,
|
||||
|
@ -4561,6 +4653,16 @@ done:
|
|||
g_free (lace_size);
|
||||
|
||||
return ret;
|
||||
|
||||
/* EXITS */
|
||||
eos:
|
||||
{
|
||||
stream->eos = TRUE;
|
||||
ret = GST_FLOW_OK;
|
||||
/* combine flows */
|
||||
ret = gst_matroska_demux_combine_flows (demux, stream, ret);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* return FALSE if block(group) should be skipped (due to a seek) */
|
||||
|
@ -5190,6 +5292,8 @@ gst_matroska_demux_loop (GstPad * pad)
|
|||
}
|
||||
|
||||
ret = gst_matroska_demux_loop_stream (demux);
|
||||
if (ret == GST_FLOW_UNEXPECTED)
|
||||
goto eos;
|
||||
if (ret != GST_FLOW_OK)
|
||||
goto pause;
|
||||
|
||||
|
@ -5208,7 +5312,7 @@ gst_matroska_demux_loop (GstPad * pad)
|
|||
|
||||
GST_INFO_OBJECT (demux, "All streams are EOS");
|
||||
ret = GST_FLOW_UNEXPECTED;
|
||||
goto pause;
|
||||
goto eos;
|
||||
}
|
||||
|
||||
next:
|
||||
|
@ -5221,6 +5325,15 @@ next:
|
|||
return;
|
||||
|
||||
/* ERRORS */
|
||||
eos:
|
||||
{
|
||||
if (demux->segment.rate < 0.0) {
|
||||
ret = gst_matroska_demux_seek_to_previous_keyframe (demux);
|
||||
if (ret == GST_FLOW_OK)
|
||||
goto next;
|
||||
}
|
||||
/* fall-through */
|
||||
}
|
||||
pause:
|
||||
{
|
||||
const gchar *reason = gst_flow_get_name (ret);
|
||||
|
|
|
@ -105,6 +105,12 @@ typedef struct _GstMatroskaDemux {
|
|||
/* some state saving */
|
||||
GstClockTime cluster_time;
|
||||
guint64 cluster_offset;
|
||||
|
||||
/* reverse playback */
|
||||
GArray *seek_index;
|
||||
gint seek_entry;
|
||||
gint64 from_offset;
|
||||
gint64 to_offset;
|
||||
} GstMatroskaDemux;
|
||||
|
||||
typedef struct _GstMatroskaDemuxClass {
|
||||
|
|
|
@ -466,6 +466,8 @@ struct _GstMatroskaTrackContext {
|
|||
GstCaps *caps;
|
||||
guint index;
|
||||
GstFlowReturn last_flow;
|
||||
/* reverse playback */
|
||||
GstClockTime from_time;
|
||||
|
||||
GArray *index_table;
|
||||
|
||||
|
|
Loading…
Reference in a new issue