asfdemux: Add support for dvr-ms
authorMatej Knopp <matej.knopp@gmail.com>
Sun, 28 Jul 2013 14:43:28 +0000 (16:43 +0200)
committerSebastian Dröge <slomo@circular-chaos.org>
Mon, 29 Jul 2013 08:07:55 +0000 (10:07 +0200)
https://bugzilla.gnome.org/show_bug.cgi?id=705026

gst/asfdemux/asfheaders.c
gst/asfdemux/asfheaders.h
gst/asfdemux/asfpacket.c
gst/asfdemux/gstasfdemux.c
gst/asfdemux/gstasfdemux.h

index ea5f953..f1488be 100644 (file)
@@ -30,6 +30,8 @@ const ASFGuidHash asf_payload_ext_guids[] = {
   {ASF_PAYLOAD_EXTENSION_SYSTEM_PIXEL_ASPECT_RATIO,
         "ASF_PAYLOAD_EXTENSION_SYSTEM_PIXEL_ASPECT_RATIO",
       {0x1b1ee554, 0x4bc8f9ea, 0x6b371a82, 0xb8c4e474}},
+  {ASF_PAYLOAD_EXTENSION_TIMING, "ASF_PAYLOAD_EXTENSION_TIMING",
+      {0XFD3CC02A, 0X4CFA06DB, 0X12721C80, 0XE44587D3}},
   {ASF_PAYLOAD_EXTENSION_UNDEFINED, "ASF_PAYLOAD_EXTENSION_UNDEFINED",
         {0, 0, 0, 0}
       }
@@ -58,11 +60,22 @@ const ASFGuidHash asf_stream_guids[] = {
   {ASF_STREAM_AUDIO, "ASF_STREAM_AUDIO",
         {0xF8699E40, 0x11CF5B4D, 0x8000FDA8, 0x2B445C5F}
       },
+  {ASF_STREAM_EXT_EMBED_HEADER, "ASF_STREAM_EXT_EMBED_HEADER",
+      {0X3AFB65E2, 0X40F247EF, 0XA9702CAC, 0X43D3710D}},
   {ASF_STREAM_UNDEFINED, "ASF_STREAM_UNDEFINED",
         {0, 0, 0, 0}
       }
 };
 
+const ASFGuidHash asf_ext_stream_guids[] = {
+  {ASF_EXT_STREAM_AUDIO, "ASF_EXT_STREAM_AUDIO",
+        {0X31178C9D, 0X452803E1, 0XF93D82B5, 0X03F522DB}
+      },
+  {ASF_EXT_STREAM_UNDEFINED, "ASF_EXT_STREAM_UNDEFINED",
+        {0, 0, 0, 0}
+      }
+};
+
 const ASFGuidHash asf_object_guids[] = {
   {ASF_OBJ_STREAM, "ASF_OBJ_STREAM",
         {0xB7DC0791, 0x11CFA9B7, 0xC000E68E, 0x6553200C}
index 8a38b5a..4b65b95 100644 (file)
@@ -74,10 +74,16 @@ typedef enum {
 typedef enum {
   ASF_STREAM_UNDEFINED = 0,
   ASF_STREAM_VIDEO,
-  ASF_STREAM_AUDIO
+  ASF_STREAM_AUDIO,
+  ASF_STREAM_EXT_EMBED_HEADER
 } AsfStreamType;
 
 typedef enum {
+  ASF_EXT_STREAM_UNDEFINED = 0,
+  ASF_EXT_STREAM_AUDIO
+} AsfExtStreamType;
+
+typedef enum {
   ASF_CORRECTION_UNDEFINED = 0,
   ASF_CORRECTION_ON,
   ASF_CORRECTION_OFF
@@ -87,7 +93,8 @@ typedef enum {
   ASF_PAYLOAD_EXTENSION_UNDEFINED = 0,
   ASF_PAYLOAD_EXTENSION_DURATION,
   ASF_PAYLOAD_EXTENSION_SYSTEM_CONTENT,
-  ASF_PAYLOAD_EXTENSION_SYSTEM_PIXEL_ASPECT_RATIO
+  ASF_PAYLOAD_EXTENSION_SYSTEM_PIXEL_ASPECT_RATIO,
+  ASF_PAYLOAD_EXTENSION_TIMING
 } AsfPayloadExtensionID;
 
 extern const ASFGuidHash asf_payload_ext_guids[];
@@ -96,6 +103,8 @@ extern const ASFGuidHash asf_correction_guids[];
 
 extern const ASFGuidHash asf_stream_guids[];
 
+extern const ASFGuidHash asf_ext_stream_guids[];
+
 extern const ASFGuidHash asf_object_guids[];
 
 /* GUID utilities */
index 9dc4237..ecf1e05 100644 (file)
@@ -134,27 +134,16 @@ gst_asf_payload_queue_for_stream (GstASFDemux * demux, AsfPayload * payload,
   GST_DEBUG_OBJECT (demux, "Got payload for stream %d ts:%" GST_TIME_FORMAT,
       stream->id, GST_TIME_ARGS (payload->ts));
 
-  /* Before preroll ts might be invalid and set to 0 */
-  if (G_UNLIKELY (payload->ts == 0 && demux->preroll)) {
-    payload->ts = GST_CLOCK_TIME_NONE;
-  }
-
-  /* remember the first timestamp in the stream */
-  if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (demux->first_ts) &&
-          GST_CLOCK_TIME_IS_VALID (payload->ts))) {
-    GST_DEBUG_OBJECT (demux, "first ts: %" GST_TIME_FORMAT,
-        GST_TIME_ARGS (payload->ts));
-    demux->first_ts = payload->ts;
+  /* make timestamps start from 0; first_ts will be determined during activation (once we have enough data),
+     which will also update ts of all packets queued before we knew first_ts;  */
+  if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (demux->first_ts)
+          && GST_CLOCK_TIME_IS_VALID (payload->ts))) {
+    if (payload->ts > demux->first_ts)
+      payload->ts -= demux->first_ts;
+    else
+      payload->ts = 0;
   }
 
-  /* make timestamps start from 0 */
-  if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (demux->first_ts) &&
-          GST_CLOCK_TIME_IS_VALID (payload->ts) &&
-          demux->first_ts < payload->ts))
-    payload->ts -= demux->first_ts;
-  else
-    payload->ts = 0;
-
   /* remove any incomplete payloads that will never be completed */
   while (stream->payloads->len > 0) {
     AsfPayload *prev;
@@ -201,19 +190,6 @@ gst_asf_payload_queue_for_stream (GstASFDemux * demux, AsfPayload * payload,
     GST_BUFFER_FLAG_SET (payload->buf, GST_BUFFER_FLAG_DISCONT);
   }
 
-  /* remember the first queued timestamp for the segment */
-  if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (demux->segment_ts) &&
-          GST_CLOCK_TIME_IS_VALID (payload->ts))) {
-    GST_DEBUG_OBJECT (demux, "segment ts: %" GST_TIME_FORMAT,
-        GST_TIME_ARGS (payload->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);
-  }
-
   g_array_append_vals (stream->payloads, payload, 1);
 }
 
