From ff7b54e7da95a401665f8d7ae922155a32edac91 Mon Sep 17 00:00:00 2001 From: Zaheer Merali Date: Fri, 8 May 2009 18:24:28 +0100 Subject: mpegtsdemux: Add initial naive seeking support and fix duration query. Sync from gst-fluendo-mpegdemux and have seeking/duration query improvements in. No support however for wrapped around pcrs etc. but a start nonetheless. Also fix indentation issues. --- gst/mpegdemux/gstmpegtsdemux.c | 272 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 243 insertions(+), 29 deletions(-) (limited to 'gst/mpegdemux/gstmpegtsdemux.c') diff --git a/gst/mpegdemux/gstmpegtsdemux.c b/gst/mpegdemux/gstmpegtsdemux.c index 4464cdf8..99a499a0 100644 --- a/gst/mpegdemux/gstmpegtsdemux.c +++ b/gst/mpegdemux/gstmpegtsdemux.c @@ -34,9 +34,9 @@ * * The Original Code is Fluendo MPEG Demuxer plugin. * - * The Initial Developer of the Original Code is Fluendo, S.L. - * Portions created by Fluendo, S.L. are Copyright (C) 2005 - * Fluendo, S.L. All Rights Reserved. + * The Initial Developer of the Original Code is Fluendo, S.A. + * Portions created by Fluendo, S.L. are Copyright (C) 2005,2006,2007,2008,2009 + * Fluendo, S.A. All Rights Reserved. * * Contributor(s): Wim Taymans */ @@ -126,6 +126,13 @@ enum PROP_M2TS }; +#define GSTTIME_TO_BYTES(time) \ + ((time != -1) ? gst_util_uint64_scale (MAX(0,(gint64) ((time))), \ + demux->bitrate, GST_SECOND) : -1) +#define BYTES_TO_GSTTIME(bytes) \ + ((bytes != -1) ? (gst_util_uint64_scale (bytes, GST_SECOND, \ + demux->bitrate)) : -1) + #define VIDEO_CAPS \ GST_STATIC_CAPS (\ "video/mpeg, " \ @@ -189,6 +196,7 @@ static void gst_mpegts_demux_get_property (GObject * object, guint prop_id, static gboolean gst_mpegts_demux_is_PMT (GstMpegTSDemux * demux, guint16 PID); static gboolean gst_mpegts_demux_sink_event (GstPad * pad, GstEvent * event); +static gboolean gst_mpegts_demux_src_event (GstPad * pad, GstEvent * event); static GstFlowReturn gst_mpegts_demux_chain (GstPad * pad, GstBuffer * buffer); static gboolean gst_mpegts_demux_sink_setcaps (GstPad * pad, GstCaps * caps); @@ -327,7 +335,12 @@ gst_mpegts_demux_init (GstMpegTSDemux * demux) demux->m2ts_mode = FALSE; demux->sync_lut = NULL; demux->sync_lut_len = 0; - + demux->bitrate = -1; + demux->num_packets = 0; + demux->pcr[0] = -1; + demux->pcr[1] = -1; + demux->cache_duration = GST_CLOCK_TIME_NONE; + demux->base_pts = GST_CLOCK_TIME_NONE; #ifdef USE_LIBOIL oil_init (); #endif @@ -668,6 +681,8 @@ gst_mpegts_demux_fill_stream (GstMpegTSStream * stream, guint8 id, gst_caps_unref (caps); gst_pad_set_query_function (stream->pad, GST_DEBUG_FUNCPTR (gst_mpegts_demux_src_pad_query)); + gst_pad_set_event_function (stream->pad, + GST_DEBUG_FUNCPTR (gst_mpegts_demux_src_event)); g_free (name); return TRUE; @@ -730,7 +745,7 @@ gst_mpegts_demux_send_new_segment (GstMpegTSDemux * demux, } base_PCR = PCR_stream->base_PCR; - time = MPEGTIME_TO_GSTTIME (base_PCR); + demux->base_pts = time = MPEGTIME_TO_GSTTIME (base_PCR); GST_DEBUG_OBJECT (demux, "segment PTS to (%" G_GUINT64_FORMAT ") time: %" G_GUINT64_FORMAT, base_PCR, time); @@ -1680,7 +1695,13 @@ gst_mpegts_demux_parse_adaptation_field (GstMpegTSStream * stream, "valid pcr: %d last PCR difference: %" G_GUINT64_FORMAT, valid_pcr, stream->last_PCR_difference); if (valid_pcr) { - + if (demux->pcr[0] == -1) { + demux->pcr[0] = pcr; + demux->num_packets = 0; + } /* Considering a difference of 1 sec ie 90000 ticks */ + else if (demux->pcr[1] == -1 && ((pcr - demux->pcr[0]) >= 90000)) { + demux->pcr[1] = pcr; + } stream->last_PCR = pcr; if (demux->clock && demux->clock_base != GST_CLOCK_TIME_NONE) { @@ -1955,15 +1976,20 @@ gst_mpegts_demux_is_PMT (GstMpegTSDemux * demux, guint16 PID) } static FORCE_INLINE GstFlowReturn -gst_mpegts_stream_pes_buffer_flush (GstMpegTSStream * stream) +gst_mpegts_stream_pes_buffer_flush (GstMpegTSStream * stream, gboolean discard) { GstFlowReturn ret = GST_FLOW_OK; if (stream->pes_buffer) { - GST_BUFFER_SIZE (stream->pes_buffer) = stream->pes_buffer_used; - ret = gst_pes_filter_push (&stream->filter, stream->pes_buffer); - if (ret == GST_FLOW_LOST_SYNC) + if (discard) { + gst_buffer_unref (stream->pes_buffer); stream->pes_buffer_in_sync = FALSE; + } else { + GST_BUFFER_SIZE (stream->pes_buffer) = stream->pes_buffer_used; + ret = gst_pes_filter_push (&stream->filter, stream->pes_buffer); + if (ret == GST_FLOW_LOST_SYNC) + stream->pes_buffer_in_sync = FALSE; + } stream->pes_buffer = NULL; } return ret; @@ -1985,7 +2011,7 @@ gst_mpegts_stream_pes_buffer_push (GstMpegTSStream * stream, if (stream->pes_buffer_size < (MPEGTS_MAX_PES_BUFFER_SIZE >> 1)) stream->pes_buffer_size <<= 1; - ret = gst_mpegts_stream_pes_buffer_flush (stream); + ret = gst_mpegts_stream_pes_buffer_flush (stream, FALSE); if (ret == GST_FLOW_LOST_SYNC) goto done; } @@ -2010,7 +2036,7 @@ done: } static FORCE_INLINE GstFlowReturn -gst_mpegts_demux_pes_buffer_flush (GstMpegTSDemux * demux) +gst_mpegts_demux_pes_buffer_flush (GstMpegTSDemux * demux, gboolean discard) { gint i; GstFlowReturn ret = GST_FLOW_OK; @@ -2018,7 +2044,7 @@ gst_mpegts_demux_pes_buffer_flush (GstMpegTSDemux * demux) for (i = 0; i < MPEGTS_MAX_PID + 1; i++) { GstMpegTSStream *stream = demux->streams[i]; if (stream && stream->pad) { - gst_mpegts_stream_pes_buffer_flush (stream); + gst_mpegts_stream_pes_buffer_flush (stream, discard); stream->pes_buffer_in_sync = FALSE; } } @@ -2233,7 +2259,7 @@ gst_mpegts_demux_parse_stream (GstMpegTSDemux * demux, GstMpegTSStream * stream, "bytes of %u bytes in the PES buffer", PID, stream->pes_buffer_used, stream->pes_buffer_size); /* Flush buffered PES data */ - gst_mpegts_stream_pes_buffer_flush (stream); + gst_mpegts_stream_pes_buffer_flush (stream, FALSE); gst_pes_filter_drain (&stream->filter); /* Resize the buffer to half if no overflow detected and * had been used less than half of it */ @@ -2319,11 +2345,137 @@ gst_mpegts_demux_parse_transport_packet (GstMpegTSDemux * demux, ret = gst_mpegts_demux_parse_stream (demux, stream, data, MPEGTS_NORMAL_TS_PACKETSIZE - 1); + if (demux->pcr[1] != -1 && demux->bitrate == -1) { + GST_DEBUG_OBJECT (demux, "stream->last_PCR_difference: %" G_GINT64_FORMAT + ", demux->num_packets %" G_GUINT64_FORMAT, + demux->pcr[1] - demux->pcr[0], demux->num_packets); + demux->bitrate = gst_util_uint64_scale (GST_SECOND, + MPEGTS_NORMAL_TS_PACKETSIZE * demux->num_packets, + MPEGTIME_TO_GSTTIME (demux->pcr[1] - demux->pcr[0])); + GST_DEBUG_OBJECT (demux, "bitrate is %" G_GINT64_FORMAT + " bytes per second", demux->bitrate); + } + demux->num_packets++; return ret; /* ERRORS */ } +static gboolean +gst_mpegts_demux_handle_seek_push (GstMpegTSDemux * demux, GstEvent * event) +{ + gboolean res = FALSE; + gdouble rate; + GstFormat format; + GstSeekFlags flags; + GstSeekType start_type, stop_type; + gint64 start, stop, bstart, bstop; + GstEvent *bevent; + + gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start, + &stop_type, &stop); + + GST_DEBUG_OBJECT (demux, "seek event, rate: %f start: %" GST_TIME_FORMAT + " stop: %" GST_TIME_FORMAT, rate, GST_TIME_ARGS (start), + GST_TIME_ARGS (stop)); + + if (format == GST_FORMAT_BYTES) { + GST_DEBUG_OBJECT (demux, "seek not supported on format %d", format); + goto beach; + } + + GST_DEBUG_OBJECT (demux, "seek - trying directly upstream first"); + + /* first try original format seek */ + res = gst_pad_push_event (demux->sinkpad, gst_event_ref (event)); + if (res == TRUE) + goto beach; + GST_DEBUG_OBJECT (demux, "seek - no upstream"); + + if (format != GST_FORMAT_TIME) { + /* From here down, we only support time based seeks */ + GST_DEBUG_OBJECT (demux, "seek not supported on format %d", format); + goto beach; + } + + /* We need to convert to byte based seek and we need a scr_rate for that. */ + if (demux->bitrate == -1) { + GST_DEBUG_OBJECT (demux, "seek not possible, no bitrate"); + goto beach; + } + + GST_DEBUG_OBJECT (demux, "try with bitrate"); + + bstart = GSTTIME_TO_BYTES (start); + bstop = GSTTIME_TO_BYTES (stop); + + GST_DEBUG_OBJECT (demux, "in bytes bstart %" G_GINT64_FORMAT " bstop %" + G_GINT64_FORMAT, bstart, bstop); + bevent = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, start_type, + bstart, stop_type, bstop); + + res = gst_pad_push_event (demux->sinkpad, bevent); + +beach: + gst_event_unref (event); + return res; +} + +static gboolean +gst_mpegts_demux_src_event (GstPad * pad, GstEvent * event) +{ + GstMpegTSDemux *demux = GST_MPEGTS_DEMUX (gst_pad_get_parent (pad)); + gboolean res = FALSE; + + GST_DEBUG_OBJECT (demux, "got event %s", + gst_event_type_get_name (GST_EVENT_TYPE (event))); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + res = gst_mpegts_demux_handle_seek_push (demux, event); + break; + default: + res = gst_pad_push_event (demux->sinkpad, event); + break; + } + + gst_object_unref (demux); + + return res; +} + +static void +gst_mpegts_demux_flush (GstMpegTSDemux * demux, gboolean discard) +{ + GstMpegTSStream *PCR_stream; + GstMpegTSStream *PMT_stream; + + GST_DEBUG_OBJECT (demux, "flushing MPEG TS demuxer (discard %d)", discard); + + /* Start by flushing internal buffers */ + gst_mpegts_demux_pes_buffer_flush (demux, discard); + + /* Clear adapter */ + gst_adapter_clear (demux->adapter); + + /* Try resetting the last_PCR value as we will have a discont */ + if (demux->current_PMT == 0) + goto beach; + + PMT_stream = demux->streams[demux->current_PMT]; + if (PMT_stream == NULL) + goto beach; + + PCR_stream = demux->streams[PMT_stream->PMT.PCR_PID]; + if (PCR_stream == NULL) + goto beach; + + PCR_stream->last_PCR = -1; + +beach: + return; +} + static gboolean gst_mpegts_demux_send_event (GstMpegTSDemux * demux, GstEvent * event) { @@ -2347,7 +2499,7 @@ static gboolean gst_mpegts_demux_sink_event (GstPad * pad, GstEvent * event) { GstMpegTSDemux *demux = GST_MPEGTS_DEMUX (gst_pad_get_parent (pad)); - gboolean res; + gboolean res = FALSE; GST_DEBUG_OBJECT (demux, "got event %s", gst_event_type_get_name (GST_EVENT_TYPE (event))); @@ -2358,11 +2510,11 @@ gst_mpegts_demux_sink_event (GstPad * pad, GstEvent * event) break; case GST_EVENT_FLUSH_STOP: gst_adapter_clear (demux->adapter); + gst_mpegts_demux_flush (demux, TRUE); res = gst_mpegts_demux_send_event (demux, event); break; case GST_EVENT_EOS: - /* Flush buffered PES data */ - gst_mpegts_demux_pes_buffer_flush (demux); + gst_mpegts_demux_flush (demux, FALSE); /* Send the EOS event on each stream */ if (!(res = gst_mpegts_demux_send_event (demux, event))) { /* we have no streams */ @@ -2371,15 +2523,46 @@ gst_mpegts_demux_sink_event (GstPad * pad, GstEvent * event) } break; case GST_EVENT_NEWSEGMENT: - res = gst_mpegts_demux_send_event (demux, event); + { + gboolean update; + gdouble rate; + GstFormat format; + gint64 start, stop, time; + + gst_event_parse_new_segment (event, &update, &rate, &format, + &start, &stop, &time); + + gst_event_unref (event); + GST_INFO_OBJECT (demux, "received new segment: rate %g " + "format %d, start: %" G_GINT64_FORMAT ", stop: %" G_GINT64_FORMAT + ", time: %" G_GINT64_FORMAT, rate, format, start, stop, time); + if (format == GST_FORMAT_BYTES && demux->bitrate != -1) { + gint64 tstart = 0, tstop = 0, pos = 0; + + if (demux->base_pts != GST_CLOCK_TIME_NONE) { + tstart = tstop = demux->base_pts; + } + tstart += BYTES_TO_GSTTIME (start); + tstop += BYTES_TO_GSTTIME (stop); + pos = BYTES_TO_GSTTIME (time); + + event = gst_event_new_new_segment (update, rate, + GST_FORMAT_TIME, tstart, tstop, pos); + GST_DEBUG_OBJECT (demux, "pushing time newsegment from %" + GST_TIME_FORMAT " to %" GST_TIME_FORMAT " pos %" GST_TIME_FORMAT, + GST_TIME_ARGS (tstart), GST_TIME_ARGS (tstop), GST_TIME_ARGS (pos)); + + res = gst_mpegts_demux_send_event (demux, event); + } break; + } default: res = gst_mpegts_demux_send_event (demux, event); break; } gst_object_unref (demux); - return TRUE; + return res; } static gboolean @@ -2455,21 +2638,54 @@ gst_mpegts_demux_src_pad_query (GstPad * pad, GstQuery * query) case GST_QUERY_DURATION: { GstFormat format; - gint64 duration; + GstPad *peer; - gst_query_parse_duration (query, &format, &duration); + gst_query_parse_duration (query, &format, NULL); - if (format == GST_FORMAT_BYTES) { - res = FALSE; - } else { - res = gst_pad_query_default (pad, query); + /* Try query upstream first */ + peer = gst_pad_get_peer (demux->sinkpad); + if (peer) { + res = gst_pad_query (peer, query); + /* Try doing something with that query if it failed */ + if (!res && format == GST_FORMAT_TIME && demux->bitrate != -1) { + /* Try using cache first */ + if (GST_CLOCK_TIME_IS_VALID (demux->cache_duration)) { + GST_LOG_OBJECT (demux, "replying duration query from cache %" + GST_TIME_FORMAT, GST_TIME_ARGS (demux->cache_duration)); + gst_query_set_duration (query, GST_FORMAT_TIME, + demux->cache_duration); + res = TRUE; + } else { /* Query upstream and approximate */ + GstQuery *bquery = gst_query_new_duration (GST_FORMAT_BYTES); + gint64 duration = 0; + + /* Query peer for duration in bytes */ + res = gst_pad_query (peer, bquery); + if (res) { + /* Convert to time format */ + gst_query_parse_duration (bquery, &format, &duration); + GST_DEBUG_OBJECT (demux, "query on peer pad reported bytes %" + G_GUINT64_FORMAT, duration); + demux->cache_duration = BYTES_TO_GSTTIME (duration); + GST_DEBUG_OBJECT (demux, "converted to time %" GST_TIME_FORMAT, + GST_TIME_ARGS (demux->cache_duration)); + gst_query_set_duration (query, GST_FORMAT_TIME, + demux->cache_duration); + } + gst_query_unref (bquery); + } + } else { + GST_WARNING_OBJECT (demux, "unsupported query format or no bitrate " + "yet to approximate duration from bytes"); + } + gst_object_unref (peer); } - break; } default: res = gst_pad_query_default (pad, query); } + gst_object_unref (demux); return res; } @@ -2554,9 +2770,7 @@ gst_mpegts_demux_chain (GstPad * pad, GstBuffer * buffer) guint sync_count; if (GST_BUFFER_IS_DISCONT (buffer)) { - /* Flush buffered PES data */ - gst_mpegts_demux_pes_buffer_flush (demux); - gst_adapter_clear (demux->adapter); + gst_mpegts_demux_flush (demux, FALSE); } /* first push the new buffer into the adapter */ gst_adapter_push (demux->adapter, buffer); -- cgit v1.2.1