win32: remove outdated build cruft
[platform/upstream/gst-plugins-good.git] / gst / wavparse / gstwavparse.c
index 167e4ba..138ec16 100644 (file)
@@ -59,6 +59,8 @@
 GST_DEBUG_CATEGORY_STATIC (wavparse_debug);
 #define GST_CAT_DEFAULT (wavparse_debug)
 
+#define GST_RIFF_TAG_Fake GST_MAKE_FOURCC ('F','a','k','e')
+
 #define GST_BWF_TAG_iXML GST_MAKE_FOURCC ('i','X','M','L')
 #define GST_BWF_TAG_qlty GST_MAKE_FOURCC ('q','l','t','y')
 #define GST_BWF_TAG_mext GST_MAKE_FOURCC ('m','e','x','t')
@@ -66,6 +68,10 @@ GST_DEBUG_CATEGORY_STATIC (wavparse_debug);
 #define GST_BWF_TAG_link GST_MAKE_FOURCC ('l','i','n','k')
 #define GST_BWF_TAG_axml GST_MAKE_FOURCC ('a','x','m','l')
 
+/* Data size chunk of RF64,
+ * see http://tech.ebu.ch/docs/tech/tech3306-2009.pdf */
+#define GST_RS64_TAG_DS64 GST_MAKE_FOURCC ('d','s','6','4')
+
 static void gst_wavparse_dispose (GObject * object);
 
 static gboolean gst_wavparse_sink_activate (GstPad * sinkpad,
@@ -303,8 +309,7 @@ gst_wavparse_parse_file_header (GstElement * element, GstBuffer * buf)
 not_wav:
   {
     GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
-        ("File is not a WAVE file: %" GST_FOURCC_FORMAT,
-            GST_FOURCC_ARGS (doctype)));
+        ("File is not a WAVE file: 0x%" G_GINT32_MODIFIER "x", doctype));
     return FALSE;
   }
 }
@@ -345,8 +350,7 @@ gst_wavparse_time_to_bytepos (GstWavParse * wav, gint64 ts, gint64 * bytepos)
     *bytepos = gst_util_uint64_scale_ceil (ts, (guint64) wav->bps, GST_SECOND);
     return TRUE;
   } else if (wav->fact) {
-    guint64 bps =
-        gst_util_uint64_scale_int (wav->datasize, wav->rate, wav->fact);
+    guint64 bps = gst_util_uint64_scale (wav->datasize, wav->rate, wav->fact);
     *bytepos = gst_util_uint64_scale_ceil (ts, bps, GST_SECOND);
     return TRUE;
   }
@@ -374,12 +378,14 @@ gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event)
   gboolean update;
   GstSegment seeksegment = { 0, };
   gint64 last_stop;
+  guint32 seqnum = 0;
 
   if (event) {
     GST_DEBUG_OBJECT (wav, "doing seek with event");
 
     gst_event_parse_seek (event, &rate, &format, &flags,
         &cur_type, &cur, &stop_type, &stop);
+    seqnum = gst_event_get_seqnum (event);
 
     /* no negative rates yet */
     if (rate < 0.0)
@@ -445,6 +451,7 @@ gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event)
       /* BYTE seek event */
       event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, cur,
           stop_type, stop);
+      gst_event_set_seqnum (event, seqnum);
       res = gst_pad_push_event (wav->sinkpad, event);
     }
     return res;
@@ -460,8 +467,13 @@ gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event)
    * as it completes one iteration (and thus might block when the sink is
    * blocking in preroll). */
   if (flush) {
+    GstEvent *fevent;
     GST_DEBUG_OBJECT (wav, "sending flush start");
-    gst_pad_push_event (wav->srcpad, gst_event_new_flush_start ());
+
+    fevent = gst_event_new_flush_start ();
+    gst_event_set_seqnum (fevent, seqnum);
+    gst_pad_push_event (wav->sinkpad, gst_event_ref (fevent));
+    gst_pad_push_event (wav->srcpad, fevent);
   } else {
     gst_pad_pause_task (wav->sinkpad);
   }
