diff options
Diffstat (limited to 'gst/asfdemux/gstasfdemux.c')
-rw-r--r-- | gst/asfdemux/gstasfdemux.c | 334 |
1 files changed, 301 insertions, 33 deletions
diff --git a/gst/asfdemux/gstasfdemux.c b/gst/asfdemux/gstasfdemux.c index caa4d3c..ec44035 100644 --- a/gst/asfdemux/gstasfdemux.c +++ b/gst/asfdemux/gstasfdemux.c @@ -14,8 +14,8 @@ * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ /* TODO: @@ -34,6 +34,7 @@ #include <gst/gstutils.h> #include <gst/base/gstbytereader.h> +#include <gst/base/gsttypefindhelper.h> #include <gst/riff/riff-media.h> #include <gst/tag/tag.h> #include <gst/gst-i18n-plugin.h> @@ -234,6 +235,8 @@ gst_asf_demux_reset (GstASFDemux * demux, gboolean chain_reset) /* do not remove those for not adding pads with same name */ demux->num_audio_streams = 0; demux->num_video_streams = 0; + demux->have_group_id = FALSE; + demux->group_id = G_MAXUINT; } demux->num_streams = 0; demux->activated_streams = FALSE; @@ -256,6 +259,7 @@ gst_asf_demux_reset (GstASFDemux * demux, gboolean chain_reset) GST_LOG_OBJECT (demux, "Restarting"); gst_segment_init (&demux->segment, GST_FORMAT_TIME); demux->need_newsegment = TRUE; + demux->segment_seqnum = 0; demux->segment_running = FALSE; demux->accurate = FALSE; demux->metadata = gst_caps_new_empty (); @@ -266,6 +270,9 @@ gst_asf_demux_reset (GstASFDemux * demux, gboolean chain_reset) } else { demux->base_offset = 0; } + + g_slist_free (demux->other_streams); + demux->other_streams = NULL; } static void @@ -390,6 +397,7 @@ gst_asf_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) demux->segment_ts = GST_CLOCK_TIME_NONE; demux->in_gap = GST_CLOCK_TIME_NONE; demux->need_newsegment = TRUE; + demux->segment_seqnum = gst_event_get_seqnum (event); gst_asf_demux_reset_stream_state_after_discont (demux); GST_OBJECT_UNLOCK (demux); @@ -552,6 +560,7 @@ gst_asf_demux_handle_seek_push (GstASFDemux * demux, GstEvent * event) gint64 cur, stop; guint packet; gboolean res; + GstEvent *byte_event; gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, &stop_type, &stop); @@ -582,9 +591,10 @@ gst_asf_demux_handle_seek_push (GstASFDemux * demux, GstEvent * event) GST_DEBUG_OBJECT (demux, "Pushing BYTE seek rate %g, " "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, cur, stop); /* BYTE seek event */ - event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, cur, - stop_type, stop); - res = gst_pad_push_event (demux->sinkpad, event); + byte_event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, + cur, stop_type, stop); + gst_event_set_seqnum (byte_event, gst_event_get_seqnum (event)); + res = gst_pad_push_event (demux->sinkpad, byte_event); return res; } @@ -605,6 +615,8 @@ gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event) gint64 seek_time; guint packet, speed_count = 1; gboolean eos; + guint32 seqnum; + GstEvent *fevent; if (G_UNLIKELY (demux->seekable == FALSE || demux->packet_size == 0 || demux->num_packets == 0 || demux->play_time == 0)) { @@ -619,6 +631,7 @@ gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event) gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, &stop_type, &stop); + seqnum = gst_event_get_seqnum (event); if (G_UNLIKELY (format != GST_FORMAT_TIME)) { GST_LOG_OBJECT (demux, "seeking is only supported in TIME format"); @@ -661,8 +674,11 @@ gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event) /* unlock the streaming thread */ if (G_LIKELY (flush)) { - gst_pad_push_event (demux->sinkpad, gst_event_new_flush_start ()); - gst_asf_demux_send_event_unlocked (demux, gst_event_new_flush_start ()); + fevent = gst_event_new_flush_start (); + + gst_event_set_seqnum (fevent, seqnum); + gst_pad_push_event (demux->sinkpad, gst_event_ref (fevent)); + gst_asf_demux_send_event_unlocked (demux, fevent); } else { gst_pad_pause_task (demux->sinkpad); } @@ -673,10 +689,14 @@ gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event) GST_PAD_STREAM_LOCK (demux->sinkpad); /* we now can stop flushing, since we have the stream lock now */ - gst_pad_push_event (demux->sinkpad, gst_event_new_flush_stop (TRUE)); + fevent = gst_event_new_flush_stop (TRUE); + gst_event_set_seqnum (fevent, seqnum); + gst_pad_push_event (demux->sinkpad, gst_event_ref (fevent)); if (G_LIKELY (flush)) - gst_asf_demux_send_event_unlocked (demux, gst_event_new_flush_stop (TRUE)); + gst_asf_demux_send_event_unlocked (demux, fevent); + else + gst_event_unref (fevent); /* operating on copy of segment until we know the seek worked */ segment = demux->segment; @@ -688,6 +708,7 @@ gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event) /* create the segment event to close the current segment */ gst_segment_copy_into (&segment, &newsegment); newseg = gst_event_new_segment (&newsegment); + gst_event_set_seqnum (newseg, seqnum); gst_asf_demux_send_event_unlocked (demux, newseg); } @@ -758,6 +779,7 @@ gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event) demux->segment = segment; demux->packet = packet; demux->need_newsegment = TRUE; + demux->segment_seqnum = seqnum; demux->speed_packets = speed_count; gst_asf_demux_reset_stream_state_after_discont (demux); GST_OBJECT_UNLOCK (demux); @@ -1219,9 +1241,9 @@ all_streams_prerolled (GstASFDemux * demux) * and (b) the timestamp of last piece of data queued is < demux->preroll * AND there is at least one other stream with data queued */ for (i = 0; i < demux->num_streams; ++i) { - AsfPayload *last_payload; + AsfPayload *last_payload = NULL; AsfStream *stream; - guint last_idx; + gint last_idx; stream = &demux->stream[i]; if (G_UNLIKELY (stream->payloads->len == 0)) { @@ -1230,19 +1252,24 @@ all_streams_prerolled (GstASFDemux * demux) continue; } - last_idx = stream->payloads->len - 1; - last_payload = &g_array_index (stream->payloads, AsfPayload, last_idx); + /* find last payload with timestamp */ + for (last_idx = stream->payloads->len - 1; + last_idx >= 0 && (last_payload == NULL + || !GST_CLOCK_TIME_IS_VALID (last_payload->ts)); --last_idx) { + last_payload = &g_array_index (stream->payloads, AsfPayload, last_idx); + } GST_LOG_OBJECT (stream->pad, "checking if %" GST_TIME_FORMAT " > %" GST_TIME_FORMAT, GST_TIME_ARGS (last_payload->ts), GST_TIME_ARGS (preroll_time)); - if (G_UNLIKELY (last_payload->ts <= preroll_time)) { + if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (last_payload->ts) + || last_payload->ts <= preroll_time)) { GST_LOG_OBJECT (stream->pad, "not beyond preroll point yet"); return FALSE; } } - if (G_UNLIKELY (num_no_data == demux->num_streams)) + if (G_UNLIKELY (num_no_data > 0)) return FALSE; return TRUE; @@ -1287,6 +1314,140 @@ gst_asf_demux_have_mutually_exclusive_active_stream (GstASFDemux * demux, } #endif +static void +gst_asf_demux_check_segment_ts (GstASFDemux * demux, GstClockTime payload_ts) +{ + /* remember the first queued timestamp for the segment */ + if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (demux->segment_ts) && + GST_CLOCK_TIME_IS_VALID (demux->first_ts))) { + GST_DEBUG_OBJECT (demux, "segment ts: %" GST_TIME_FORMAT, + GST_TIME_ARGS (demux->first_ts)); + demux->segment_ts = payload_ts; + /* always note, but only determines segment when streaming */ + if (demux->streaming) + gst_segment_do_seek (&demux->segment, demux->in_segment.rate, + GST_FORMAT_TIME, (GstSeekFlags) demux->segment.flags, + GST_SEEK_TYPE_SET, demux->segment_ts, GST_SEEK_TYPE_NONE, 0, NULL); + } +} + +static gboolean +gst_asf_demux_check_first_ts (GstASFDemux * demux, gboolean force) +{ + if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (demux->first_ts))) { + GstClockTime first_ts = GST_CLOCK_TIME_NONE; + int i; + + /* go trhough each stream, find smallest timestamp */ + for (i = 0; i < demux->num_streams; ++i) { + AsfStream *stream; + int j; + GstClockTime stream_min_ts = GST_CLOCK_TIME_NONE; + stream = &demux->stream[i]; + + for (j = 0; j < stream->payloads->len; ++j) { + AsfPayload *payload = &g_array_index (stream->payloads, AsfPayload, j); + if (GST_CLOCK_TIME_IS_VALID (payload->ts) && + (!GST_CLOCK_TIME_IS_VALID (stream_min_ts) + || stream_min_ts > payload->ts)) + stream_min_ts = payload->ts; + } + + /* if we don't have timestamp for this stream, wait for more data */ + if (!GST_CLOCK_TIME_IS_VALID (stream_min_ts) && !force) + return FALSE; + + if (GST_CLOCK_TIME_IS_VALID (stream_min_ts) && + (!GST_CLOCK_TIME_IS_VALID (first_ts) || first_ts > stream_min_ts)) + first_ts = stream_min_ts; + } + + if (!GST_CLOCK_TIME_IS_VALID (first_ts)) /* can happen with force = TRUE */ + first_ts = 0; + + demux->first_ts = first_ts; + + /* update packets queued before we knew first timestamp */ + for (i = 0; i < demux->num_streams; ++i) { + AsfStream *stream; + int j; + stream = &demux->stream[i]; + + for (j = 0; j < stream->payloads->len; ++j) { + AsfPayload *payload = &g_array_index (stream->payloads, AsfPayload, j); + if (GST_CLOCK_TIME_IS_VALID (payload->ts)) { + if (payload->ts > first_ts) + payload->ts -= first_ts; + else + payload->ts = 0; + } + } + } + } + + gst_asf_demux_check_segment_ts (demux, 0); + + return TRUE; +} + +static gboolean +gst_asf_demux_update_caps_from_payload (GstASFDemux * demux, AsfStream * stream) +{ + /* try to determine whether the stream is AC-3 or MPEG; In dvr-ms the codecTag is unreliable + and often set wrong, inspecting the data is the only way that seem to be working */ + GstTypeFindProbability prob = GST_TYPE_FIND_NONE; + GstCaps *caps = NULL; + int i; + GstAdapter *adapter = gst_adapter_new (); + + for (i = 0; i < stream->payloads->len && prob < GST_TYPE_FIND_LIKELY; ++i) { + const guint8 *data; + AsfPayload *payload; + int len; + + payload = &g_array_index (stream->payloads, AsfPayload, i); + gst_adapter_push (adapter, gst_buffer_ref (payload->buf)); + len = gst_adapter_available (adapter); + data = gst_adapter_map (adapter, len); + + again: + +#define MIN_LENGTH 128 + + /* look for the sync points */ + while (TRUE) { + if (len < MIN_LENGTH || /* give typefind something to work on */ + (data[0] == 0x0b && data[1] == 0x77) || /* AC-3 sync point */ + (data[0] == 0xFF && ((data[1] & 0xF0) >> 4) == 0xF)) /* MPEG sync point */ + break; + ++data; + --len; + } + + gst_caps_take (&caps, gst_type_find_helper_for_data (GST_OBJECT (demux), + data, len, &prob)); + + if (prob < GST_TYPE_FIND_LIKELY) { + ++data; + --len; + if (len > MIN_LENGTH) + /* this wasn't it, look for another sync point */ + goto again; + } + + gst_adapter_unmap (adapter); + } + + gst_object_unref (adapter); + + if (caps) { + gst_caps_take (&stream->caps, caps); + return TRUE; + } else { + return FALSE; + } +} + static gboolean gst_asf_demux_check_activate_streams (GstASFDemux * demux, gboolean force) { @@ -1295,6 +1456,9 @@ gst_asf_demux_check_activate_streams (GstASFDemux * demux, gboolean force) if (demux->activated_streams) return TRUE; + if (G_UNLIKELY (!gst_asf_demux_check_first_ts (demux, force))) + return FALSE; + if (!all_streams_prerolled (demux) && !force) { GST_DEBUG_OBJECT (demux, "not all streams with data beyond preroll yet"); return FALSE; @@ -1304,6 +1468,14 @@ gst_asf_demux_check_activate_streams (GstASFDemux * demux, gboolean force) AsfStream *stream = &demux->stream[i]; if (stream->payloads->len > 0) { + + if (stream->inspect_payload && /* dvr-ms required payload inspection */ + !stream->active && /* do not inspect active streams (caps were already set) */ + !gst_asf_demux_update_caps_from_payload (demux, stream) && /* failed to determine caps */ + stream->payloads->len < 20) { /* if we couldn't determine the caps from 20 packets then just give up and use whatever was in codecTag */ + /* try to gather some more data */ + return FALSE; + } /* we don't check mutual exclusion stuff here; either we have data for * a stream, then we active it, or we don't, then we'll ignore it */ GST_LOG_OBJECT (stream->pad, "is prerolled - activate!"); @@ -1334,6 +1506,7 @@ gst_asf_demux_find_stream_with_complete_payload (GstASFDemux * demux) for (i = 0; i < demux->num_streams; ++i) { AsfStream *stream; + int j; stream = &demux->stream[i]; @@ -1342,11 +1515,20 @@ gst_asf_demux_find_stream_with_complete_payload (GstASFDemux * demux) * don't need to be decoded after a seek, sending only data from the * keyframe directly before our segment start */ if (stream->payloads->len > 0) { - AsfPayload *payload; - guint last_idx; + AsfPayload *payload = NULL; + gint last_idx; + + /* find last payload with timestamp */ + for (last_idx = stream->payloads->len - 1; + last_idx >= 0 && (payload == NULL + || !GST_CLOCK_TIME_IS_VALID (payload->ts)); --last_idx) { + payload = &g_array_index (stream->payloads, AsfPayload, last_idx); + } + + /* if this is first payload after seek we might need to update the segment */ + if (GST_CLOCK_TIME_IS_VALID (payload->ts)) + gst_asf_demux_check_segment_ts (demux, payload->ts); - last_idx = stream->payloads->len - 1; - payload = &g_array_index (stream->payloads, AsfPayload, last_idx); if (G_UNLIKELY (GST_CLOCK_TIME_IS_VALID (payload->ts) && (payload->ts < demux->segment.start))) { if (G_UNLIKELY ((!demux->accurate) && payload->keyframe)) { @@ -1366,7 +1548,14 @@ gst_asf_demux_find_stream_with_complete_payload (GstASFDemux * demux) /* Now see if there's a complete payload queued for this stream */ - payload = &g_array_index (stream->payloads, AsfPayload, 0); + payload = NULL; + /* find first complete payload with timestamp */ + for (j = 0; + j < stream->payloads->len && (payload == NULL + || !GST_CLOCK_TIME_IS_VALID (payload->ts)); ++j) { + payload = &g_array_index (stream->payloads, AsfPayload, j); + } + if (!gst_asf_payload_is_complete (payload)) continue; @@ -1393,18 +1582,19 @@ gst_asf_demux_push_complete_payloads (GstASFDemux * demux, gboolean force) /* streams are now activated */ } - /* wait until we had a chance to "lock on" some payload's timestamp */ - if (G_UNLIKELY (demux->need_newsegment - && !GST_CLOCK_TIME_IS_VALID (demux->segment_ts))) - return GST_FLOW_OK; - while ((stream = gst_asf_demux_find_stream_with_complete_payload (demux))) { AsfPayload *payload; + /* wait until we had a chance to "lock on" some payload's timestamp */ + if (G_UNLIKELY (demux->need_newsegment + && !GST_CLOCK_TIME_IS_VALID (demux->segment_ts))) + return GST_FLOW_OK; + payload = &g_array_index (stream->payloads, AsfPayload, 0); /* do we need to send a newsegment event */ if ((G_UNLIKELY (demux->need_newsegment))) { + GstEvent *segment_event; /* safe default if insufficient upstream info */ if (!GST_CLOCK_TIME_IS_VALID (demux->in_gap)) @@ -1429,8 +1619,10 @@ gst_asf_demux_push_complete_payloads (GstASFDemux * demux, gboolean force) &demux->segment); /* note: we fix up all timestamps to start from 0, so this should be ok */ - gst_asf_demux_send_event_unlocked (demux, - gst_event_new_segment (&demux->segment)); + segment_event = gst_event_new_segment (&demux->segment); + if (demux->segment_seqnum) + gst_event_set_seqnum (segment_event, demux->segment_seqnum); + gst_asf_demux_send_event_unlocked (demux, segment_event); /* now post any global tags we may have found */ if (demux->taglist == NULL) { @@ -1447,6 +1639,7 @@ gst_asf_demux_push_complete_payloads (GstASFDemux * demux, gboolean force) demux->taglist = NULL; demux->need_newsegment = FALSE; + demux->segment_seqnum = 0; demux->segment_running = TRUE; } @@ -1504,7 +1697,10 @@ gst_asf_demux_push_complete_payloads (GstASFDemux * demux, gboolean force) * typically useful for live src, but might (unavoidably) mess with * position reporting if a live src is playing not so live content * (e.g. rtspsrc taking some time to fall back to tcp) */ - GST_BUFFER_TIMESTAMP (payload->buf) = payload->ts + demux->in_gap; + GST_BUFFER_PTS (payload->buf) = payload->ts; + if (GST_BUFFER_PTS_IS_VALID (payload->buf)) { + GST_BUFFER_PTS (payload->buf) += demux->in_gap; + } if (payload->duration == GST_CLOCK_TIME_NONE && stream->ext_props.avg_time_per_frame != 0) GST_BUFFER_DURATION (payload->buf) = @@ -2172,7 +2368,8 @@ gst_asf_demux_get_stream (GstASFDemux * demux, guint16 id) return &demux->stream[i]; } - GST_WARNING ("Segment found for undefined stream: (%d)", id); + if (gst_asf_demux_is_unknown_stream (demux, id)) + GST_WARNING ("Segment found for undefined stream: (%d)", id); return NULL; } @@ -2368,6 +2565,7 @@ static void gst_asf_demux_activate_stream (GstASFDemux * demux, AsfStream * stream) { if (!stream->active) { + GstEvent *event; gchar *stream_id; GST_INFO_OBJECT (demux, "Activating stream %2u, pad %s, caps %" @@ -2376,8 +2574,26 @@ gst_asf_demux_activate_stream (GstASFDemux * demux, AsfStream * stream) stream_id = gst_pad_create_stream_id_printf (stream->pad, GST_ELEMENT_CAST (demux), - "%u", stream->id); - gst_pad_push_event (stream->pad, gst_event_new_stream_start (stream_id)); + "%03u", stream->id); + + event = + gst_pad_get_sticky_event (demux->sinkpad, GST_EVENT_STREAM_START, 0); + if (event) { + if (gst_event_parse_group_id (event, &demux->group_id)) + demux->have_group_id = TRUE; + else + demux->have_group_id = FALSE; + gst_event_unref (event); + } else if (!demux->have_group_id) { + demux->have_group_id = TRUE; + demux->group_id = gst_util_group_id_next (); + } + + event = gst_event_new_stream_start (stream_id); + if (demux->have_group_id) + gst_event_set_group_id (event, demux->group_id); + + gst_pad_push_event (stream->pad, event); g_free (stream_id); gst_pad_set_caps (stream->pad, stream->caps); @@ -2400,6 +2616,8 @@ gst_asf_demux_parse_stream_object (GstASFDemux * demux, guint8 * data, guint stream_specific_size; guint type_specific_size G_GNUC_UNUSED; guint unknown G_GNUC_UNUSED; + gboolean inspect_payload = FALSE; + AsfStream *stream; /* Get the rest of the header's header */ if (size < (16 + 16 + 8 + 4 + 4 + 2 + 4)) @@ -2424,6 +2642,25 @@ gst_asf_demux_parse_stream_object (GstASFDemux * demux, guint8 * data, GST_DEBUG_OBJECT (demux, "Found stream %u, time_offset=%" GST_TIME_FORMAT, stream_id, GST_TIME_ARGS (time_offset)); + /* dvr-ms has audio stream declared in stream specific data */ + if (stream_type == ASF_STREAM_EXT_EMBED_HEADER) { + AsfExtStreamType ext_stream_type; + gst_asf_demux_get_guid (&guid, &data, &size); + ext_stream_type = gst_asf_demux_identify_guid (asf_ext_stream_guids, &guid); + + if (ext_stream_type == ASF_EXT_STREAM_AUDIO) { + inspect_payload = TRUE; + + gst_asf_demux_get_guid (&guid, &data, &size); + gst_asf_demux_get_uint32 (&data, &size); + gst_asf_demux_get_uint32 (&data, &size); + gst_asf_demux_get_uint32 (&data, &size); + gst_asf_demux_get_guid (&guid, &data, &size); + gst_asf_demux_get_uint32 (&data, &size); + stream_type = ASF_STREAM_AUDIO; + } + } + switch (stream_type) { case ASF_STREAM_AUDIO:{ asf_stream_audio audio_object; @@ -2529,10 +2766,15 @@ gst_asf_demux_parse_stream_object (GstASFDemux * demux, guint8 * data, default: GST_WARNING_OBJECT (demux, "Unknown stream type for stream %u", stream_id); + demux->other_streams = + g_slist_append (demux->other_streams, GINT_TO_POINTER (stream_id)); break; } - return gst_asf_demux_get_stream (demux, stream_id); + stream = gst_asf_demux_get_stream (demux, stream_id); + if (stream) + stream->inspect_payload = inspect_payload; + return stream; not_enough_data: { @@ -3394,6 +3636,13 @@ not_enough_data: } } +gboolean +gst_asf_demux_is_unknown_stream (GstASFDemux * demux, guint stream_num) +{ + return g_slist_find (demux->other_streams, + GINT_TO_POINTER (stream_num)) == NULL; +} + static GstFlowReturn gst_asf_demux_process_ext_stream_props (GstASFDemux * demux, guint8 * data, guint64 size) @@ -3560,7 +3809,7 @@ done: GST_TAG_LANGUAGE_CODE, demux->languages[stream->ext_props.lang_idx], NULL); } - } else { + } else if (gst_asf_demux_is_unknown_stream (demux, stream_num)) { GST_WARNING_OBJECT (demux, "Ext. stream properties for unknown stream"); } @@ -4061,6 +4310,25 @@ gst_asf_demux_handle_src_query (GstPad * pad, GstObject * parent, gst_query_set_latency (query, live, min, max); break; } + case GST_QUERY_SEGMENT: + { + GstFormat format; + gint64 start, stop; + + format = demux->segment.format; + + start = + gst_segment_to_stream_time (&demux->segment, format, + demux->segment.start); + if ((stop = demux->segment.stop) == -1) + stop = demux->segment.duration; + else + stop = gst_segment_to_stream_time (&demux->segment, format, stop); + + gst_query_set_segment (query, demux->segment.rate, format, start, stop); + res = TRUE; + break; + } default: res = gst_pad_query_default (pad, parent, query); break; |