diff options
Diffstat (limited to 'gst/mpegdemux')
-rw-r--r-- | gst/mpegdemux/gstmpegdefs.h | 4 | ||||
-rw-r--r-- | gst/mpegdemux/gstmpegdemux.c | 134 | ||||
-rw-r--r-- | gst/mpegdemux/gstmpegdemux.h | 5 | ||||
-rw-r--r-- | gst/mpegdemux/gstmpegtsdemux.c | 375 | ||||
-rw-r--r-- | gst/mpegdemux/gstmpegtsdemux.h | 22 | ||||
-rw-r--r-- | gst/mpegdemux/gstpesfilter.c | 18 | ||||
-rw-r--r-- | gst/mpegdemux/gstpesfilter.h | 2 | ||||
-rw-r--r-- | gst/mpegdemux/mpegtspacketizer.c | 19 | ||||
-rw-r--r-- | gst/mpegdemux/mpegtspacketizer.h | 2 | ||||
-rw-r--r-- | gst/mpegdemux/mpegtsparse.c | 69 | ||||
-rw-r--r-- | gst/mpegdemux/mpegtsparse.h | 2 |
11 files changed, 547 insertions, 105 deletions
diff --git a/gst/mpegdemux/gstmpegdefs.h b/gst/mpegdemux/gstmpegdefs.h index 7f38f4de..7ad1e25c 100644 --- a/gst/mpegdemux/gstmpegdefs.h +++ b/gst/mpegdemux/gstmpegdefs.h @@ -170,7 +170,11 @@ #define ST_PS_AUDIO_AC3 0x81 #define ST_PS_AUDIO_DTS 0x8a #define ST_PS_AUDIO_LPCM 0x8b +#define ST_HDV_PRIVATE_A0 0xa0 +#define ST_HDV_PRIVATE_A1 0xa1 #define ST_PS_DVD_SUBPICTURE 0xff +/* Blu-ray PGS subpictures */ +#define ST_BD_PGS_SUBPICTURE 0x90 /* Un-official time-code stream */ #define ST_PS_TIMECODE 0xd2 diff --git a/gst/mpegdemux/gstmpegdemux.c b/gst/mpegdemux/gstmpegdemux.c index 439bb563..ce1d0978 100644 --- a/gst/mpegdemux/gstmpegdemux.c +++ b/gst/mpegdemux/gstmpegdemux.c @@ -39,6 +39,7 @@ * Fluendo, S.L. All Rights Reserved. * * Contributor(s): Wim Taymans <wim@fluendo.com> + * Jan Schmidt <thaytan@noraisin.net> */ #ifdef HAVE_CONFIG_H @@ -56,6 +57,9 @@ #define SCAN_SCR_SZ 12 #define SCAN_PTS_SZ 80 +#define SEGMENT_THRESHOLD (300*GST_MSECOND) +#define VIDEO_SEGMENT_THRESHOLD (500*GST_MSECOND) + typedef enum { SCAN_SCR, @@ -177,6 +181,13 @@ static GstStaticPadTemplate audio_template = "audio/x-private1-ac3;" "audio/x-private1-dts;" "audio/ac3") ); +static GstStaticPadTemplate subpicture_template = +GST_STATIC_PAD_TEMPLATE ("subpicture_%02x", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + GST_STATIC_CAPS ("video/x-dvd-subpicture") + ); + static GstStaticPadTemplate private_template = GST_STATIC_PAD_TEMPLATE ("private_%d", GST_PAD_SRC, @@ -209,6 +220,10 @@ static inline gboolean gst_flups_demux_scan_forward_ts (GstFluPSDemux * demux, static inline gboolean gst_flups_demux_scan_backward_ts (GstFluPSDemux * demux, guint64 * pos, SCAN_MODE mode, guint64 * rts); +static void gst_flups_demux_send_segment_updates (GstFluPSDemux * demux, + GstClockTime new_time); +static void gst_flups_demux_clear_times (GstFluPSDemux * demux); + static GstElementClass *parent_class = NULL; /*static guint gst_flups_demux_signals[LAST_SIGNAL] = { 0 };*/ @@ -250,10 +265,14 @@ gst_flups_demux_base_init (GstFluPSDemuxClass * klass) klass->sink_template = gst_static_pad_template_get (&sink_template); klass->video_template = gst_static_pad_template_get (&video_template); klass->audio_template = gst_static_pad_template_get (&audio_template); + klass->subpicture_template = + gst_static_pad_template_get (&subpicture_template); klass->private_template = gst_static_pad_template_get (&private_template); gst_element_class_add_pad_template (element_class, klass->video_template); gst_element_class_add_pad_template (element_class, klass->audio_template); + gst_element_class_add_pad_template (element_class, + klass->subpicture_template); gst_element_class_add_pad_template (element_class, klass->private_template); gst_element_class_add_pad_template (element_class, klass->sink_template); @@ -340,6 +359,7 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type) gchar *name; GstFluPSDemuxClass *klass = GST_FLUPS_DEMUX_GET_CLASS (demux); GstCaps *caps; + GstClockTime threshold = SEGMENT_THRESHOLD; name = NULL; template = NULL; @@ -368,6 +388,7 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type) caps = gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, mpeg_version, "systemstream", G_TYPE_BOOLEAN, FALSE, NULL); + threshold = VIDEO_SEGMENT_THRESHOLD; break; } case ST_AUDIO_MPEG1: @@ -387,6 +408,7 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type) template = klass->video_template; name = g_strdup_printf ("video_%02x", id); caps = gst_caps_new_simple ("video/x-h264", NULL); + threshold = VIDEO_SEGMENT_THRESHOLD; break; case ST_PS_AUDIO_AC3: template = klass->audio_template; @@ -404,6 +426,9 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type) caps = gst_caps_new_simple ("audio/x-private1-lpcm", NULL); break; case ST_PS_DVD_SUBPICTURE: + template = klass->subpicture_template; + name = g_strdup_printf ("subpicture_%02x", id); + caps = gst_caps_new_simple ("video/x-dvd-subpicture", NULL); break; case ST_GST_AUDIO_RAWA52: template = klass->audio_template; @@ -424,6 +449,7 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type) stream->notlinked = FALSE; stream->type = stream_type; stream->pad = gst_pad_new_from_template (template, name); + stream->segment_thresh = threshold; gst_pad_set_event_function (stream->pad, GST_DEBUG_FUNCPTR (gst_flups_demux_src_event)); gst_pad_set_query_function (stream->pad, @@ -559,6 +585,20 @@ gst_flups_demux_send_data (GstFluPSDemux * demux, GstFluPSStream * stream, GST_TIME_ARGS (demux->src_segment.last_stop), GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (demux->current_scr))); + if (demux->src_segment.last_stop != GST_CLOCK_TIME_NONE) { + GstClockTime new_time = demux->base_time + demux->src_segment.last_stop; + + if (stream->last_ts == GST_CLOCK_TIME_NONE || stream->last_ts < new_time) { +#if 0 + g_print ("last_ts update on pad %s to time %" GST_TIME_FORMAT "\n", + GST_PAD_NAME (stream->pad), GST_TIME_ARGS (cur_scr_time)); +#endif + stream->last_ts = new_time; + } + + gst_flups_demux_send_segment_updates (demux, new_time); + } + /* Set the buffer discont flag, and clear discont state on the stream */ if (stream->discont) { GST_DEBUG_OBJECT (demux, "marking discont buffer"); @@ -731,12 +771,76 @@ gst_flups_demux_flush (GstFluPSDemux * demux) gst_adapter_clear (demux->adapter); gst_adapter_clear (demux->rev_adapter); gst_pes_filter_drain (&demux->filter); + gst_flups_demux_clear_times (demux); demux->adapter_offset = G_MAXUINT64; demux->current_scr = G_MAXUINT64; demux->bytes_since_scr = 0; } static void +gst_flups_demux_clear_times (GstFluPSDemux * demux) +{ + gint id; + + /* Clear the last ts for all streams */ + for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) { + GstFluPSStream *stream = demux->streams[id]; + + if (stream) { + stream->last_seg_start = stream->last_ts = GST_CLOCK_TIME_NONE; + } + } +} + +static void +gst_flups_demux_send_segment_updates (GstFluPSDemux * demux, + GstClockTime new_time) +{ + /* Advance all lagging streams by sending a segment update */ + gint id; + GstEvent *event = NULL; + + /* FIXME: Handle reverse playback */ + + if (new_time > demux->src_segment.stop) + return; + + for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) { + GstFluPSStream *stream = demux->streams[id]; + + if (stream) { + if (stream->last_ts == GST_CLOCK_TIME_NONE || + stream->last_ts < demux->src_segment.start) + stream->last_ts = demux->src_segment.start; + if (stream->last_ts + stream->segment_thresh < new_time) { +#if 0 + g_print ("Segment update to pad %s time %" GST_TIME_FORMAT " stop now %" + GST_TIME_FORMAT "\n", GST_PAD_NAME (stream->pad), + GST_TIME_ARGS (new_time), GST_TIME_ARGS (demux->src_segment.stop)); +#endif + GST_DEBUG_OBJECT (demux, + "Segment update to pad %s time %" GST_TIME_FORMAT, + GST_PAD_NAME (stream->pad), GST_TIME_ARGS (new_time)); + if (event == NULL) { + event = gst_event_new_new_segment_full (TRUE, + demux->src_segment.rate, demux->src_segment.applied_rate, + GST_FORMAT_TIME, new_time, + demux->src_segment.stop, + demux->src_segment.time + (new_time - demux->src_segment.start)); + } + gst_event_ref (event); + gst_pad_push_event (stream->pad, event); + stream->last_seg_start = stream->last_ts = new_time; + stream->need_segment = FALSE; + } + } + } + + if (event) + gst_event_unref (event); +} + +static void gst_flups_demux_close_segment (GstFluPSDemux * demux) { gint id; @@ -747,6 +851,10 @@ gst_flups_demux_close_segment (GstFluPSDemux * demux) &demux->src_segment); #endif + /* FIXME: Need to send a different segment-close to each pad where the + * last_seg_start != clock_time_none, as that indicates a sparse-stream + * event was sent there */ + /* Close the current segment for a linear playback */ if (demux->src_segment.rate >= 0) { /* for forward playback, we played from start to last_stop */ @@ -1306,7 +1414,7 @@ gst_flups_demux_reset_psm (GstFluPSDemux * demux) FILL_TYPE (0x40, 0x7f, -1); FILL_TYPE (0x80, 0x87, ST_PS_AUDIO_AC3); FILL_TYPE (0x88, 0x9f, ST_PS_AUDIO_DTS); - FILL_TYPE (0xa0, 0xbf, ST_PS_AUDIO_LPCM); + FILL_TYPE (0xa0, 0xaf, ST_PS_AUDIO_LPCM); FILL_TYPE (0xbd, 0xbd, -1); FILL_TYPE (0xc0, 0xdf, ST_AUDIO_MPEG1); FILL_TYPE (0xe0, 0xef, ST_GST_VIDEO_MPEG1_OR_2); @@ -1839,18 +1947,26 @@ gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first, } if (G_LIKELY (stream_type == -1)) { - /* new id */ + /* new id is in the first byte */ id = data[offset++]; - /* Number of audio frames in this packet */ - nframes = data[offset++]; - - GST_DEBUG_OBJECT (demux, "private type 0x%02x, %d frames", id, - nframes); - - datalen -= 2; + datalen--; /* and remap */ stream_type = demux->psm[id]; + + /* Now, if it's a subpicture stream - no more, otherwise + * take the first byte too, since it's the frame count in audio + * streams and our backwards compat convention is to strip it off */ + if (stream_type != ST_PS_DVD_SUBPICTURE) { + /* Number of audio frames in this packet */ + nframes = data[offset++]; + datalen--; + GST_DEBUG_OBJECT (demux, "private type 0x%02x, %d frames", id, + nframes); + } else { + GST_DEBUG_OBJECT (demux, "private type 0x%02x, stream type %d", id, + stream_type); + } } } if (stream_type == -1) diff --git a/gst/mpegdemux/gstmpegdemux.h b/gst/mpegdemux/gstmpegdemux.h index ef175f76..29b3d5e5 100644 --- a/gst/mpegdemux/gstmpegdemux.h +++ b/gst/mpegdemux/gstmpegdemux.h @@ -86,6 +86,10 @@ struct _GstFluPSStream gint type; gint size_bound; + GstClockTime segment_thresh; + GstClockTime last_seg_start; + GstClockTime last_ts; + gboolean discont; gboolean notlinked; gboolean need_segment; @@ -149,6 +153,7 @@ struct _GstFluPSDemuxClass GstPadTemplate *sink_template; GstPadTemplate *video_template; GstPadTemplate *audio_template; + GstPadTemplate *subpicture_template; GstPadTemplate *private_template; }; diff --git a/gst/mpegdemux/gstmpegtsdemux.c b/gst/mpegdemux/gstmpegtsdemux.c index 4464cdf8..ef0de2c8 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 <wim@fluendo.com> */ @@ -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, " \ @@ -150,6 +157,10 @@ enum "audio/x-dts" \ ) +/* Can also use the subpicture pads for text subtitles? */ +#define SUBPICTURE_CAPS \ + GST_STATIC_CAPS ("subpicture/x-pgs; video/x-dvd-subpicture") + static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, @@ -168,6 +179,12 @@ GST_STATIC_PAD_TEMPLATE ("audio_%04x", GST_PAD_SOMETIMES, AUDIO_CAPS); +static GstStaticPadTemplate subpicture_template = +GST_STATIC_PAD_TEMPLATE ("subpicture_%04x", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + SUBPICTURE_CAPS); + static GstStaticPadTemplate private_template = GST_STATIC_PAD_TEMPLATE ("private_%04x", GST_PAD_SRC, @@ -189,6 +206,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); @@ -242,10 +260,14 @@ gst_mpegts_demux_base_init (GstMpegTSDemuxClass * klass) klass->sink_template = gst_static_pad_template_get (&sink_template); klass->video_template = gst_static_pad_template_get (&video_template); klass->audio_template = gst_static_pad_template_get (&audio_template); + klass->subpicture_template = + gst_static_pad_template_get (&subpicture_template); klass->private_template = gst_static_pad_template_get (&private_template); gst_element_class_add_pad_template (element_class, klass->video_template); gst_element_class_add_pad_template (element_class, klass->audio_template); + gst_element_class_add_pad_template (element_class, + klass->subpicture_template); gst_element_class_add_pad_template (element_class, klass->private_template); gst_element_class_add_pad_template (element_class, klass->sink_template); @@ -327,7 +349,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 @@ -369,8 +396,8 @@ gst_mpegts_demux_reset (GstMpegTSDemux * demux) case PID_TYPE_ELEMENTARY: gst_pes_filter_uninit (&stream->filter); break; - case PID_PROGRAM_ASSOCIATION_TABLE: - case PID_CONDITIONAL_ACCESS_TABLE: + case PID_TYPE_PROGRAM_ASSOCIATION: + case PID_TYPE_CONDITIONAL_ACCESS: case PID_TYPE_PROGRAM_MAP: gst_section_filter_uninit (&stream->section_filter); break; @@ -607,6 +634,16 @@ gst_mpegts_demux_fill_stream (GstMpegTSStream * stream, guint8 id, caps = gst_caps_new_simple ("private/teletext", NULL); } break; + case ST_HDV_PRIVATE_A0: + template = klass->private_template; + name = g_strdup_printf ("private_%04x", stream->PID); + caps = gst_caps_new_simple ("private/hdv-a0", NULL); + break; + case ST_HDV_PRIVATE_A1: + template = klass->private_template; + name = g_strdup_printf ("private_%04x", stream->PID); + caps = gst_caps_new_simple ("private/hdv-a1", NULL); + break; case ST_PRIVATE_SECTIONS: case ST_MHEG: case ST_DSMCC: @@ -652,6 +689,14 @@ gst_mpegts_demux_fill_stream (GstMpegTSStream * stream, guint8 id, caps = gst_caps_new_simple ("audio/x-lpcm", NULL); break; case ST_PS_DVD_SUBPICTURE: + template = klass->subpicture_template; + name = g_strdup_printf ("subpicture_%04x", stream->PID); + caps = gst_caps_new_simple ("video/x-dvd-subpicture", NULL); + break; + case ST_BD_PGS_SUBPICTURE: + template = klass->subpicture_template; + name = g_strdup_printf ("subpicture_%04x", stream->PID); + caps = gst_caps_new_simple ("subpicture/x-pgs", NULL); break; default: break; @@ -668,6 +713,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 +777,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); @@ -828,7 +875,7 @@ gst_mpegts_demux_data_cb (GstPESFilter * filter, gboolean first, demux = stream->demux; srcpad = stream->pad; - GST_LOG_OBJECT (demux, "got data on PID 0x%04x", stream->PID); + GST_DEBUG_OBJECT (demux, "got data on PID 0x%04x", stream->PID); if (first && filter->pts != -1) { pts = filter->pts; @@ -932,7 +979,7 @@ gst_mpegts_demux_data_cb (GstPESFilter * filter, gboolean first, pts = -1; } - GST_LOG_OBJECT (demux, "setting PTS to (%" G_GUINT64_FORMAT ") time: %" + GST_DEBUG_OBJECT (demux, "setting PTS to (%" G_GUINT64_FORMAT ") time: %" GST_TIME_FORMAT " on buffer %p first buffer: %d base_time: %" GST_TIME_FORMAT, pts, GST_TIME_ARGS (time), buffer, first, GST_TIME_ARGS (stream->base_time)); @@ -967,8 +1014,8 @@ gst_mpegts_demux_data_cb (GstPESFilter * filter, gboolean first, goto unknown_type; GST_DEBUG_OBJECT (demux, - "New stream 0x%04x of type %d with caps %" GST_PTR_FORMAT, stream->PID, - stream->stream_type, GST_PAD_CAPS (stream->pad)); + "New stream 0x%04x of type 0x%02x with caps %" GST_PTR_FORMAT, + stream->PID, stream->stream_type, GST_PAD_CAPS (stream->pad)); srcpad = stream->pad; @@ -981,7 +1028,7 @@ gst_mpegts_demux_data_cb (GstPESFilter * filter, gboolean first, gst_mpegts_demux_send_new_segment (demux, stream, pts); } - GST_DEBUG_OBJECT (demux, "pushing buffer"); + GST_DEBUG_OBJECT (srcpad, "pushing buffer"); gst_buffer_set_caps (buffer, GST_PAD_CAPS (srcpad)); ret = gst_pad_push (srcpad, buffer); ret = gst_mpegts_demux_combine_flows (demux, stream, ret); @@ -992,7 +1039,7 @@ gst_mpegts_demux_data_cb (GstPESFilter * filter, gboolean first, unknown_type: { GST_DEBUG_OBJECT (demux, "got unknown stream id 0x%02x, type 0x%02x", - filter->id, filter->type); + filter->id, stream->stream_type); gst_buffer_unref (buffer); return gst_mpegts_demux_combine_flows (demux, stream, GST_FLOW_NOT_LINKED); } @@ -1184,16 +1231,16 @@ gst_mpegts_stream_parse_pmt (GstMpegTSStream * stream, if (gst_mpegts_demux_calc_crc32 (data - 3, datalen) != 0) goto wrong_crc; - GST_DEBUG_OBJECT (demux, "PMT section_length: %d", datalen - 3); + GST_LOG_OBJECT (demux, "PMT section_length: %d", datalen - 3); PMT = &stream->PMT; /* check if version number changed */ version_number = (data[2] & 0x3e) >> 1; - GST_DEBUG_OBJECT (demux, "PMT version_number: %d", version_number); + GST_LOG_OBJECT (demux, "PMT version_number: %d", version_number); current_next_indicator = (data[2] & 0x01); - GST_DEBUG_OBJECT (demux, "PMT current_next_indicator %d", + GST_LOG_OBJECT (demux, "PMT current_next_indicator %d", current_next_indicator); if (current_next_indicator == 0) goto not_yet_applicable; @@ -1559,7 +1606,7 @@ gst_mpegts_demux_parse_adaptation_field (GstMpegTSStream * stream, if (length > 0) { guint8 flags = *data++; - GST_DEBUG_OBJECT (demux, "flags 0x%02x", flags); + GST_LOG_OBJECT (demux, "flags 0x%02x", flags); /* discontinuity flag */ if (flags & 0x80) { GST_DEBUG_OBJECT (demux, "discontinuity flag set"); @@ -1578,9 +1625,12 @@ gst_mpegts_demux_parse_adaptation_field (GstMpegTSStream * stream, pcr_ext = (pcr2 & 0x01ff); if (pcr_ext) pcr = (pcr * 300 + pcr_ext % 300) / 300; - GST_DEBUG_OBJECT (demux, "have PCR %" G_GUINT64_FORMAT " on PID 0x%04x " - "and last pcr is %" G_GUINT64_FORMAT, - pcr, stream->PID, stream->last_PCR); + GST_DEBUG_OBJECT (demux, + "have PCR %" G_GUINT64_FORMAT "(%" GST_TIME_FORMAT ") on PID 0x%04x " + "and last pcr is %" G_GUINT64_FORMAT " (%" GST_TIME_FORMAT ")", pcr, + GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (pcr)), stream->PID, + stream->last_PCR, + GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (stream->last_PCR))); /* pcr has been converted into units of 90Khz ticks * so assume discont if last pcr was > 900000 (10 second) lower */ if (stream->last_PCR != -1 && @@ -1676,11 +1726,24 @@ gst_mpegts_demux_parse_adaptation_field (GstMpegTSStream * stream, } stream->last_PCR_difference = pcr - stream->last_PCR; } + GST_DEBUG_OBJECT (demux, "valid pcr: %d last PCR difference: %" G_GUINT64_FORMAT, valid_pcr, stream->last_PCR_difference); if (valid_pcr) { - + GstMpegTSStream *PMT_stream = demux->streams[demux->current_PMT]; + + if (PMT_stream && PMT_stream->PMT.PCR_PID == stream->PID) { + if (demux->pcr[0] == -1) { + GST_DEBUG ("RECORDING pcr[0]:%" G_GUINT64_FORMAT, pcr); + 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)) { + GST_DEBUG ("RECORDING pcr[1]:%" G_GUINT64_FORMAT, pcr); + demux->pcr[1] = pcr; + } + } stream->last_PCR = pcr; if (demux->clock && demux->clock_base != GST_CLOCK_TIME_NONE) { @@ -1955,15 +2018,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 +2053,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 +2078,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 +2086,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; } } @@ -2093,7 +2161,7 @@ gst_mpegts_demux_parse_stream (GstMpegTSDemux * demux, GstMpegTSStream * stream, data += 3; datalen -= 3; - GST_DEBUG_OBJECT (demux, "afc 0x%x, pusi %d, PID 0x%04x datalen %u", + GST_LOG_OBJECT (demux, "afc 0x%x, pusi %d, PID 0x%04x datalen %u", adaptation_field_control, payload_unit_start_indicator, PID, datalen); ret = GST_FLOW_OK; @@ -2115,13 +2183,12 @@ gst_mpegts_demux_parse_stream (GstMpegTSDemux * demux, GstMpegTSStream * stream, data += consumed; datalen -= consumed; - GST_DEBUG_OBJECT (demux, "consumed: %u datalen: %u", consumed, datalen); + GST_LOG_OBJECT (demux, "consumed: %u datalen: %u", consumed, datalen); } /* If this packet has a payload, handle it */ if (adaptation_field_control & 0x1) { - GST_DEBUG_OBJECT (demux, "Packet payload %d bytes, PID 0x%04x", datalen, - PID); + GST_LOG_OBJECT (demux, "Packet payload %d bytes, PID 0x%04x", datalen, PID); /* For unknown streams, check if the PID is in the partial PIDs * list as an elementary stream and override the type if so @@ -2233,7 +2300,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 */ @@ -2302,7 +2369,7 @@ static FORCE_INLINE GstFlowReturn gst_mpegts_demux_parse_transport_packet (GstMpegTSDemux * demux, const guint8 * data) { - GstFlowReturn ret; + GstFlowReturn ret = GST_FLOW_OK; guint16 PID; GstMpegTSStream *stream; @@ -2312,6 +2379,10 @@ gst_mpegts_demux_parse_transport_packet (GstMpegTSDemux * demux, /* get PID */ PID = ((data[0] & 0x1f) << 8) | data[1]; + /* Skip NULL packets */ + if (G_UNLIKELY (PID == 0x1fff)) + goto beach; + /* get the stream. */ stream = gst_mpegts_demux_get_stream_for_PID (demux, PID); @@ -2319,12 +2390,153 @@ 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) { + guint64 bitrate; + GST_DEBUG_OBJECT (demux, "pcr[0]:%" G_GUINT64_FORMAT, demux->pcr[0]); + GST_DEBUG_OBJECT (demux, "pcr[1]:%" G_GUINT64_FORMAT, demux->pcr[1]); + GST_DEBUG_OBJECT (demux, "diff in time %" GST_TIME_FORMAT, + GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (demux->pcr[1] - demux->pcr[0]))); + GST_DEBUG_OBJECT (demux, "stream->last_PCR_difference: %" G_GUINT64_FORMAT + ", demux->num_packets %" G_GUINT64_FORMAT, + demux->pcr[1] - demux->pcr[0], demux->num_packets); + bitrate = gst_util_uint64_scale (GST_SECOND, + MPEGTS_NORMAL_TS_PACKETSIZE * demux->num_packets, + MPEGTIME_TO_GSTTIME (demux->pcr[1] - demux->pcr[0])); + /* somehow... I doubt a bitrate below one packet per second is valid */ + if (bitrate > MPEGTS_NORMAL_TS_PACKETSIZE - 1) { + demux->bitrate = bitrate; + GST_DEBUG_OBJECT (demux, "bitrate is %" G_GINT64_FORMAT + " bytes per second", demux->bitrate); + } else { + GST_WARNING_OBJECT (demux, "Couldn't compute valid bitrate, recomputing"); + demux->pcr[0] = demux->pcr[1] = -1; + demux->num_packets = -1; + } + } + +beach: + 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) { gint i; @@ -2347,7 +2559,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 +2570,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 +2583,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 @@ -2415,8 +2658,7 @@ gst_mpegts_demux_provide_clock (GstElement * element) "MpegTSClock", NULL); demux->clock_base = GST_CLOCK_TIME_NONE; } - - return demux->clock; + return gst_object_ref (demux->clock); } return NULL; @@ -2455,21 +2697,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 +2829,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); diff --git a/gst/mpegdemux/gstmpegtsdemux.h b/gst/mpegdemux/gstmpegtsdemux.h index 8a4ca3d2..fde68926 100644 --- a/gst/mpegdemux/gstmpegtsdemux.h +++ b/gst/mpegdemux/gstmpegtsdemux.h @@ -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.A. are Copyright (C) 2005,2006,2007,2008,2009 + * Fluendo, S.A. All Rights Reserved. * * Contributor(s): Wim Taymans <wim@fluendo.com> */ @@ -205,6 +205,21 @@ struct _GstMpegTSDemux { /* clocking */ GstClock * clock; GstClockTime clock_base; + + /* Additional information required for seeking. + * num_packets: Number of packets outputted + * bitrate: estimated bitrate (based on pcr and num_packets */ + guint64 num_packets; + gint64 bitrate; + + /* Two PCRs observations to calculate bitrate */ + guint64 pcr[2]; + + /* Cached duration estimation */ + GstClockTime cache_duration; + + /* Cached base_PCR in GStreamer time. */ + GstClockTime base_pts; }; struct _GstMpegTSDemuxClass { @@ -213,6 +228,7 @@ struct _GstMpegTSDemuxClass { GstPadTemplate * sink_template; GstPadTemplate * video_template; GstPadTemplate * audio_template; + GstPadTemplate * subpicture_template; GstPadTemplate * private_template; }; diff --git a/gst/mpegdemux/gstpesfilter.c b/gst/mpegdemux/gstpesfilter.c index 1295a193..b520c766 100644 --- a/gst/mpegdemux/gstpesfilter.c +++ b/gst/mpegdemux/gstpesfilter.c @@ -167,6 +167,9 @@ gst_pes_filter_parse (GstPESFilter * filter) avail = MIN (avail, filter->length + 6); } + if (avail < 7) + goto need_more_data; + /* read more data, either the whole packet if there is a length * or whatever we have available if this in an unbounded packet. */ if (!(data = gst_adapter_peek (filter->adapter, avail))) @@ -189,7 +192,8 @@ gst_pes_filter_parse (GstPESFilter * filter) case ID_PROGRAM_STREAM_DIRECTORY: case ID_DSMCC_STREAM: case ID_ITU_TREC_H222_TYPE_E_STREAM: - goto skip; + /* Push directly out */ + goto push_out; case ID_PADDING_STREAM: GST_DEBUG ("skipping padding stream"); goto skip; @@ -197,9 +201,6 @@ gst_pes_filter_parse (GstPESFilter * filter) break; } - if (datalen < 1) - goto need_more_data; - filter->pts = filter->dts = -1; /* stuffing bits, first two bits are '10' for mpeg2 pes so this code is @@ -404,6 +405,7 @@ gst_pes_filter_parse (GstPESFilter * filter) goto lost_sync; } +push_out: { GstBuffer *out; guint16 consumed; @@ -563,14 +565,8 @@ gst_pes_filter_process (GstPESFilter * filter) ret = GST_FLOW_OK; } else { GstBuffer *out; - guint8 *data; - - data = gst_adapter_take (filter->adapter, avail); - out = gst_buffer_new (); - GST_BUFFER_DATA (out) = data; - GST_BUFFER_SIZE (out) = avail; - GST_BUFFER_MALLOCDATA (out) = data; + out = gst_adapter_take_buffer (filter->adapter, avail); ret = gst_pes_filter_data_push (filter, filter->first, out); filter->first = FALSE; diff --git a/gst/mpegdemux/gstpesfilter.h b/gst/mpegdemux/gstpesfilter.h index b35d8746..ccc8461d 100644 --- a/gst/mpegdemux/gstpesfilter.h +++ b/gst/mpegdemux/gstpesfilter.h @@ -84,8 +84,6 @@ struct _GstPESFilter { gboolean unbounded_packet; guint16 length; - guint8 type; - gint64 pts; gint64 dts; }; diff --git a/gst/mpegdemux/mpegtspacketizer.c b/gst/mpegdemux/mpegtspacketizer.c index 1abbf1fe..b986fa60 100644 --- a/gst/mpegdemux/mpegtspacketizer.c +++ b/gst/mpegdemux/mpegtspacketizer.c @@ -241,7 +241,8 @@ mpegts_packetizer_parse_section_header (MpegTSPacketizer * packetizer, data = GST_BUFFER_DATA (section->buffer); section->table_id = *data++; - if ((data[0] & 0x80) == 0) + /* if table_id is 0 (pat) then ignore the subtable extension */ + if ((data[0] & 0x80) == 0 || section->table_id == 0) section->subtable_extension = 0; else section->subtable_extension = GST_READ_UINT16_BE (data + 2); @@ -1844,6 +1845,22 @@ mpegts_packetizer_clear (MpegTSPacketizer * packetizer) gst_adapter_clear (packetizer->adapter); } +void +mpegts_packetizer_remove_stream (MpegTSPacketizer * packetizer, gint16 pid) +{ + MpegTSPacketizerStream *stream = + (MpegTSPacketizerStream *) g_hash_table_lookup (packetizer->streams, + GINT_TO_POINTER ((gint) pid)); + if (stream) { + GST_INFO ("Removing stream for PID %d", pid); + + g_hash_table_remove (packetizer->streams, GINT_TO_POINTER ((gint) pid)); + + g_object_unref (stream->section_adapter); + g_free (stream); + } +} + MpegTSPacketizer * mpegts_packetizer_new () { diff --git a/gst/mpegdemux/mpegtspacketizer.h b/gst/mpegdemux/mpegtspacketizer.h index c54228af..97e15cf2 100644 --- a/gst/mpegdemux/mpegtspacketizer.h +++ b/gst/mpegdemux/mpegtspacketizer.h @@ -116,6 +116,8 @@ gboolean mpegts_packetizer_next_packet (MpegTSPacketizer *packetizer, MpegTSPacketizerPacket *packet); void mpegts_packetizer_clear_packet (MpegTSPacketizer *packetizer, MpegTSPacketizerPacket *packet); +void mpegts_packetizer_remove_stream(MpegTSPacketizer *packetizer, + gint16 pid); gboolean mpegts_packetizer_push_section (MpegTSPacketizer *packetzer, MpegTSPacketizerPacket *packet, MpegTSPacketizerSection *section); diff --git a/gst/mpegdemux/mpegtsparse.c b/gst/mpegdemux/mpegtsparse.c index 3a2d647d..976935f2 100644 --- a/gst/mpegdemux/mpegtsparse.c +++ b/gst/mpegdemux/mpegtsparse.c @@ -266,12 +266,15 @@ mpegts_parse_init (MpegTSParse * parse, MpegTSParseClass * klass) gst_element_add_pad (GST_ELEMENT (parse), parse->sinkpad); parse->disposed = FALSE; + parse->need_sync_program_pads = FALSE; parse->packetizer = mpegts_packetizer_new (); parse->program_numbers = g_strdup (""); parse->pads_to_add = NULL; + parse->pads_to_remove = NULL; parse->programs = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) mpegts_parse_free_program); parse->psi_pids = g_hash_table_new (g_direct_hash, g_direct_equal); + parse->pes_pids = g_hash_table_new (g_direct_hash, g_direct_equal); mpegts_parse_reset (parse); } @@ -301,6 +304,7 @@ mpegts_parse_finalize (GObject * object) } g_hash_table_destroy (parse->programs); g_hash_table_destroy (parse->psi_pids); + g_hash_table_destroy (parse->pes_pids); if (G_OBJECT_CLASS (parent_class)->finalize) G_OBJECT_CLASS (parent_class)->finalize (object); @@ -348,7 +352,7 @@ mpegts_parse_add_program (MpegTSParse * parse, program->pcr_pid = G_MAXUINT16; program->streams = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) mpegts_parse_free_stream); - program->patcount = 1; + program->patcount = 0; program->selected = 0; program->active = FALSE; @@ -422,22 +426,30 @@ mpegts_parse_remove_program (MpegTSParse * parse, gint program_number) } static void -mpegts_parse_sync_program_pads (MpegTSParse * parse, - GList * to_add, GList * to_remove) +mpegts_parse_sync_program_pads (MpegTSParse * parse) { GList *walk; - for (walk = to_remove; walk; walk = walk->next) + GST_INFO_OBJECT (parse, "begin sync pads"); + for (walk = parse->pads_to_remove; walk; walk = walk->next) gst_element_remove_pad (GST_ELEMENT (parse), GST_PAD (walk->data)); - for (walk = to_add; walk; walk = walk->next) + for (walk = parse->pads_to_add; walk; walk = walk->next) gst_element_add_pad (GST_ELEMENT (parse), GST_PAD (walk->data)); - if (to_add) - g_list_free (to_add); + if (parse->pads_to_add) + g_list_free (parse->pads_to_add); + + if (parse->pads_to_remove) + g_list_free (parse->pads_to_remove); + + GST_OBJECT_LOCK (parse); + parse->pads_to_remove = NULL; + parse->pads_to_add = NULL; + parse->need_sync_program_pads = FALSE; + GST_OBJECT_UNLOCK (parse); - if (to_remove) - g_list_free (to_remove); + GST_INFO_OBJECT (parse, "end sync pads"); } @@ -493,9 +505,6 @@ static void mpegts_parse_reset_selected_programs (MpegTSParse * parse, gchar * program_numbers) { - GList *pads_to_add = NULL; - GList *pads_to_remove = NULL; - GST_OBJECT_LOCK (parse); if (parse->program_numbers) g_free (parse->program_numbers); @@ -526,13 +535,9 @@ mpegts_parse_reset_selected_programs (MpegTSParse * parse, g_hash_table_foreach (parse->programs, foreach_program_activate_or_deactivate, parse); - pads_to_add = parse->pads_to_add; - parse->pads_to_add = NULL; - pads_to_remove = parse->pads_to_remove; - parse->pads_to_remove = NULL; + if (parse->pads_to_remove || parse->pads_to_add) + parse->need_sync_program_pads = TRUE; GST_OBJECT_UNLOCK (parse); - - mpegts_parse_sync_program_pads (parse, pads_to_add, pads_to_remove); } static void @@ -804,6 +809,10 @@ mpegts_parse_is_psi (MpegTSParse * parse, MpegTSPacketizerPacket * packet) if (g_hash_table_lookup (parse->psi_pids, GINT_TO_POINTER ((gint) packet->pid)) != NULL) retval = TRUE; + /* check is it is a pes pid */ + if (g_hash_table_lookup (parse->pes_pids, + GINT_TO_POINTER ((gint) packet->pid)) != NULL) + return FALSE; if (!retval) { if (packet->payload_unit_start_indicator) { table_id = *(packet->data); @@ -850,8 +859,6 @@ mpegts_parse_apply_pat (MpegTSParse * parse, GstStructure * pat_info) guint pid; MpegTSParseProgram *program; gint i; - GList *pads_to_add = NULL; - GList *pads_to_remove = NULL; const GValue *programs; gchar *dbg; @@ -889,14 +896,12 @@ mpegts_parse_apply_pat (MpegTSParse * parse, GstStructure * pat_info) g_hash_table_insert (parse->psi_pids, GINT_TO_POINTER ((gint) pid), GINT_TO_POINTER (1)); } - - program->patcount += 1; } else { g_hash_table_insert (parse->psi_pids, GINT_TO_POINTER ((gint) pid), GINT_TO_POINTER (1)); program = mpegts_parse_add_program (parse, program_number, pid); } - + program->patcount += 1; if (program->selected && !program->active) parse->pads_to_add = g_list_append (parse->pads_to_add, mpegts_parse_activate_program (parse, program)); @@ -937,18 +942,15 @@ mpegts_parse_apply_pat (MpegTSParse * parse, GstStructure * pat_info) mpegts_parse_remove_program (parse, program_number); g_hash_table_remove (parse->psi_pids, GINT_TO_POINTER ((gint) pid)); + mpegts_packetizer_remove_stream (parse->packetizer, pid); } gst_structure_free (old_pat); } - pads_to_add = parse->pads_to_add; - parse->pads_to_add = NULL; - pads_to_remove = parse->pads_to_remove; - parse->pads_to_remove = NULL; GST_OBJECT_UNLOCK (parse); - mpegts_parse_sync_program_pads (parse, pads_to_add, pads_to_remove); + mpegts_parse_sync_program_pads (parse); } static void @@ -983,10 +985,13 @@ mpegts_parse_apply_pmt (MpegTSParse * parse, gst_structure_get_uint (stream, "pid", &pid); gst_structure_get_uint (stream, "stream-type", &stream_type); mpegts_parse_program_remove_stream (parse, program, (guint16) pid); + g_hash_table_remove (parse->pes_pids, GINT_TO_POINTER ((gint) pid)); } /* remove pcr stream */ mpegts_parse_program_remove_stream (parse, program, program->pcr_pid); + g_hash_table_remove (parse->pes_pids, + GINT_TO_POINTER ((gint) program->pcr_pid)); gst_structure_free (program->pmt_info); program->pmt_info = NULL; @@ -1003,6 +1008,8 @@ mpegts_parse_apply_pmt (MpegTSParse * parse, program->pmt_pid = pmt_pid; program->pcr_pid = pcr_pid; mpegts_parse_program_add_stream (parse, program, (guint16) pcr_pid, -1); + g_hash_table_insert (parse->pes_pids, GINT_TO_POINTER ((gint) pcr_pid), + GINT_TO_POINTER (1)); for (i = 0; i < gst_value_list_get_size (new_streams); ++i) { value = gst_value_list_get_value (new_streams, i); @@ -1012,6 +1019,9 @@ mpegts_parse_apply_pmt (MpegTSParse * parse, gst_structure_get_uint (stream, "stream-type", &stream_type); mpegts_parse_program_add_stream (parse, program, (guint16) pid, (guint8) stream_type); + g_hash_table_insert (parse->pes_pids, GINT_TO_POINTER ((gint) pid), + GINT_TO_POINTER ((gint) 1)); + } GST_OBJECT_UNLOCK (parse); @@ -1226,6 +1236,9 @@ mpegts_parse_chain (GstPad * pad, GstBuffer * buf) mpegts_packetizer_clear_packet (parse->packetizer, &packet); } + if (parse->need_sync_program_pads) + mpegts_parse_sync_program_pads (parse); + gst_object_unref (parse); return res; } diff --git a/gst/mpegdemux/mpegtsparse.h b/gst/mpegdemux/mpegtsparse.h index 34a78634..36466b81 100644 --- a/gst/mpegdemux/mpegtsparse.h +++ b/gst/mpegdemux/mpegtsparse.h @@ -60,7 +60,9 @@ struct _MpegTSParse { GstStructure *pat; MpegTSPacketizer *packetizer; GHashTable *psi_pids; + GHashTable *pes_pids; gboolean disposed; + gboolean need_sync_program_pads; }; struct _MpegTSParseClass { |