@@ -529,6 +541,9 @@ gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event)
   if (gst_pad_peer_query_duration (wav->sinkpad, bformat, &upstream_size))
     wav->end_offset = MIN (wav->end_offset, upstream_size);
 
+  if (wav->datasize > 0 && wav->end_offset > wav->datastart + wav->datasize)
+    wav->end_offset = wav->datastart + wav->datasize;
+
   /* this is the range of bytes we will use for playback */
   wav->offset = MIN (wav->offset, wav->end_offset);
   wav->dataleft = wav->end_offset - wav->offset;
@@ -540,9 +555,15 @@ gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event)
 
   /* prepare for streaming again */
   if (flush) {
+    GstEvent *fevent;
+
     /* if we sent a FLUSH_START, we now send a FLUSH_STOP */
     GST_DEBUG_OBJECT (wav, "sending flush stop");
-    gst_pad_push_event (wav->srcpad, gst_event_new_flush_stop (TRUE));
+
+    fevent = gst_event_new_flush_stop (TRUE);
+    gst_event_set_seqnum (fevent, seqnum);
+    gst_pad_push_event (wav->sinkpad, gst_event_ref (fevent));
+    gst_pad_push_event (wav->srcpad, fevent);
   }
 
   /* now we did the seek and can activate the new segment values */
@@ -563,6 +584,7 @@ gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event)
   if (wav->start_segment)
     gst_event_unref (wav->start_segment);
   wav->start_segment = gst_event_new_segment (&wav->segment);
