diff --git a/gst/mpegstream/gstmpegdemux.c b/gst/mpegstream/gstmpegdemux.c index 52be3d7313..1eac419d2c 100644 --- a/gst/mpegstream/gstmpegdemux.c +++ b/gst/mpegstream/gstmpegdemux.c @@ -138,6 +138,7 @@ static void gst_mpeg_demux_send_data (GstMPEGParse *mpeg_parse, GstData *data, GstClockTime time); static void gst_mpeg_demux_handle_discont (GstMPEGParse *mpeg_parse); +static gboolean gst_mpeg_demux_handle_src_event (GstPad *pad, GstEvent *event); static void gst_mpeg_demux_set_index (GstElement *element, GstIndex *index); static GstIndex* gst_mpeg_demux_get_index (GstElement *element); @@ -421,12 +422,17 @@ gst_mpeg_demux_parse_syshead (GstMPEGParse *mpeg_parse, GstBuffer *buffer) gst_pad_try_set_caps (*outpad, gst_pad_get_pad_template_caps (*outpad)); gst_pad_set_formats_function (*outpad, gst_mpeg_parse_get_src_formats); + gst_pad_set_convert_function (*outpad, gst_mpeg_parse_convert_src); gst_pad_set_event_mask_function (*outpad, gst_mpeg_parse_get_src_event_masks); - gst_pad_set_event_function (*outpad, gst_mpeg_parse_handle_src_event); + gst_pad_set_event_function (*outpad, gst_mpeg_demux_handle_src_event); gst_pad_set_query_type_function (*outpad, gst_mpeg_parse_get_src_query_types); gst_pad_set_query_function (*outpad, gst_mpeg_parse_handle_src_query); gst_element_add_pad (GST_ELEMENT (mpeg_demux), (*outpad)); + gst_pad_set_element_private (*outpad, *outstream); + + (*outstream)->size_bound = buf_byte_size_bound; + mpeg_demux->total_size_bound += buf_byte_size_bound; if (mpeg_demux->index) { gst_index_get_writer_id (mpeg_demux->index, GST_OBJECT (*outpad), @@ -854,12 +860,14 @@ gst_mpeg_demux_parse_pes (GstMPEGParse *mpeg_parse, GstBuffer *buffer) gst_pad_try_set_caps (*outpad, gst_pad_get_pad_template_caps (*outpad)); gst_pad_set_formats_function (*outpad, gst_mpeg_parse_get_src_formats); + gst_pad_set_convert_function (*outpad, gst_mpeg_parse_convert_src); gst_pad_set_event_mask_function (*outpad, gst_mpeg_parse_get_src_event_masks); - gst_pad_set_event_function (*outpad, gst_mpeg_parse_handle_src_event); + gst_pad_set_event_function (*outpad, gst_mpeg_demux_handle_src_event); gst_pad_set_query_type_function (*outpad, gst_mpeg_parse_get_src_query_types); gst_pad_set_query_function (*outpad, gst_mpeg_parse_handle_src_query); gst_element_add_pad(GST_ELEMENT(mpeg_demux), *outpad); + gst_pad_set_element_private (*outpad, *outstream); if (mpeg_demux->index) { gst_index_get_writer_id (mpeg_demux->index, GST_OBJECT (*outpad), @@ -911,6 +919,78 @@ gst_mpeg_demux_parse_pes (GstMPEGParse *mpeg_parse, GstBuffer *buffer) return TRUE; } +static gboolean +index_seek (GstPad *pad, GstEvent *event, gint64 *offset) +{ + GstIndexEntry *entry; + GstMPEGDemux *mpeg_demux = GST_MPEG_DEMUX (gst_pad_get_parent (pad)); + GstMPEGStream *stream = gst_pad_get_element_private (pad); + + entry = gst_index_get_assoc_entry (mpeg_demux->index, stream->index_id, + GST_INDEX_LOOKUP_BEFORE, 0, + GST_EVENT_SEEK_FORMAT (event), + GST_EVENT_SEEK_OFFSET (event)); + if (!entry) + return FALSE; + + if (gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, offset)) { + return TRUE; + } + return FALSE; +} + +static gboolean +normal_seek (GstPad *pad, GstEvent *event, gint64 *offset) +{ + gboolean res = FALSE; + gint64 adjust; + GstFormat format; + GstMPEGDemux *mpeg_demux = GST_MPEG_DEMUX (gst_pad_get_parent (pad)); + + format = GST_EVENT_SEEK_FORMAT (event); + + res = gst_pad_convert (pad, GST_FORMAT_BYTES, mpeg_demux->total_size_bound, + &format, &adjust); + + GST_DEBUG (0, "seek adjusted from %lld bytes to %lld\n", mpeg_demux->total_size_bound, adjust); + + if (res) + *offset = MAX (GST_EVENT_SEEK_OFFSET (event) - adjust, 0); + + return res; +} + +static gboolean +gst_mpeg_demux_handle_src_event (GstPad *pad, GstEvent *event) +{ + gboolean res = FALSE; + GstMPEGDemux *mpeg_demux = GST_MPEG_DEMUX (gst_pad_get_parent (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + { + guint64 desired_offset; + + if (mpeg_demux->index) + res = index_seek (pad, event, &desired_offset); + if (!res) + res = normal_seek (pad, event, &desired_offset); + + if (res) { + GstEvent *new_event; + + new_event = gst_event_new_seek (GST_EVENT_SEEK_TYPE (event), desired_offset); + gst_event_unref (event); + res = gst_mpeg_parse_handle_src_event (pad, new_event); + } + break; + } + default: + break; + } + return res; +} + static GstElementStateReturn gst_mpeg_demux_change_state (GstElement *element) { diff --git a/gst/mpegstream/gstmpegdemux.h b/gst/mpegstream/gstmpegdemux.h index b39256fd5b..b5db9db331 100644 --- a/gst/mpegstream/gstmpegdemux.h +++ b/gst/mpegstream/gstmpegdemux.h @@ -54,24 +54,26 @@ struct _GstMPEGStream { GstPad *pad; guint64 pts; gint index_id; + gint size_bound; }; struct _GstMPEGDemux { - GstMPEGParse parent; + GstMPEGParse parent; /* previous partial chunk and bytes remaining in it */ - gboolean in_flush; + gboolean in_flush; /* program stream header values */ - guint16 header_length; - guint32 rate_bound; - guint8 audio_bound; - gboolean fixed; - gboolean constrained; - gboolean audio_lock; - gboolean video_lock; - guint8 video_bound; - gboolean packet_rate_restriction; + guint16 header_length; + guint32 rate_bound; + guint8 audio_bound; + gboolean fixed; + gboolean constrained; + gboolean audio_lock; + gboolean video_lock; + guint8 video_bound; + gboolean packet_rate_restriction; + gint64 total_size_bound; #define NUM_PRIVATE_1_STREAMS 8 #define NUM_SUBTITLE_STREAMS 16 diff --git a/gst/mpegstream/gstmpegpacketize.c b/gst/mpegstream/gstmpegpacketize.c index 024cd6e8b3..88ee8751ec 100644 --- a/gst/mpegstream/gstmpegpacketize.c +++ b/gst/mpegstream/gstmpegpacketize.c @@ -250,8 +250,6 @@ gst_mpeg_packetize_read (GstMPEGPacketize *packetize) gst_bytestream_get_status (packetize->bs, &remaining, &event); etype = event? GST_EVENT_TYPE (event) : GST_EVENT_EOS; - g_print ("remaining %d\n", remaining); - switch (etype) { case GST_EVENT_DISCONTINUOUS: GST_DEBUG (GST_CAT_EVENT, "packetize: discont\n"); diff --git a/gst/mpegstream/gstmpegparse.c b/gst/mpegstream/gstmpegparse.c index 5b74aab52d..00fb8d1a5a 100644 --- a/gst/mpegstream/gstmpegparse.c +++ b/gst/mpegstream/gstmpegparse.c @@ -159,7 +159,6 @@ gst_mpeg_parse_class_init (GstMPEGParseClass *klass) gstelement_class->get_index = gst_mpeg_parse_get_index; gstelement_class->set_index = gst_mpeg_parse_set_index; - klass->parse_packhead = gst_mpeg_parse_parse_packhead; klass->parse_syshead = NULL; klass->parse_packet = NULL; @@ -174,11 +173,14 @@ gst_mpeg_parse_init (GstMPEGParse *mpeg_parse) mpeg_parse->sinkpad = gst_pad_new_from_template( GST_PAD_TEMPLATE_GET (sink_factory), "sink"); gst_element_add_pad(GST_ELEMENT(mpeg_parse),mpeg_parse->sinkpad); + gst_pad_set_formats_function (mpeg_parse->sinkpad, gst_mpeg_parse_get_src_formats); + gst_pad_set_convert_function (mpeg_parse->sinkpad, gst_mpeg_parse_convert_src); mpeg_parse->srcpad = gst_pad_new_from_template( GST_PAD_TEMPLATE_GET (src_factory), "src"); gst_element_add_pad(GST_ELEMENT(mpeg_parse),mpeg_parse->srcpad); gst_pad_set_formats_function (mpeg_parse->srcpad, gst_mpeg_parse_get_src_formats); + gst_pad_set_convert_function (mpeg_parse->srcpad, gst_mpeg_parse_convert_src); gst_pad_set_event_mask_function (mpeg_parse->srcpad, gst_mpeg_parse_get_src_event_masks); gst_pad_set_event_function (mpeg_parse->srcpad, gst_mpeg_parse_handle_src_event); gst_pad_set_query_type_function (mpeg_parse->srcpad, gst_mpeg_parse_get_src_query_types); @@ -186,17 +188,8 @@ gst_mpeg_parse_init (GstMPEGParse *mpeg_parse) gst_element_set_loop_function (GST_ELEMENT (mpeg_parse), gst_mpeg_parse_loop); - /* initialize parser state */ mpeg_parse->packetize = NULL; - mpeg_parse->current_scr = 0; - mpeg_parse->bytes_since_scr = 0; - mpeg_parse->adjust = 0; mpeg_parse->sync = FALSE; - - /* zero counters (should be done at RUNNING?) */ - mpeg_parse->mux_rate = 0; - mpeg_parse->discont_pending = FALSE; - mpeg_parse->scr_pending = TRUE; mpeg_parse->max_discont = DEFAULT_MAX_DISCONT; mpeg_parse->provided_clock = gst_mpeg_clock_new ("MPEGParseClock", gst_mpeg_parse_get_time, mpeg_parse); @@ -207,9 +200,9 @@ gst_mpeg_parse_init (GstMPEGParse *mpeg_parse) static GstClock* gst_mpeg_parse_get_clock (GstElement *element) { - //GstMPEGParse *parse = GST_MPEG_PARSE (element); + /* GstMPEGParse *parse = GST_MPEG_PARSE (element); */ - //return parse->provided_clock; + /* return parse->provided_clock; */ return NULL; } @@ -329,6 +322,10 @@ gst_mpeg_parse_parse_packhead (GstMPEGParse *mpeg_parse, GstBuffer *buffer) mpeg_parse->bytes_since_scr = 0; scr_adj = scr + mpeg_parse->adjust; + if (mpeg_parse->next_scr == -1) { + mpeg_parse->next_scr = scr; + } + GST_DEBUG (0, "SCR is %llu (%llu) next: %lld (%lld) diff: %lld (%lld)", scr, MPEGTIME_TO_GSTTIME (scr), @@ -356,8 +353,7 @@ gst_mpeg_parse_parse_packhead (GstMPEGParse *mpeg_parse, GstBuffer *buffer) gst_index_add_association (mpeg_parse->index, mpeg_parse->index_id, GST_ACCOCIATION_FLAG_KEY_UNIT, GST_FORMAT_BYTES, GST_BUFFER_OFFSET (buffer), - GST_FORMAT_TIME, MPEGTIME_TO_GSTTIME (scr), - scr_format, scr_orig, 0); + GST_FORMAT_TIME, MPEGTIME_TO_GSTTIME (scr), 0); } mpeg_parse->current_scr = scr; @@ -367,10 +363,9 @@ gst_mpeg_parse_parse_packhead (GstMPEGParse *mpeg_parse, GstBuffer *buffer) mpeg_parse->mux_rate = new_rate; g_object_notify (G_OBJECT (mpeg_parse), "bitrate"); + GST_DEBUG (0, "stream is %1.3fMbs", (mpeg_parse->mux_rate * 400) / 1000000.0); } - GST_DEBUG (0, "stream is %1.3fMbs", (mpeg_parse->mux_rate * 400) / 1000000.0); - return TRUE; } @@ -433,7 +428,6 @@ gst_mpeg_parse_loop (GstElement *element) GST_DEBUG (GST_CAT_EVENT, "event: %d\n", GST_EVENT_TYPE (data)); mpeg_parse->discont_pending = TRUE; - mpeg_parse->scr_pending = TRUE; mpeg_parse->packetize->resync = TRUE; gst_event_unref (event); return; @@ -478,6 +472,7 @@ gst_mpeg_parse_loop (GstElement *element) gst_element_clock_wait (GST_ELEMENT (mpeg_parse), mpeg_parse->clock, time, NULL); } + if (mpeg_parse->current_scr != -1) { guint64 scr, bss, br; @@ -521,6 +516,46 @@ gst_mpeg_parse_get_src_formats (GstPad *pad) return formats; } +gboolean +gst_mpeg_parse_convert_src (GstPad *pad, GstFormat src_format, gint64 src_value, + GstFormat *dest_format, gint64 *dest_value) +{ + gboolean res = TRUE; + GstMPEGParse *mpeg_parse = GST_MPEG_PARSE (gst_pad_get_parent (pad)); + + switch (src_format) { + case GST_FORMAT_BYTES: + switch (*dest_format) { + case GST_FORMAT_DEFAULT: + *dest_format = GST_FORMAT_TIME; + case GST_FORMAT_TIME: + if (mpeg_parse->mux_rate == 0) + res = FALSE; + else + *dest_value = src_value * GST_SECOND / (mpeg_parse->mux_rate * 50); + break; + default: + res = FALSE; + } + break; + case GST_FORMAT_TIME: + switch (*dest_format) { + case GST_FORMAT_DEFAULT: + *dest_format = GST_FORMAT_BYTES; + case GST_FORMAT_BYTES: + *dest_value = mpeg_parse->mux_rate * 50 * src_value / GST_SECOND; + break; + default: + res = FALSE; + } + break; + default: + res = FALSE; + break; + } + return res; +} + const GstPadQueryType* gst_mpeg_parse_get_src_query_types (GstPad *pad) { @@ -538,6 +573,8 @@ gst_mpeg_parse_handle_src_query (GstPad *pad, GstPadQueryType type, { gboolean res = TRUE; GstMPEGParse *mpeg_parse = GST_MPEG_PARSE (gst_pad_get_parent (pad)); + GstFormat src_format; + gint64 src_value; switch (type) { case GST_PAD_QUERY_TOTAL: @@ -546,27 +583,13 @@ gst_mpeg_parse_handle_src_query (GstPad *pad, GstPadQueryType type, case GST_FORMAT_DEFAULT: *format = GST_FORMAT_TIME; /* fallthrough */ - case GST_FORMAT_TIME: - { - GstFormat peer_format; - gint64 peer_value; - - if (mpeg_parse->mux_rate == 0) - return FALSE; - - peer_format = GST_FORMAT_BYTES; - if (gst_pad_query (GST_PAD_PEER (mpeg_parse->sinkpad), - GST_PAD_QUERY_TOTAL, &peer_format, &peer_value)) - { - /* multiply bywith 8 because vbr is in bits/second */ - *value = peer_value * GST_SECOND / (mpeg_parse->mux_rate * 50); - } - else - res = FALSE; - break; - } default: - res = FALSE; + src_format = GST_FORMAT_BYTES; + if (!gst_pad_query (GST_PAD_PEER (mpeg_parse->sinkpad), + GST_PAD_QUERY_TOTAL, &src_format, &src_value)) + { + res = FALSE; + } break; } break; @@ -577,11 +600,9 @@ gst_mpeg_parse_handle_src_query (GstPad *pad, GstPadQueryType type, case GST_FORMAT_DEFAULT: *format = GST_FORMAT_TIME; /* fallthrough */ - case GST_FORMAT_TIME: - *value = MPEGTIME_TO_GSTTIME (mpeg_parse->current_scr); - break; default: - res = FALSE; + src_format = GST_FORMAT_TIME; + src_value = MPEGTIME_TO_GSTTIME (mpeg_parse->current_scr); break; } break; @@ -591,6 +612,10 @@ gst_mpeg_parse_handle_src_query (GstPad *pad, GstPadQueryType type, break; } + /* bring to requested format */ + if (res) + res = gst_pad_convert (pad, src_format, src_value, format, value); + return res; } @@ -605,23 +630,57 @@ gst_mpeg_parse_get_src_event_masks (GstPad *pad) } static gboolean -seek_index (GstMPEGParse *parse, GstEvent *event, guint64 *offset) +index_seek (GstPad *pad, GstEvent *event, guint64 *offset, gint64 *scr) { GstIndexEntry *entry; + GstMPEGParse *mpeg_parse = GST_MPEG_PARSE (gst_pad_get_parent (pad)); - entry = gst_index_get_assoc_entry (parse->index, parse->index_id, - GST_INDEX_LOOKUP_BEFORE, + entry = gst_index_get_assoc_entry (mpeg_parse->index, mpeg_parse->index_id, + GST_INDEX_LOOKUP_BEFORE, 0, GST_EVENT_SEEK_FORMAT (event), GST_EVENT_SEEK_OFFSET (event)); if (!entry) return FALSE; if (gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, offset)) { + gint64 time; + + if (gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &time)) { + *scr = GSTTIME_TO_MPEGTIME (time); + } return TRUE; } return FALSE; } +static gboolean +normal_seek (GstPad *pad, GstEvent *event, guint64 *offset, gint64 *scr) +{ + gboolean res; + GstFormat format; + gint64 time; + + /* bring offset to bytes */ + format = GST_FORMAT_BYTES; + res = gst_pad_convert (pad, + GST_EVENT_SEEK_FORMAT (event), + GST_EVENT_SEEK_OFFSET (event), + &format, + offset); + /* bring offset to time */ + format = GST_FORMAT_TIME; + res &= gst_pad_convert (pad, + GST_EVENT_SEEK_FORMAT (event), + GST_EVENT_SEEK_OFFSET (event), + &format, + &time); + + /* convert to scr */ + *scr = GSTTIME_TO_MPEGTIME (time); + + return res; +} + gboolean gst_mpeg_parse_handle_src_event (GstPad *pad, GstEvent *event) { @@ -632,37 +691,32 @@ gst_mpeg_parse_handle_src_event (GstPad *pad, GstEvent *event) case GST_EVENT_SEEK: { guint64 desired_offset; + guint64 expected_scr; - switch (GST_EVENT_SEEK_FORMAT (event)) { - case GST_FORMAT_BYTES: - if (mpeg_parse->index) { - if (!seek_index (mpeg_parse, event, &desired_offset)) { - goto done; - } - } - else { - desired_offset = GST_EVENT_SEEK_OFFSET (event); - } - break; - case GST_FORMAT_TIME: - desired_offset = mpeg_parse->mux_rate * 50 * GST_EVENT_SEEK_OFFSET (event) / (GST_SECOND); - break; - default: - goto done; - } + /* first to to use the index if we have one */ + if (mpeg_parse->index) + res = index_seek (pad, event, &desired_offset, &expected_scr); + /* nothing found, try fuzzy seek */ + if (!res) + res = normal_seek (pad, event, &desired_offset, &expected_scr); - if (!gst_bytestream_seek (mpeg_parse->packetize->bs, desired_offset, GST_SEEK_METHOD_SET)) { - goto done; + if (!res) + break; + + GST_DEBUG (0, "sending seek to %lld", desired_offset); + if (gst_bytestream_seek (mpeg_parse->packetize->bs, desired_offset, GST_SEEK_METHOD_SET)) { + mpeg_parse->discont_pending = TRUE; + mpeg_parse->scr_pending = TRUE; + mpeg_parse->next_scr = expected_scr; + mpeg_parse->current_scr = -1; + mpeg_parse->adjust = 0; + res = TRUE; } - mpeg_parse->discont_pending = TRUE; - mpeg_parse->scr_pending = TRUE; - res = TRUE; break; } default: break; } -done: gst_event_unref (event); return res; } @@ -677,6 +731,16 @@ gst_mpeg_parse_change_state (GstElement *element) if (!mpeg_parse->packetize) { mpeg_parse->packetize = gst_mpeg_packetize_new (mpeg_parse->sinkpad, GST_MPEG_PACKETIZE_SYSTEM); } + /* initialize parser state */ + mpeg_parse->current_scr = 0; + mpeg_parse->bytes_since_scr = 0; + mpeg_parse->adjust = 0; + mpeg_parse->next_scr = 0; + + /* zero counters (should be done at RUNNING?) */ + mpeg_parse->mux_rate = 0; + mpeg_parse->discont_pending = FALSE; + mpeg_parse->scr_pending = FALSE; break; case GST_STATE_PAUSED_TO_READY: if (mpeg_parse->packetize) { @@ -696,7 +760,6 @@ gst_mpeg_parse_get_property (GObject *object, guint prop_id, GValue *value, GPar { GstMPEGParse *mpeg_parse; - /* it's not null if we got it, but it might not be ours */ mpeg_parse = GST_MPEG_PARSE(object); switch (prop_id) { diff --git a/gst/mpegstream/gstmpegparse.h b/gst/mpegstream/gstmpegparse.h index 53b1c50490..e766f27383 100644 --- a/gst/mpegstream/gstmpegparse.h +++ b/gst/mpegstream/gstmpegparse.h @@ -44,6 +44,7 @@ extern "C" { #define GST_MPEG_PARSE_IS_MPEG2(parse) (GST_MPEG_PACKETIZE_IS_MPEG2 (GST_MPEG_PARSE (parse)->packetize)) #define MPEGTIME_TO_GSTTIME(time) (((time) * (GST_MSECOND/10)) / 9LL) +#define GSTTIME_TO_MPEGTIME(time) (((time) * 9) / (GST_MSECOND/10)) typedef struct _GstMPEGParse GstMPEGParse; typedef struct _GstMPEGParseClass GstMPEGParseClass; @@ -96,6 +97,8 @@ gboolean gst_mpeg_parse_plugin_init (GModule *module, GstPlugin *plugin); const GstFormat* gst_mpeg_parse_get_src_formats (GstPad *pad); +gboolean gst_mpeg_parse_convert_src (GstPad *pad, GstFormat src_format, gint64 src_value, + GstFormat *dest_format, gint64 *dest_value); const GstEventMask* gst_mpeg_parse_get_src_event_masks (GstPad *pad); gboolean gst_mpeg_parse_handle_src_event (GstPad *pad, GstEvent *event);