@@ -223,29 +199,35 @@ asf_payload_parse_replicated_data_extensions (AsfStream * stream,
 {
   AsfPayloadExtension *ext;
   guint off;
+  guint16 ext_len;
 
   if (!stream->ext_props.valid || stream->ext_props.payload_extensions == NULL)
     return;
 
   off = 8;
   for (ext = stream->ext_props.payload_extensions; ext->len > 0; ++ext) {
-    if (G_UNLIKELY (off + ext->len > payload->rep_data_len)) {
+    ext_len = ext->len;
+    if (ext_len == 0xFFFF) {    /* extension length is determined by first two bytes in replicated data */
+      ext_len = GST_READ_UINT16_LE (payload->rep_data + off);
+      off += 2;
+    }
+    if (G_UNLIKELY (off + ext_len > payload->rep_data_len)) {
       GST_WARNING ("not enough replicated data for defined extensions");
       return;
     }
     switch (ext->id) {
       case ASF_PAYLOAD_EXTENSION_DURATION:
-        if (G_LIKELY (ext->len == 2)) {
+        if (G_LIKELY (ext_len == 2)) {
           guint16 tdur = GST_READ_UINT16_LE (payload->rep_data + off);
           /* packet durations of 1ms are mostly invalid */
           if (tdur != 1)
             payload->duration = tdur * GST_MSECOND;
         } else {
-          GST_WARNING ("unexpected DURATION extensions len %u", ext->len);
+          GST_WARNING ("unexpected DURATION extensions len %u", ext_len);
         }
         break;
       case ASF_PAYLOAD_EXTENSION_SYSTEM_CONTENT:
-        if (G_LIKELY (ext->len == 1)) {
+        if (G_LIKELY (ext_len == 1)) {
           guint8 data = payload->rep_data[off];
 
           payload->interlaced = data & 0x1;
@@ -254,24 +236,34 @@ asf_payload_parse_replicated_data_extensions (AsfStream * stream,
           GST_DEBUG ("SYSTEM_CONTENT: interlaced:%d, rff:%d, tff:%d",
               payload->interlaced, payload->rff, payload->tff);
         } else {
-          GST_WARNING ("unexpected SYSTEM_CONTE extensions len %u", ext->len);
+          GST_WARNING ("unexpected SYSTEM_CONTE extensions len %u", ext_len);
         }
         break;
       case ASF_PAYLOAD_EXTENSION_SYSTEM_PIXEL_ASPECT_RATIO:
-        if (G_LIKELY (ext->len == 2)) {
+        if (G_LIKELY (ext_len == 2)) {
           payload->par_x = payload->rep_data[off];
           payload->par_y = payload->rep_data[off + 1];
           GST_DEBUG ("PAR %d / %d", payload->par_x, payload->par_y);
         } else {
           GST_WARNING ("unexpected SYSTEM_PIXEL_ASPECT_RATIO extensions len %u",
-              ext->len);
+              ext_len);
         }
         break;
+      case ASF_PAYLOAD_EXTENSION_TIMING:
+      {
+        /* dvr-ms timing - this will override packet timestamp */
+        guint64 time = GST_READ_UINT64_LE (payload->rep_data + off + 8);
+        if (time != 0xFFFFFFFFFFFFFFFF)
+          payload->ts = time * 100;
+        else
+          payload->ts = GST_CLOCK_TIME_NONE;
+      }
+        break;
       default:
-        GST_WARNING ("UNKNOWN PAYLOAD EXTENSION !");
+        GST_LOG ("UNKNOWN PAYLOAD EXTENSION!");
         break;
     }
-    off += ext->len;
+    off += ext_len;
   }
 }
 
@@ -351,8 +343,10 @@ gst_asf_demux_parse_payload (GstASFDemux * demux, AsfPacket * packet,
   stream = gst_asf_demux_get_stream (demux, stream_num);
 
   if (G_UNLIKELY (stream == NULL)) {
-    GST_WARNING_OBJECT (demux, "Payload for unknown stream %u, skipping",
-        stream_num);
+    if (gst_asf_demux_is_unknown_stream (demux, stream_num)) {
+      GST_WARNING_OBJECT (demux, "Payload for unknown stream %u, skipping",
+          stream_num);
+    }
     if (*p_size < payload_len) {
       *p_data += *p_size;
       *p_size = 0;
index df0d86e..b50d4a1 100644 (file)
@@ -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>
@@ -269,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
@@ -1237,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)) {
@@ -1248,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;
@@ -1306,6 +1315,132 @@ gst_asf_demux_have_mutually_exclusive_active_stream (GstASFDemux * demux,
 #endif
 
 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;
+        }
+      }
+    }
+
+    /* remember the first queued timestamp for the segment */
+    if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (demux->segment_ts))) {
+      GST_DEBUG_OBJECT (demux, "segment ts: %" GST_TIME_FORMAT,
+          GST_TIME_ARGS (first_ts));
+      demux->segment_ts = first_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);
+    }
+  }
+
+  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;
+  GstAdapter *adapter = gst_adapter_new ();
+
+  for (int 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)
 {
   guint i;
@@ -1313,6 +1448,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;
@@ -1322,6 +1460,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!");
@@ -1352,6 +1498,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];
 
@@ -1360,11 +1507,15 @@ 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;
-
-      last_idx = stream->payloads->len - 1;
-      payload = &g_array_index (stream->payloads, AsfPayload, 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 (G_UNLIKELY (GST_CLOCK_TIME_IS_VALID (payload->ts) &&
               (payload->ts < demux->segment.start))) {
         if (G_UNLIKELY ((!demux->accurate) && payload->keyframe)) {
@@ -1384,7 +1535,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;
 
@@ -1526,7 +1684,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) =
@@ -2194,7 +2355,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;
 }
 
@@ -2441,6 +2603,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))
@@ -2459,12 +2623,31 @@ gst_asf_demux_parse_stream_object (GstASFDemux * demux, guint8 * data,
 
   flags = gst_asf_demux_get_uint16 (&data, &size);
   stream_id = flags & 0x7f;
-  is_encrypted = ! !((flags & 0x8000) << 15);
+  is_encrypted = !!((flags & 0x8000) << 15);
   unknown = gst_asf_demux_get_uint32 (&data, &size);
 
   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;
@@ -2570,10 +2753,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:
   {
@@ -3435,6 +3623,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)
@@ -3601,7 +3796,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");
   }
 