+  gst_event_set_seqnum (wav->start_segment, seqnum);
 
   /* mark discont if we are going to stream from another position. */
   if (last_stop != wav->segment.position) {
@@ -694,7 +716,7 @@ gst_wavparse_calculate_duration (GstWavParse * wav)
     return TRUE;
   } else if (wav->fact) {
     wav->duration =
-        gst_util_uint64_scale_int_ceil (GST_SECOND, wav->fact, wav->rate);
+        gst_util_uint64_scale_ceil (GST_SECOND, wav->fact, wav->rate);
     GST_INFO_OBJECT (wav, "Got duration (fact) %" GST_TIME_FORMAT,
         GST_TIME_ARGS (wav->duration));
     return TRUE;
@@ -887,6 +909,12 @@ gst_wavparse_adtl_chunk (GstWavParse * wav, const guint8 * data, guint32 size)
   while (size >= 8) {
     ltag = GST_READ_UINT32_LE (data + offset);
     lsize = GST_READ_UINT32_LE (data + offset + 4);
+
+    if (lsize + 8 > size) {
+      GST_WARNING_OBJECT (wav, "Invalid adtl size: %u + 8 > %u", lsize, size);
+      return FALSE;
+    }
+
     switch (ltag) {
       case GST_RIFF_TAG_labl:
         gst_wavparse_labl_chunk (wav, data + offset, size);
@@ -1025,6 +1053,31 @@ gst_wavparse_create_toc (GstWavParse * wav)
 
 #define MAX_BUFFER_SIZE 4096
 
+static gboolean
+parse_ds64 (GstWavParse * wav, GstBuffer * buf)
+{
+  GstMapInfo map;
+  guint32 dataSizeLow, dataSizeHigh;
+  guint32 sampleCountLow, sampleCountHigh;
+
+  gst_buffer_map (buf, &map, GST_MAP_READ);
+  dataSizeLow = GST_READ_UINT32_LE (map.data + 2 * 4);
+  dataSizeHigh = GST_READ_UINT32_LE (map.data + 3 * 4);
+  sampleCountLow = GST_READ_UINT32_LE (map.data + 4 * 4);
+  sampleCountHigh = GST_READ_UINT32_LE (map.data + 5 * 4);
+  gst_buffer_unmap (buf, &map);
+  if (dataSizeHigh != 0xFFFFFFFF && dataSizeLow != 0xFFFFFFFF) {
+    wav->datasize = ((guint64) dataSizeHigh << 32) | dataSizeLow;
+  }
+  if (sampleCountHigh != 0xFFFFFFFF && sampleCountLow != 0xFFFFFFFF) {
+    wav->fact = ((guint64) sampleCountHigh << 32) | sampleCountLow;
+  }
+
+  GST_DEBUG_OBJECT (wav, "Got 'ds64' TAG, datasize : %" G_GINT64_FORMAT
+      " fact: %" G_GINT64_FORMAT, wav->datasize, wav->fact);
+  return TRUE;
+}
+
 static GstFlowReturn
 gst_wavparse_stream_headers (GstWavParse * wav)
 {
@@ -1035,7 +1088,6 @@ gst_wavparse_stream_headers (GstWavParse * wav)
   gboolean gotdata = FALSE;
   GstCaps *caps = NULL;
   gchar *codec_name = NULL;
-  GstEvent **event_p;
   gint64 upstream_size = 0;
   GstStructure *s;
 
@@ -1071,7 +1123,8 @@ gst_wavparse_stream_headers (GstWavParse * wav)
         tag == GST_RIFF_TAG_id3 || tag == GST_RIFF_TAG_IDVX ||
         tag == GST_BWF_TAG_iXML || tag == GST_BWF_TAG_qlty ||
         tag == GST_BWF_TAG_mext || tag == GST_BWF_TAG_levl ||
-        tag == GST_BWF_TAG_link || tag == GST_BWF_TAG_axml) {
+        tag == GST_BWF_TAG_link || tag == GST_BWF_TAG_axml ||
+        tag == GST_RIFF_TAG_Fake) {
       GST_DEBUG_OBJECT (wav, "skipping %" GST_FOURCC_FORMAT " chunk",
           GST_FOURCC_ARGS (tag));
       gst_buffer_unref (buf);
@@ -1079,6 +1132,13 @@ gst_wavparse_stream_headers (GstWavParse * wav)
       continue;
     }
 
+    if (tag == GST_RS64_TAG_DS64) {
+      if (!parse_ds64 (wav, buf))
+        goto fail;
+      else
+        continue;
+    }
+
     if (tag != GST_RIFF_TAG_fmt)
       goto invalid_wav;
 
@@ -1218,18 +1278,33 @@ gst_wavparse_stream_headers (GstWavParse * wav)
     }
 
     GST_INFO_OBJECT (wav,
-        "Got TAG: %" GST_FOURCC_FORMAT ", offset %" G_GUINT64_FORMAT,
-        GST_FOURCC_ARGS (tag), wav->offset);
+        "Got TAG: %" GST_FOURCC_FORMAT ", offset %" G_GUINT64_FORMAT ", size %"
+        G_GUINT32_FORMAT, GST_FOURCC_ARGS (tag), wav->offset, size);
+
+    /* Maximum valid size is INT_MAX */
+    if (size & 0x80000000) {
+      GST_WARNING_OBJECT (wav, "Invalid size, clipping to 0x7fffffff");
+      size = 0x7fffffff;
+    }
+
+    /* Clip to upstream size if known */
+    if (wav->datasize > 0 && size + wav->offset > wav->datasize) {
+      GST_WARNING_OBJECT (wav, "Clipping chunk size to file size");
+      size = wav->datasize - wav->offset;
+    }
 
     /* wav is a st00pid format, we don't know for sure where data starts.
      * So we have to go bit by bit until we find the 'data' header
      */
     switch (tag) {
       case GST_RIFF_TAG_data:{
+        guint64 size64;
+
         GST_DEBUG_OBJECT (wav, "Got 'data' TAG, size : %u", size);
+        size64 = size;
         if (wav->ignore_length) {
           GST_DEBUG_OBJECT (wav, "Ignoring length");
-          size = 0;
+          size64 = 0;
         }
         if (wav->streaming) {
           gst_adapter_flush (wav->adapter, 8);
@@ -1239,27 +1314,33 @@ gst_wavparse_stream_headers (GstWavParse * wav)
         }
         wav->offset += 8;
         wav->datastart = wav->offset;
+        /* use size from ds64 chunk if available */
+        if (size64 == -1 && wav->datasize > 0) {
+          GST_DEBUG_OBJECT (wav, "Using ds64 datasize");
+          size64 = wav->datasize;
+        }
         /* If size is zero, then the data chunk probably actually extends to
            the end of the file */
-        if (size == 0 && upstream_size) {
-          size = upstream_size - wav->datastart;
+        if (size64 == 0 && upstream_size) {
+          size64 = upstream_size - wav->datastart;
         }
         /* Or the file might be truncated */
         else if (upstream_size) {
-          size = MIN (size, (upstream_size - wav->datastart));
+          size64 = MIN (size64, (upstream_size - wav->datastart));
         }
-        wav->datasize = (guint64) size;
-        wav->dataleft = (guint64) size;
-        wav->end_offset = size + wav->datastart;
+        wav->datasize = size64;
+        wav->dataleft = size64;
+        wav->end_offset = size64 + wav->datastart;
         if (!wav->streaming) {
           /* We will continue parsing tags 'till end */
-          wav->offset += size;
+          wav->offset += size64;
         }
-        GST_DEBUG_OBJECT (wav, "datasize = %u", size);
+        GST_DEBUG_OBJECT (wav, "datasize = %" G_GUINT64_FORMAT, size64);
         break;
       }
       case GST_RIFF_TAG_fact:{
-        if (wav->format != GST_RIFF_WAVE_FORMAT_MPEGL12 &&
+        if (wav->fact == 0 &&
+            wav->format != GST_RIFF_WAVE_FORMAT_MPEGL12 &&
             wav->format != GST_RIFF_WAVE_FORMAT_MPEGL3) {
           const guint data_size = 4;
 
@@ -1296,7 +1377,7 @@ gst_wavparse_stream_headers (GstWavParse * wav)
             wav->fact = GUINT32_FROM_LE (wav->fact);
             gst_buffer_unref (buf);
           }
-          GST_DEBUG_OBJECT (wav, "have fact %u", wav->fact);
+          GST_DEBUG_OBJECT (wav, "have fact %" G_GUINT64_FORMAT, wav->fact);
           wav->offset += 8 + GST_ROUND_UP_2 (size);
           break;
         } else {
@@ -1427,13 +1508,14 @@ gst_wavparse_stream_headers (GstWavParse * wav)
             break;
           }
           case GST_RIFF_LIST_adtl:{
-            const gint data_size = size;
+            const gint data_size = size - 4;
 
             GST_INFO_OBJECT (wav, "Have 'adtl' LIST, size %u", data_size);
             if (wav->streaming) {
               const guint8 *data = NULL;
 
               gst_adapter_flush (wav->adapter, 12);
+              wav->offset += 12;
               data = gst_adapter_map (wav->adapter, data_size);
               gst_wavparse_adtl_chunk (wav, data, data_size);
               gst_adapter_unmap (wav->adapter);
@@ -1442,8 +1524,9 @@ gst_wavparse_stream_headers (GstWavParse * wav)
 
               gst_buffer_unref (buf);
               buf = NULL;
+              wav->offset += 12;
               if ((res =
-                      gst_pad_pull_range (wav->sinkpad, wav->offset + 12,
+                      gst_pad_pull_range (wav->sinkpad, wav->offset,
                           data_size, &buf)) != GST_FLOW_OK)
                 goto header_read_error;
               gst_buffer_map (buf, &map, GST_MAP_READ);
@@ -1598,8 +1681,7 @@ gst_wavparse_stream_headers (GstWavParse * wav)
    * the right newsegment event downstream. */
   gst_wavparse_perform_seek (wav, wav->seek_event);
   /* remove pending event */
-  event_p = &wav->seek_event;
-  gst_event_replace (event_p, NULL);
+  gst_event_replace (&wav->seek_event, NULL);
 
   /* we just started, we are discont */
   wav->discont = TRUE;
@@ -1625,10 +1707,8 @@ gst_wavparse_stream_headers (GstWavParse * wav)
   /* ERROR */
 exit:
   {
-    if (codec_name)
-      g_free (codec_name);
-    if (header)
-      g_free (header);
+    g_free (codec_name);
+    g_free (header);
     if (caps)
       gst_caps_unref (caps);
     return res;
@@ -1739,7 +1819,6 @@ gst_wavparse_send_event (GstElement * element, GstEvent * event)
 {
   GstWavParse *wav = GST_WAVPARSE (element);
   gboolean res = FALSE;
-  GstEvent **event_p;
 
   GST_DEBUG_OBJECT (wav, "received event %s", GST_EVENT_TYPE_NAME (event));
 
@@ -1751,8 +1830,7 @@ gst_wavparse_send_event (GstElement * element, GstEvent * event)
       } else {
         GST_DEBUG_OBJECT (wav, "queuing seek for later");
 
-        event_p = &wav->seek_event;
-        gst_event_replace (event_p, event);
+        gst_event_replace (&wav->seek_event, event);
 
         /* we always return true */
         res = TRUE;
@@ -1773,12 +1851,16 @@ gst_wavparse_have_dts_caps (const GstCaps * caps, GstTypeFindProbability prob)
   s = gst_caps_get_structure (caps, 0);
   if (!gst_structure_has_name (s, "audio/x-dts"))
     return FALSE;
-  if (prob >= GST_TYPE_FIND_LIKELY)
+  /* typefind behavior for DTS:
+   *  MAXIMUM: multiple frame syncs detected, certainly DTS
+   *  LIKELY: single frame sync at offset 0.  Maybe DTS?
+   *  POSSIBLE: single frame sync, not at offset 0.  Highly unlikely
+   *    to be DTS.  */
+  if (prob > GST_TYPE_FIND_LIKELY)
     return TRUE;
-  /* DTS at non-0 offsets and without second sync may yield POSSIBLE .. */
-  if (prob < GST_TYPE_FIND_POSSIBLE)
+  if (prob <= GST_TYPE_FIND_POSSIBLE)
     return FALSE;
-  /* .. in which case we want at least a valid-looking rate and channels */
+  /* for maybe, check for at least a valid-looking rate and channels */
   if (!gst_structure_has_field (s, "channels"))
     return FALSE;
   /* and for extra assurance we could also check the rate from the DTS frame
@@ -2243,8 +2325,6 @@ gst_wavparse_flush_data (GstWavParse * wav)
   guint av;
 
   if ((av = gst_adapter_available (wav->adapter)) > 0) {
-    wav->dataleft = av;
-    wav->end_offset = wav->offset + av;
     ret = gst_wavparse_stream_data (wav);
   }
 
@@ -2294,8 +2374,8 @@ gst_wavparse_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
         }
         if (stop > 0) {
           end_offset = stop;
-          segment.stop -= wav->datastart;
-          segment.stop = MAX (stop, 0);
+          stop -= wav->datastart;
+          stop = MAX (stop, 0);
         }
         if (wav->segment.format == GST_FORMAT_TIME) {
           guint64 bps = wav->bps;
@@ -2343,7 +2423,12 @@ gst_wavparse_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
       /* and set up streaming thread for next one */
       wav->offset = offset;
       wav->end_offset = end_offset;
-      if (wav->end_offset > 0) {
+
+      if (wav->datasize > 0 && (wav->end_offset == -1
+              || wav->end_offset > wav->datastart + wav->datasize))
+        wav->end_offset = wav->datastart + wav->datasize;
+
+      if (wav->end_offset != -1) {
         wav->dataleft = wav->end_offset - wav->offset;
       } else {
         /* infinity; upstream will EOS when done */
@@ -2393,7 +2478,7 @@ gst_wavparse_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
 static const GstFormat *
 gst_wavparse_get_formats (GstPad * pad)
 {
-  static GstFormat formats[] = {
+  static const GstFormat formats[] = {
     GST_FORMAT_TIME,
     GST_FORMAT_BYTES,
     GST_FORMAT_DEFAULT,         /* a "frame", ie a set of samples per Hz */