mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-22 00:06:36 +00:00
gst/flv/gstflvdemux.c: Implement seeking in push mode.
Original commit message from CVS: 2007-08-24 Julien MOUTTE <julien@moutte.net> * gst/flv/gstflvdemux.c: (gst_flv_demux_flush), (gst_flv_demux_cleanup), (gst_flv_demux_chain), (gst_flv_demux_pull_tag), (gst_flv_demux_find_offset), (gst_flv_demux_handle_seek_push), (gst_flv_demux_handle_seek_pull), (gst_flv_demux_sink_event), (gst_flv_demux_src_event): Implement seeking in push mode. * gst/flv/gstflvdemux.h:
This commit is contained in:
parent
b4c31f289b
commit
b86971338f
3 changed files with 176 additions and 35 deletions
10
ChangeLog
10
ChangeLog
|
@ -1,3 +1,13 @@
|
||||||
|
2007-08-24 Julien MOUTTE <julien@moutte.net>
|
||||||
|
|
||||||
|
* gst/flv/gstflvdemux.c: (gst_flv_demux_flush),
|
||||||
|
(gst_flv_demux_cleanup), (gst_flv_demux_chain),
|
||||||
|
(gst_flv_demux_pull_tag), (gst_flv_demux_find_offset),
|
||||||
|
(gst_flv_demux_handle_seek_push), (gst_flv_demux_handle_seek_pull),
|
||||||
|
(gst_flv_demux_sink_event), (gst_flv_demux_src_event): Implement
|
||||||
|
seeking in push mode.
|
||||||
|
* gst/flv/gstflvdemux.h:
|
||||||
|
|
||||||
2007-08-24 Zaheer Abbas Merali <zaheerabbas at merali dot org>
|
2007-08-24 Zaheer Abbas Merali <zaheerabbas at merali dot org>
|
||||||
|
|
||||||
* gst/mpegtsparse/flutspatinfo.c:
|
* gst/mpegtsparse/flutspatinfo.c:
|
||||||
|
|
|
@ -68,6 +68,16 @@ gst_flv_demux_flush (GstFLVDemux * demux, gboolean discont)
|
||||||
|
|
||||||
demux->audio_need_discont = TRUE;
|
demux->audio_need_discont = TRUE;
|
||||||
demux->video_need_discont = TRUE;
|
demux->video_need_discont = TRUE;
|
||||||
|
|
||||||
|
demux->flushing = FALSE;
|
||||||
|
|
||||||
|
/* Only in push mode */
|
||||||
|
if (!demux->random_access) {
|
||||||
|
/* After a flush we expect a tag_type */
|
||||||
|
demux->state = FLV_STATE_TAG_TYPE;
|
||||||
|
/* We reset the offset and will get one from first push */
|
||||||
|
demux->offset = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -77,6 +87,7 @@ gst_flv_demux_cleanup (GstFLVDemux * demux)
|
||||||
|
|
||||||
demux->state = FLV_STATE_HEADER;
|
demux->state = FLV_STATE_HEADER;
|
||||||
|
|
||||||
|
demux->flushing = FALSE;
|
||||||
demux->need_header = TRUE;
|
demux->need_header = TRUE;
|
||||||
demux->audio_need_segment = TRUE;
|
demux->audio_need_segment = TRUE;
|
||||||
demux->video_need_segment = TRUE;
|
demux->video_need_segment = TRUE;
|
||||||
|
@ -146,9 +157,28 @@ gst_flv_demux_chain (GstPad * pad, GstBuffer * buffer)
|
||||||
|
|
||||||
demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
|
demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (demux, "received buffer of %d bytes at offset %"
|
||||||
|
G_GUINT64_FORMAT, GST_BUFFER_SIZE (buffer), GST_BUFFER_OFFSET (buffer));
|
||||||
|
|
||||||
|
if (G_UNLIKELY (GST_BUFFER_OFFSET (buffer) == 0)) {
|
||||||
|
GST_DEBUG_OBJECT (demux, "beginning of file, expect header");
|
||||||
|
demux->state = FLV_STATE_HEADER;
|
||||||
|
demux->offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (G_UNLIKELY (demux->offset == 0)) {
|
||||||
|
GST_DEBUG_OBJECT (demux, "offset was zero, synchronizing with buffer's");
|
||||||
|
demux->offset = GST_BUFFER_OFFSET (buffer);
|
||||||
|
}
|
||||||
|
|
||||||
gst_adapter_push (demux->adapter, buffer);
|
gst_adapter_push (demux->adapter, buffer);
|
||||||
|
|
||||||
parse:
|
parse:
|
||||||
|
if (G_UNLIKELY (demux->flushing)) {
|
||||||
|
GST_DEBUG_OBJECT (demux, "we are now flushing, exiting parser loop");
|
||||||
|
ret = GST_FLOW_WRONG_STATE;
|
||||||
|
goto beach;
|
||||||
|
}
|
||||||
switch (demux->state) {
|
switch (demux->state) {
|
||||||
case FLV_STATE_HEADER:
|
case FLV_STATE_HEADER:
|
||||||
{
|
{
|
||||||
|
@ -502,52 +532,134 @@ pause:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static guint64
|
||||||
gst_flv_demux_do_seek (GstFLVDemux * demux, GstSegment * segment)
|
gst_flv_demux_find_offset (GstFLVDemux * demux, GstSegment * segment)
|
||||||
{
|
{
|
||||||
gboolean ret = FALSE;
|
gint64 bytes = 0;
|
||||||
|
gint64 time = 0;
|
||||||
GstIndexEntry *entry;
|
GstIndexEntry *entry;
|
||||||
|
|
||||||
|
g_return_val_if_fail (segment != NULL, 0);
|
||||||
|
|
||||||
|
time = segment->start;
|
||||||
|
|
||||||
|
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 */
|
||||||
entry = gst_index_get_assoc_entry (demux->index, demux->index_id,
|
entry = gst_index_get_assoc_entry (demux->index, demux->index_id,
|
||||||
GST_INDEX_LOOKUP_BEFORE, GST_ASSOCIATION_FLAG_KEY_UNIT, GST_FORMAT_TIME,
|
GST_INDEX_LOOKUP_BEFORE, GST_ASSOCIATION_FLAG_KEY_UNIT, GST_FORMAT_TIME,
|
||||||
segment->start);
|
time);
|
||||||
|
|
||||||
if (entry) {
|
if (entry) {
|
||||||
gint64 bytes = 0;
|
|
||||||
gint64 time = 0;
|
|
||||||
|
|
||||||
gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &bytes);
|
gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &bytes);
|
||||||
gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &time);
|
gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &time);
|
||||||
|
|
||||||
demux->offset = bytes;
|
|
||||||
demux->state = FLV_STATE_TAG_TYPE;
|
|
||||||
|
|
||||||
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->start), GST_TIME_ARGS (time), bytes);
|
||||||
|
|
||||||
/* Key frame seeking */
|
/* Key frame seeking */
|
||||||
if (demux->segment->flags & GST_SEEK_FLAG_KEY_UNIT) {
|
if (segment->flags & GST_SEEK_FLAG_KEY_UNIT) {
|
||||||
/* Adjust the segment so that the keyframe fits in */
|
/* Adjust the segment so that the keyframe fits in */
|
||||||
if (time < segment->start) {
|
if (time < segment->start) {
|
||||||
segment->start = segment->time = time;
|
segment->start = segment->time = time;
|
||||||
}
|
}
|
||||||
segment->last_stop = time;
|
segment->last_stop = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = TRUE;
|
|
||||||
} else {
|
} else {
|
||||||
GST_DEBUG_OBJECT (demux, "no index entry found for %" GST_TIME_FORMAT,
|
GST_DEBUG_OBJECT (demux, "no index entry found for %" GST_TIME_FORMAT,
|
||||||
GST_TIME_ARGS (segment->start));
|
GST_TIME_ARGS (segment->start));
|
||||||
ret = FALSE;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_flv_demux_handle_seek (GstFLVDemux * demux, GstEvent * event)
|
gst_flv_demux_handle_seek_push (GstFLVDemux * demux, GstEvent * event)
|
||||||
|
{
|
||||||
|
GstFormat format;
|
||||||
|
GstSeekFlags flags;
|
||||||
|
GstSeekType start_type, stop_type;
|
||||||
|
gint64 start, stop;
|
||||||
|
gdouble rate;
|
||||||
|
gboolean update, flush, keyframe, ret;
|
||||||
|
GstSegment seeksegment;
|
||||||
|
|
||||||
|
gst_event_parse_seek (event, &rate, &format, &flags,
|
||||||
|
&start_type, &start, &stop_type, &stop);
|
||||||
|
|
||||||
|
if (format != GST_FORMAT_TIME)
|
||||||
|
goto wrong_format;
|
||||||
|
|
||||||
|
flush = flags & GST_SEEK_FLAG_FLUSH;
|
||||||
|
keyframe = flags & GST_SEEK_FLAG_KEY_UNIT;
|
||||||
|
|
||||||
|
/* Work on a copy until we are sure the seek succeeded. */
|
||||||
|
memcpy (&seeksegment, demux->segment, sizeof (GstSegment));
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (demux, "segment before configure %" GST_SEGMENT_FORMAT,
|
||||||
|
demux->segment);
|
||||||
|
|
||||||
|
/* Apply the seek to our segment */
|
||||||
|
gst_segment_set_seek (&seeksegment, rate, format, flags,
|
||||||
|
start_type, start, stop_type, stop, &update);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (demux, "segment configured %" GST_SEGMENT_FORMAT,
|
||||||
|
&seeksegment);
|
||||||
|
|
||||||
|
if (flush || seeksegment.last_stop != demux->segment->last_stop) {
|
||||||
|
/* Do the actual seeking */
|
||||||
|
guint64 offset = gst_flv_demux_find_offset (demux, &seeksegment);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (demux, "generating an upstream seek at position %"
|
||||||
|
G_GUINT64_FORMAT, offset);
|
||||||
|
ret = gst_pad_push_event (demux->sinkpad,
|
||||||
|
gst_event_new_seek (seeksegment.rate, GST_FORMAT_BYTES,
|
||||||
|
GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET,
|
||||||
|
offset, GST_SEEK_TYPE_NONE, 0));
|
||||||
|
if (G_UNLIKELY (!ret)) {
|
||||||
|
GST_WARNING_OBJECT (demux, "upstream seek failed");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
/* Ok seek succeeded, take the newly configured segment */
|
||||||
|
memcpy (demux->segment, &seeksegment, sizeof (GstSegment));
|
||||||
|
|
||||||
|
/* Notify about the start of a new segment */
|
||||||
|
if (demux->segment->flags & GST_SEEK_FLAG_SEGMENT) {
|
||||||
|
gst_element_post_message (GST_ELEMENT (demux),
|
||||||
|
gst_message_new_segment_start (GST_OBJECT (demux),
|
||||||
|
demux->segment->format, demux->segment->last_stop));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tell all the stream a new segment is needed */
|
||||||
|
{
|
||||||
|
demux->audio_need_segment = TRUE;
|
||||||
|
demux->video_need_segment = TRUE;
|
||||||
|
/* Clean any potential newsegment event kept for the streams. The first
|
||||||
|
* stream needing a new segment will create a new one. */
|
||||||
|
if (G_UNLIKELY (demux->new_seg_event)) {
|
||||||
|
gst_event_unref (demux->new_seg_event);
|
||||||
|
demux->new_seg_event = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
wrong_format:
|
||||||
|
{
|
||||||
|
GST_WARNING_OBJECT (demux, "we only support seeking in TIME format");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_flv_demux_handle_seek_pull (GstFLVDemux * demux, GstEvent * event)
|
||||||
{
|
{
|
||||||
GstFormat format;
|
GstFormat format;
|
||||||
GstSeekFlags flags;
|
GstSeekFlags flags;
|
||||||
|
@ -599,7 +711,14 @@ gst_flv_demux_handle_seek (GstFLVDemux * demux, GstEvent * event)
|
||||||
|
|
||||||
if (flush || seeksegment.last_stop != demux->segment->last_stop) {
|
if (flush || seeksegment.last_stop != demux->segment->last_stop) {
|
||||||
/* Do the actual seeking */
|
/* Do the actual seeking */
|
||||||
ret = gst_flv_demux_do_seek (demux, &seeksegment);
|
demux->offset = gst_flv_demux_find_offset (demux, &seeksegment);
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ret = TRUE;
|
ret = TRUE;
|
||||||
}
|
}
|
||||||
|
@ -728,12 +847,17 @@ gst_flv_demux_sink_event (GstPad * pad, GstEvent * event)
|
||||||
|
|
||||||
demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
|
demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (demux, "handling event %s", GST_EVENT_TYPE_NAME (event));
|
||||||
|
|
||||||
switch (GST_EVENT_TYPE (event)) {
|
switch (GST_EVENT_TYPE (event)) {
|
||||||
|
case GST_EVENT_FLUSH_START:
|
||||||
|
GST_DEBUG_OBJECT (demux, "trying to force chain function to exit");
|
||||||
|
demux->flushing = TRUE;
|
||||||
|
ret = gst_pad_event_default (demux->sinkpad, event);
|
||||||
|
break;
|
||||||
case GST_EVENT_FLUSH_STOP:
|
case GST_EVENT_FLUSH_STOP:
|
||||||
GST_DEBUG_OBJECT (demux, "flushing FLV demuxer");
|
GST_DEBUG_OBJECT (demux, "flushing FLV demuxer");
|
||||||
gst_flv_demux_flush (demux, TRUE);
|
gst_flv_demux_flush (demux, TRUE);
|
||||||
gst_adapter_clear (demux->adapter);
|
|
||||||
gst_segment_init (demux->segment, GST_FORMAT_TIME);
|
|
||||||
ret = gst_pad_event_default (demux->sinkpad, event);
|
ret = gst_pad_event_default (demux->sinkpad, event);
|
||||||
break;
|
break;
|
||||||
case GST_EVENT_EOS:
|
case GST_EVENT_EOS:
|
||||||
|
@ -803,9 +927,15 @@ gst_flv_demux_src_event (GstPad * pad, GstEvent * event)
|
||||||
|
|
||||||
demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
|
demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (demux, "handling event %s", GST_EVENT_TYPE_NAME (event));
|
||||||
|
|
||||||
switch (GST_EVENT_TYPE (event)) {
|
switch (GST_EVENT_TYPE (event)) {
|
||||||
case GST_EVENT_SEEK:
|
case GST_EVENT_SEEK:
|
||||||
ret = gst_flv_demux_handle_seek (demux, event);
|
if (demux->random_access) {
|
||||||
|
ret = gst_flv_demux_handle_seek_pull (demux, event);
|
||||||
|
} else {
|
||||||
|
ret = gst_flv_demux_handle_seek_push (demux, event);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ret = gst_pad_push_event (demux->sinkpad, event);
|
ret = gst_pad_push_event (demux->sinkpad, event);
|
||||||
|
|
|
@ -107,6 +107,7 @@ struct _GstFLVDemux
|
||||||
gboolean has_video;
|
gboolean has_video;
|
||||||
gboolean push_tags;
|
gboolean push_tags;
|
||||||
gboolean strict;
|
gboolean strict;
|
||||||
|
gboolean flushing;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstFLVDemuxClass
|
struct _GstFLVDemuxClass
|
||||||
|
|
Loading…
Reference in a new issue