index 86cd571..deddb4d 100644 (file)
@@ -110,7 +110,8 @@ typedef struct
 
   /* extended stream properties (optional) */
   AsfStreamExtProps  ext_props;
-
+  
+  gboolean     inspect_payload;
 } AsfStream;
 
 typedef enum {
@@ -167,7 +168,7 @@ struct _GstASFDemux {
   AsfStream            old_stream[GST_ASF_DEMUX_NUM_STREAMS];
   gboolean             old_num_streams;
 
-  GstClockTime         first_ts;        /* first timestamp found        */
+  GstClockTime         first_ts;        /* smallest timestamp found        */
 
   guint32              packet_size;
   guint32              timestamp;       /* in milliseconds              */
@@ -203,6 +204,8 @@ struct _GstASFDemux {
   GstClockTime         sidx_interval;    /* interval between entries in ns */
   guint                sidx_num_entries; /* number of index entries        */
   AsfSimpleIndexEntry *sidx_entries;     /* packet number for each entry   */
+  
+  GSList              *other_streams;    /* remember streams that are in header but have unknown type */
 };
 
 struct _GstASFDemuxClass {
@@ -213,6 +216,8 @@ GType           gst_asf_demux_get_type (void);
 
 AsfStream     * gst_asf_demux_get_stream (GstASFDemux * demux, guint16 id);
 
+gboolean        gst_asf_demux_is_unknown_stream(GstASFDemux *demux, guint stream_num);
+
 G_END_DECLS
 
 #endif /* __ASF_DEMUX_H__ */