mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-03 15:06:34 +00:00
flvdemux: support (pull mode) negative seek rate
This commit is contained in:
parent
d3ae0ef71f
commit
1c7b1d110b
3 changed files with 151 additions and 36 deletions
|
@ -565,10 +565,80 @@ beach:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_flv_demux_move_to_offset (GstFLVDemux * demux, gint64 offset,
|
||||||
|
gboolean reset)
|
||||||
|
{
|
||||||
|
demux->offset = offset;
|
||||||
|
|
||||||
|
/* Tell all the stream we moved to a different position (discont) */
|
||||||
|
demux->audio_need_discont = TRUE;
|
||||||
|
demux->video_need_discont = TRUE;
|
||||||
|
|
||||||
|
/* next section setup */
|
||||||
|
demux->from_offset = -1;
|
||||||
|
demux->audio_done = demux->video_done = FALSE;
|
||||||
|
demux->audio_first_ts = demux->video_first_ts = GST_CLOCK_TIME_NONE;
|
||||||
|
|
||||||
|
if (reset) {
|
||||||
|
demux->from_offset = -1;
|
||||||
|
demux->to_offset = G_MAXINT64;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we seeked at the beginning of the file parse the header again */
|
||||||
|
if (G_UNLIKELY (!demux->offset)) {
|
||||||
|
demux->state = FLV_STATE_HEADER;
|
||||||
|
} else { /* or parse a tag */
|
||||||
|
demux->state = FLV_STATE_TAG_TYPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_flv_demux_seek_to_prev_keyframe (GstFLVDemux * demux)
|
gst_flv_demux_seek_to_prev_keyframe (GstFLVDemux * demux)
|
||||||
{
|
{
|
||||||
return GST_FLOW_OK;
|
GstFlowReturn ret = GST_FLOW_UNEXPECTED;
|
||||||
|
GstIndexEntry *entry = NULL;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (demux,
|
||||||
|
"terminated section started at offset %" G_GINT64_FORMAT,
|
||||||
|
demux->from_offset);
|
||||||
|
|
||||||
|
/* we are done if we got all audio and video */
|
||||||
|
if ((!GST_CLOCK_TIME_IS_VALID (demux->audio_first_ts) ||
|
||||||
|
demux->audio_first_ts < demux->segment.start) &&
|
||||||
|
(!GST_CLOCK_TIME_IS_VALID (demux->video_first_ts) ||
|
||||||
|
demux->video_first_ts < demux->segment.start))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (demux->from_offset <= 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (demux, "locating previous position");
|
||||||
|
|
||||||
|
/* locate index entry before previous start position */
|
||||||
|
if (demux->index)
|
||||||
|
entry = gst_index_get_assoc_entry (demux->index, demux->index_id,
|
||||||
|
GST_INDEX_LOOKUP_BEFORE, GST_ASSOCIATION_FLAG_KEY_UNIT,
|
||||||
|
GST_FORMAT_BYTES, demux->from_offset - 1);
|
||||||
|
|
||||||
|
if (entry) {
|
||||||
|
gint64 bytes, time;
|
||||||
|
|
||||||
|
gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &bytes);
|
||||||
|
gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &time);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (demux, "found index entry for %" G_GINT64_FORMAT
|
||||||
|
" at %" GST_TIME_FORMAT ", seeking to %" G_GINT64_FORMAT,
|
||||||
|
demux->offset - 1, GST_TIME_ARGS (time), bytes);
|
||||||
|
|
||||||
|
/* setup for next section */
|
||||||
|
demux->to_offset = demux->from_offset;
|
||||||
|
gst_flv_demux_move_to_offset (demux, bytes, FALSE);
|
||||||
|
ret = GST_FLOW_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -702,10 +772,12 @@ gst_flv_demux_loop (GstPad * pad)
|
||||||
|
|
||||||
demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
|
demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
|
||||||
|
|
||||||
if (demux->segment.rate >= 0) {
|
if (TRUE || demux->segment.rate >= 0) {
|
||||||
/* pull in data */
|
/* pull in data */
|
||||||
switch (demux->state) {
|
switch (demux->state) {
|
||||||
case FLV_STATE_TAG_TYPE:
|
case FLV_STATE_TAG_TYPE:
|
||||||
|
if (demux->from_offset == -1)
|
||||||
|
demux->from_offset = demux->offset;
|
||||||
ret = gst_flv_demux_pull_tag (pad, demux);
|
ret = gst_flv_demux_pull_tag (pad, demux);
|
||||||
/* if we have seen real data, we probably passed a possible metadata
|
/* if we have seen real data, we probably passed a possible metadata
|
||||||
* header located at start. So if we do not yet have an index,
|
* header located at start. So if we do not yet have an index,
|
||||||
|
@ -738,16 +810,23 @@ gst_flv_demux_loop (GstPad * pad)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* pause if something went wrong */
|
if (demux->segment.rate < 0.0) {
|
||||||
|
/* check end of section */
|
||||||
|
if ((gint64) demux->offset >= demux->to_offset ||
|
||||||
|
demux->segment.last_stop >= demux->segment.stop + 2 * GST_SECOND ||
|
||||||
|
(demux->audio_done && demux->video_done))
|
||||||
|
ret = gst_flv_demux_seek_to_prev_keyframe (demux);
|
||||||
|
} else {
|
||||||
|
/* check EOS condition */
|
||||||
|
if ((demux->segment.stop != -1) &&
|
||||||
|
(demux->segment.last_stop >= demux->segment.stop)) {
|
||||||
|
ret = GST_FLOW_UNEXPECTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pause if something went wrong or at end */
|
||||||
if (G_UNLIKELY (ret != GST_FLOW_OK))
|
if (G_UNLIKELY (ret != GST_FLOW_OK))
|
||||||
goto pause;
|
goto pause;
|
||||||
|
|
||||||
/* check EOS condition */
|
|
||||||
if ((demux->segment.stop != -1) &&
|
|
||||||
(demux->segment.last_stop >= demux->segment.stop)) {
|
|
||||||
ret = GST_FLOW_UNEXPECTED;
|
|
||||||
goto pause;
|
|
||||||
}
|
|
||||||
} else { /* Reverse playback */
|
} else { /* Reverse playback */
|
||||||
/* pull in data */
|
/* pull in data */
|
||||||
switch (demux->state) {
|
switch (demux->state) {
|
||||||
|
@ -849,7 +928,7 @@ gst_flv_demux_find_offset (GstFLVDemux * demux, GstSegment * segment)
|
||||||
|
|
||||||
g_return_val_if_fail (segment != NULL, 0);
|
g_return_val_if_fail (segment != NULL, 0);
|
||||||
|
|
||||||
time = segment->start;
|
time = segment->last_stop;
|
||||||
|
|
||||||
if (demux->index) {
|
if (demux->index) {
|
||||||
/* Let's check if we have an index entry for that seek time */
|
/* Let's check if we have an index entry for that seek time */
|
||||||
|
@ -863,7 +942,7 @@ gst_flv_demux_find_offset (GstFLVDemux * demux, GstSegment * segment)
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (demux, "found index entry for %" GST_TIME_FORMAT
|
GST_DEBUG_OBJECT (demux, "found index entry for %" GST_TIME_FORMAT
|
||||||
" at %" GST_TIME_FORMAT ", seeking to %" G_GINT64_FORMAT,
|
" at %" GST_TIME_FORMAT ", seeking to %" G_GINT64_FORMAT,
|
||||||
GST_TIME_ARGS (segment->start), GST_TIME_ARGS (time), bytes);
|
GST_TIME_ARGS (segment->last_stop), GST_TIME_ARGS (time), bytes);
|
||||||
|
|
||||||
/* Key frame seeking */
|
/* Key frame seeking */
|
||||||
if (segment->flags & GST_SEEK_FLAG_KEY_UNIT) {
|
if (segment->flags & GST_SEEK_FLAG_KEY_UNIT) {
|
||||||
|
@ -1103,18 +1182,8 @@ gst_flv_demux_handle_seek_pull (GstFLVDemux * demux, GstEvent * event,
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
/* now index should be as reliable as it can be for current purpose */
|
/* now index should be as reliable as it can be for current purpose */
|
||||||
demux->offset = gst_flv_demux_find_offset (demux, &seeksegment);
|
gst_flv_demux_move_to_offset (demux,
|
||||||
|
gst_flv_demux_find_offset (demux, &seeksegment), TRUE);
|
||||||
/* Tell all the stream we moved to a different position (discont) */
|
|
||||||
demux->audio_need_discont = TRUE;
|
|
||||||
demux->video_need_discont = TRUE;
|
|
||||||
|
|
||||||
/* If we seeked at the beginning of the file parse the header again */
|
|
||||||
if (G_UNLIKELY (!demux->offset)) {
|
|
||||||
demux->state = FLV_STATE_HEADER;
|
|
||||||
} else { /* or parse a tag */
|
|
||||||
demux->state = FLV_STATE_TAG_TYPE;
|
|
||||||
}
|
|
||||||
ret = TRUE;
|
ret = TRUE;
|
||||||
} else {
|
} else {
|
||||||
ret = TRUE;
|
ret = TRUE;
|
||||||
|
@ -1171,6 +1240,18 @@ gst_flv_demux_handle_seek_pull (GstFLVDemux * demux, GstEvent * event,
|
||||||
gst_event_unref (demux->new_seg_event);
|
gst_event_unref (demux->new_seg_event);
|
||||||
demux->new_seg_event = NULL;
|
demux->new_seg_event = NULL;
|
||||||
}
|
}
|
||||||
|
if (demux->segment.rate < 0.0) {
|
||||||
|
/* we can't generate a segment by locking on
|
||||||
|
* to the first timestamp we see */
|
||||||
|
GST_DEBUG_OBJECT (demux, "preparing newsegment from %"
|
||||||
|
GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
|
||||||
|
GST_TIME_ARGS (demux->segment.start),
|
||||||
|
GST_TIME_ARGS (demux->segment.stop));
|
||||||
|
demux->new_seg_event =
|
||||||
|
gst_event_new_new_segment (FALSE, demux->segment.rate,
|
||||||
|
demux->segment.format, demux->segment.start,
|
||||||
|
demux->segment.stop, demux->segment.start);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
|
|
|
@ -129,6 +129,14 @@ struct _GstFLVDemux
|
||||||
|
|
||||||
GstClockTime index_max_time;
|
GstClockTime index_max_time;
|
||||||
gint64 index_max_pos;
|
gint64 index_max_pos;
|
||||||
|
|
||||||
|
/* reverse playback */
|
||||||
|
GstClockTime video_first_ts;
|
||||||
|
GstClockTime audio_first_ts;
|
||||||
|
gboolean video_done;
|
||||||
|
gboolean audio_done;
|
||||||
|
gint64 from_offset;
|
||||||
|
gint64 to_offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstFLVDemuxClass
|
struct _GstFLVDemuxClass
|
||||||
|
|
|
@ -856,6 +856,9 @@ gst_flv_parse_tag_audio (GstFLVDemux * demux, GstBuffer * buffer)
|
||||||
if (!GST_CLOCK_TIME_IS_VALID (demux->audio_start)) {
|
if (!GST_CLOCK_TIME_IS_VALID (demux->audio_start)) {
|
||||||
demux->audio_start = GST_BUFFER_TIMESTAMP (outbuf);
|
demux->audio_start = GST_BUFFER_TIMESTAMP (outbuf);
|
||||||
}
|
}
|
||||||
|
if (!GST_CLOCK_TIME_IS_VALID (demux->audio_first_ts)) {
|
||||||
|
demux->audio_first_ts = GST_BUFFER_TIMESTAMP (outbuf);
|
||||||
|
}
|
||||||
|
|
||||||
if (G_UNLIKELY (!demux->no_more_pads
|
if (G_UNLIKELY (!demux->no_more_pads
|
||||||
&& (GST_CLOCK_DIFF (demux->audio_start,
|
&& (GST_CLOCK_DIFF (demux->audio_start,
|
||||||
|
@ -870,13 +873,23 @@ gst_flv_parse_tag_audio (GstFLVDemux * demux, GstBuffer * buffer)
|
||||||
/* Push downstream */
|
/* Push downstream */
|
||||||
ret = gst_pad_push (demux->audio_pad, outbuf);
|
ret = gst_pad_push (demux->audio_pad, outbuf);
|
||||||
if (G_UNLIKELY (ret != GST_FLOW_OK)) {
|
if (G_UNLIKELY (ret != GST_FLOW_OK)) {
|
||||||
GST_WARNING_OBJECT (demux, "failed pushing a %" G_GUINT64_FORMAT
|
if (demux->segment.rate < 0.0 && ret == GST_FLOW_UNEXPECTED &&
|
||||||
" bytes audio buffer: %s", demux->tag_data_size,
|
demux->segment.last_stop > demux->segment.stop) {
|
||||||
gst_flow_get_name (ret));
|
/* In reverse playback we can get a GST_FLOW_UNEXPECTED when
|
||||||
if (ret == GST_FLOW_NOT_LINKED) {
|
* we are at the end of the segment, so we just need to jump
|
||||||
demux->audio_linked = FALSE;
|
* back to the previous section. */
|
||||||
|
GST_DEBUG_OBJECT (demux, "downstream has reached end of segment");
|
||||||
|
demux->audio_done = TRUE;
|
||||||
|
ret = GST_FLOW_OK;
|
||||||
|
} else {
|
||||||
|
GST_WARNING_OBJECT (demux, "failed pushing a %" G_GUINT64_FORMAT
|
||||||
|
" bytes audio buffer: %s", demux->tag_data_size,
|
||||||
|
gst_flow_get_name (ret));
|
||||||
|
if (ret == GST_FLOW_NOT_LINKED) {
|
||||||
|
demux->audio_linked = FALSE;
|
||||||
|
}
|
||||||
|
goto beach;
|
||||||
}
|
}
|
||||||
goto beach;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
demux->audio_linked = TRUE;
|
demux->audio_linked = TRUE;
|
||||||
|
@ -1192,6 +1205,9 @@ gst_flv_parse_tag_video (GstFLVDemux * demux, GstBuffer * buffer)
|
||||||
if (!GST_CLOCK_TIME_IS_VALID (demux->video_start)) {
|
if (!GST_CLOCK_TIME_IS_VALID (demux->video_start)) {
|
||||||
demux->video_start = GST_BUFFER_TIMESTAMP (outbuf);
|
demux->video_start = GST_BUFFER_TIMESTAMP (outbuf);
|
||||||
}
|
}
|
||||||
|
if (!GST_CLOCK_TIME_IS_VALID (demux->audio_first_ts)) {
|
||||||
|
demux->video_first_ts = GST_BUFFER_TIMESTAMP (outbuf);
|
||||||
|
}
|
||||||
|
|
||||||
if (G_UNLIKELY (!demux->no_more_pads
|
if (G_UNLIKELY (!demux->no_more_pads
|
||||||
&& (GST_CLOCK_DIFF (demux->video_start,
|
&& (GST_CLOCK_DIFF (demux->video_start,
|
||||||
|
@ -1207,13 +1223,23 @@ gst_flv_parse_tag_video (GstFLVDemux * demux, GstBuffer * buffer)
|
||||||
ret = gst_pad_push (demux->video_pad, outbuf);
|
ret = gst_pad_push (demux->video_pad, outbuf);
|
||||||
|
|
||||||
if (G_UNLIKELY (ret != GST_FLOW_OK)) {
|
if (G_UNLIKELY (ret != GST_FLOW_OK)) {
|
||||||
GST_WARNING_OBJECT (demux, "failed pushing a %" G_GUINT64_FORMAT
|
if (demux->segment.rate < 0.0 && ret == GST_FLOW_UNEXPECTED &&
|
||||||
" bytes video buffer: %s", demux->tag_data_size,
|
demux->segment.last_stop > demux->segment.stop) {
|
||||||
gst_flow_get_name (ret));
|
/* In reverse playback we can get a GST_FLOW_UNEXPECTED when
|
||||||
if (ret == GST_FLOW_NOT_LINKED) {
|
* we are at the end of the segment, so we just need to jump
|
||||||
demux->video_linked = FALSE;
|
* back to the previous section. */
|
||||||
|
GST_DEBUG_OBJECT (demux, "downstream has reached end of segment");
|
||||||
|
demux->video_done = TRUE;
|
||||||
|
ret = GST_FLOW_OK;
|
||||||
|
} else {
|
||||||
|
GST_WARNING_OBJECT (demux, "failed pushing a %" G_GUINT64_FORMAT
|
||||||
|
" bytes video buffer: %s", demux->tag_data_size,
|
||||||
|
gst_flow_get_name (ret));
|
||||||
|
if (ret == GST_FLOW_NOT_LINKED) {
|
||||||
|
demux->video_linked = FALSE;
|
||||||
|
}
|
||||||
|
goto beach;
|
||||||
}
|
}
|
||||||
goto beach;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
demux->video_linked = TRUE;
|
demux->video_linked = TRUE;
|
||||||
|
|
Loading…
Reference in a new issue