gst/wavparse/gstwavparse.*: Use information from 'fact' chunk for length calculation...
authorStefan Kost <ensonic@users.sourceforge.net>
Mon, 24 Jul 2006 13:40:56 +0000 (13:40 +0000)
committerStefan Kost <ensonic@users.sourceforge.net>
Mon, 24 Jul 2006 13:40:56 +0000 (13:40 +0000)
Original commit message from CVS:
* gst/wavparse/gstwavparse.c: (gst_wavparse_reset),
(gst_wavparse_other), (gst_wavparse_perform_seek),
(gst_wavparse_get_upstream_size), (gst_wavparse_stream_headers),
(gst_wavparse_add_src_pad), (gst_wavparse_stream_data),
(gst_wavparse_pad_query):
* gst/wavparse/gstwavparse.h:
Use information from 'fact' chunk for length calculation of compressed
samples. Calculate bps if bogus value is found in wav header (embeded
mp2/mp3).

ChangeLog
common
gst/wavparse/gstwavparse.c
gst/wavparse/gstwavparse.h

index b6c5ed5..1b70fcd 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2006-07-24  Stefan Kost,,,  <set EMAIL_ADDRESS environment variable>
+
+       * gst/wavparse/gstwavparse.c: (gst_wavparse_reset),
+       (gst_wavparse_other), (gst_wavparse_perform_seek),
+       (gst_wavparse_get_upstream_size), (gst_wavparse_stream_headers),
+       (gst_wavparse_add_src_pad), (gst_wavparse_stream_data),
+       (gst_wavparse_pad_query):
+       * gst/wavparse/gstwavparse.h:
+         Use information from 'fact' chunk for length calculation of compressed
+         samples. Calculate bps if bogus value is found in wav header (embeded
+         mp2/mp3).
+         
+
 2006-07-24  Tim-Philipp Müller  <tim at centricular dot net>
 
        Based on patch by: Joni Valtanen  <joni dot valtanen at movial fi>
diff --git a/common b/common
index 743c74b..ef97fb3 160000 (submodule)
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit 743c74bf92546638d3f4272fd5525bf6ef71f794
+Subproject commit ef97fb3278d98a1fdb32e5c6b2a7467116ffc160
index 04cffec..1e66138 100644 (file)
  * Last reviewed on 2006-03-03 (0.10.3)
  */
 
+/*
+ * TODO:
+ * http://replaygain.hydrogenaudio.org/file_format_wav.html
+ */
+
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
@@ -228,6 +233,7 @@ gst_wavparse_reset (GstWavParse * wavparse)
   wavparse->channels = 0;
   wavparse->blockalign = 0;
   wavparse->bps = 0;
+  wavparse->fact = 0;
   wavparse->offset = 0;
   wavparse->end_offset = 0;
   wavparse->dataleft = 0;
@@ -693,7 +699,8 @@ gst_wavparse_other (GstWavParse * wav)
         }
       }
       wav->datasize = (guint64) length;
-      break;
+      GST_DEBUG_OBJECT (wav, "datasize = %ld", length)
+          break;
 
     case GST_RIFF_TAG_cue:
       if (!gst_riff_read_skip (wav)) {
@@ -775,7 +782,7 @@ gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event)
   gint64 cur, stop, upstream_size;
   gboolean flush;
   gboolean update;
-  GstSegment seeksegment;
+  GstSegment seeksegment = { 0, };
 
   if (event) {
     GST_DEBUG_OBJECT (wav, "doing seek with event");
@@ -825,23 +832,36 @@ gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event)
         cur_type, cur, stop_type, stop, &update);
   }
 
-  if ((stop = seeksegment.stop) == -1)
+  if ((stop = seeksegment.stop) == GST_CLOCK_TIME_NONE)
     stop = seeksegment.duration;
 
-  if (cur_type != GST_SEEK_TYPE_NONE) {
+  GST_DEBUG_OBJECT (wav, "cur_type =%d", cur_type);
+  if ((cur_type != GST_SEEK_TYPE_NONE) &&
+      (seeksegment.last_stop != GST_CLOCK_TIME_NONE)) {
     wav->offset =
         gst_util_uint64_scale_int (seeksegment.last_stop, wav->bps, GST_SECOND);
-    wav->offset -= wav->offset % wav->bytes_per_sample;
+    GST_LOG_OBJECT (wav, "offset=%" G_GUINT64_FORMAT, wav->offset);
+    wav->offset -= (wav->offset % wav->bytes_per_sample);
+    GST_LOG_OBJECT (wav, "offset=%" G_GUINT64_FORMAT, wav->offset);
     wav->offset += wav->datastart;
+    GST_LOG_OBJECT (wav, "offset=%" G_GUINT64_FORMAT, wav->offset);
+  } else {
+    GST_DEBUG_OBJECT (wav, "last_stop == -1");
+    wav->offset = wav->datastart;
+    GST_LOG_OBJECT (wav, "offset=%" G_GUINT64_FORMAT, wav->offset);
   }
 
-  if (stop != -1) {
+  if (stop != GST_CLOCK_TIME_NONE) {
     wav->end_offset = gst_util_uint64_scale_int (stop, wav->bps, GST_SECOND);
-    wav->end_offset +=
-        wav->bytes_per_sample - (wav->end_offset % wav->bytes_per_sample);
+    GST_LOG_OBJECT (wav, "end_offset=%" G_GUINT64_FORMAT, wav->end_offset);
+    wav->end_offset -= (wav->end_offset % wav->bytes_per_sample);
+    GST_LOG_OBJECT (wav, "end_offset=%" G_GUINT64_FORMAT, wav->end_offset);
     wav->end_offset += wav->datastart;
+    GST_LOG_OBJECT (wav, "end_offset=%" G_GUINT64_FORMAT, wav->end_offset);
   } else {
+    GST_DEBUG_OBJECT (wav, "stop == -1");
     wav->end_offset = wav->datasize + wav->datastart;
+    GST_LOG_OBJECT (wav, "end_offset=%" G_GUINT64_FORMAT, wav->end_offset);
   }
 
   /* make sure filesize is not exceeded due to rounding errors or so,
@@ -979,13 +999,20 @@ gst_wavparse_peek_chunk (GstWavParse * wav, guint32 * tag, guint32 * size)
   }
 }
 
-/* FIXME: remove once -base 0.10.9 is out */
-#ifndef GST_RIFF_TAG_bext
-#define GST_RIFF_TAG_bext  GST_MAKE_FOURCC ('b', 'e', 'x', 't')
-#endif
-#ifndef GST_RIFF_TAG_BEXT
-#define GST_RIFF_TAG_BEXT  GST_MAKE_FOURCC ('B', 'E', 'X', 'T')
-#endif
+static gboolean
+gst_wavparse_get_upstream_size (GstWavParse * wav, gint64 * len)
+{
+  gboolean res = FALSE;
+  GstFormat fmt = GST_FORMAT_BYTES;
+  GstPad *peer;
+
+  if ((peer = gst_pad_get_peer (wav->sinkpad))) {
+    res = gst_pad_query_duration (peer, &fmt, len);
+    gst_object_unref (peer);
+  }
+
+  return res;
+}
 
 static GstFlowReturn
 gst_wavparse_stream_headers (GstWavParse * wav)
@@ -1048,8 +1075,7 @@ gst_wavparse_stream_headers (GstWavParse * wav)
 
     /* Note: gst_riff_create_audio_caps might need to fix values in
      * the header header depending on the format, so call it first */
-    caps =
-        gst_riff_create_audio_caps (header->format, NULL, header, extra,
+    caps = gst_riff_create_audio_caps (header->format, NULL, header, extra,
         NULL, &codec_name);
 
     if (extra)
@@ -1058,24 +1084,29 @@ gst_wavparse_stream_headers (GstWavParse * wav)
     wav->format = header->format;
     wav->rate = header->rate;
     wav->channels = header->channels;
-
-    if (wav->channels == 0)
-      goto no_channels;
-
     wav->blockalign = header->blockalign;
-    wav->width = (header->blockalign * 8) / header->channels;
     wav->depth = header->size;
     wav->bps = header->av_bps;
 
-    if (wav->bps <= 0)
-      goto no_bitrate;
+    g_free (header);
 
+    if (wav->channels == 0)
+      goto no_channels;
+
+    if (wav->bps == 0 && (wav->format == GST_RIFF_WAVE_FORMAT_MPEGL12 ||
+            wav->format == GST_RIFF_WAVE_FORMAT_MPEGL3)) {
+      /* Note: ugly workaround for mp2/mp3 embedded in wav, that relies on the
+       * bitrate inside the mpeg stream */
+      /* wav->bps = 1; */
+      GST_INFO ("WAV file with bps==0 and format=mp2/3");
+    }
+
+    wav->width = (wav->blockalign * 8) / wav->channels;
     wav->bytes_per_sample = wav->channels * wav->width / 8;
+
     if (wav->bytes_per_sample <= 0)
       goto no_bytes_per_sample;
 
-    g_free (header);
-
     if (!caps)
       goto unknown_format;
 
@@ -1083,6 +1114,11 @@ gst_wavparse_stream_headers (GstWavParse * wav)
     GST_DEBUG_OBJECT (wav, "width      = %u", (guint) wav->width);
     GST_DEBUG_OBJECT (wav, "depth      = %u", (guint) wav->depth);
     GST_DEBUG_OBJECT (wav, "bps        = %u", (guint) wav->bps);
+    GST_DEBUG_OBJECT (wav, "frequency  = %d", wav->rate);
+    GST_DEBUG_OBJECT (wav, "channels   = %u", (guint) wav->channels);
+    GST_DEBUG_OBJECT (wav, "bytes_per_sample = %u", wav->bytes_per_sample);
+
+    GST_DEBUG_OBJECT (wav, "caps = %" GST_PTR_FORMAT, caps);
 
     /* create pad later so we can sniff the first few bytes
      * of the real data and correct our caps if necessary */
@@ -1101,12 +1137,12 @@ gst_wavparse_stream_headers (GstWavParse * wav)
       codec_name = NULL;
     }
 
-    GST_DEBUG_OBJECT (wav, "frequency %d, channels %d", wav->rate,
-        wav->channels);
   }
 
   /* loop headers until we get data */
   while (!gotdata) {
+    gint64 upstream_size = 0;
+
     if (wav->streaming) {
       if (!gst_wavparse_peek_chunk_info (wav, &tag, &size))
         return GST_FLOW_OK;
@@ -1119,21 +1155,21 @@ gst_wavparse_stream_headers (GstWavParse * wav)
       size = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4);
     }
 
+    gst_wavparse_get_upstream_size (wav, &upstream_size);
+
     /*
        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) {
         /* TODO : Implement the various cases */
       case GST_RIFF_TAG_data:{
         GstFormat fmt;
-        gint64 upstream_size;
 
         GST_DEBUG_OBJECT (wav, "Got 'data' TAG, size : %d", size);
-        gotdata = TRUE;
         if (wav->streaming) {
           gst_adapter_flush (wav->adapter, 8);
+          gotdata = TRUE;
         } else {
           gst_buffer_unref (buf);
         }
@@ -1141,12 +1177,41 @@ gst_wavparse_stream_headers (GstWavParse * wav)
         wav->datastart = wav->offset;
         /* file might be truncated */
         fmt = GST_FORMAT_BYTES;
-        if (gst_pad_query_peer_duration (wav->sinkpad, &fmt, &upstream_size)) {
+        if (upstream_size) {
           size = MIN (size, (upstream_size - wav->datastart));
         }
-        wav->datasize = size;
-        wav->dataleft = size;
+        wav->datasize = (guint64) size;
+        wav->dataleft = (guint64) size;
         wav->end_offset = size + wav->datastart;
+        if (!wav->streaming) {
+          /* We will continue parsing tags 'till end */
+          wav->offset += size;
+        }
+        GST_DEBUG_OBJECT (wav, "datasize = %ld", size);
+        break;
+      }
+      case GST_RIFF_TAG_fact:{
+        /* number of samples (for compressed formats) */
+        if (wav->streaming) {
+          const guint8 *data = NULL;
+
+          if (gst_adapter_available (wav->adapter) < 8 + 4) {
+            return GST_FLOW_OK;
+          }
+          gst_adapter_flush (wav->adapter, 8);
+          data = gst_adapter_peek (wav->adapter, 4);
+          wav->fact = GST_READ_UINT32_LE (data);
+          gst_adapter_flush (wav->adapter, 4);
+        } else {
+          gst_buffer_unref (buf);
+          if ((res =
+                  gst_pad_pull_range (wav->sinkpad, wav->offset + 8, 4,
+                      &buf)) != GST_FLOW_OK)
+            goto header_read_error;
+          wav->fact = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf));
+          gst_buffer_unref (buf);
+        }
+        wav->offset += 8 + 4;
         break;
       }
       default:
@@ -1163,10 +1228,24 @@ gst_wavparse_stream_headers (GstWavParse * wav)
           gst_buffer_unref (buf);
         }
     }
+
+    if (upstream_size && (wav->offset >= upstream_size)) {
+      /* Now we are gone through the whole file */
+      gotdata = TRUE;
+    }
   }
 
   GST_DEBUG_OBJECT (wav, "Finished parsing headers");
 
+  if (wav->bps <= 0 && wav->fact) {
+    wav->bps =
+        (guint32) gst_util_uint64_scale ((guint64) wav->rate, wav->datasize,
+        (guint64) wav->fact);
+    GST_DEBUG_OBJECT (wav, "calculated bps : %ld", wav->bps);
+  }
+  if (wav->bps <= 0)
+    goto no_bitrate;
+
   duration = gst_util_uint64_scale_int (wav->datasize, GST_SECOND, wav->bps);
   GST_DEBUG_OBJECT (wav, "Got duration %" GST_TIME_FORMAT,
       GST_TIME_ARGS (duration));
@@ -1317,7 +1396,7 @@ gst_wavparse_add_src_pad (GstWavParse * wav, GstBuffer * buf)
   const guint8 dts_marker[] = { 0xFF, 0x1F, 0x00, 0xE8, 0xF1, 0x07 };
 
   s = gst_caps_get_structure (wav->caps, 0);
-  if (gst_structure_has_name (s, "audio/x-raw-int") &&
+  if (s && gst_structure_has_name (s, "audio/x-raw-int") && buf &&
       GST_BUFFER_SIZE (buf) > 6 &&
       memcmp (GST_BUFFER_DATA (buf), dts_marker, 6) == 0) {
 
@@ -1465,6 +1544,10 @@ found_eos:
           gst_message_new_segment_done (GST_OBJECT (wav), GST_FORMAT_TIME,
               stop));
     } else {
+      if (G_UNLIKELY (wav->first)) {
+        wav->first = FALSE;
+        gst_wavparse_add_src_pad (wav, NULL);
+      }
       gst_pad_push_event (wav->srcpad, gst_event_new_eos ());
     }
     return GST_FLOW_WRONG_STATE;
@@ -1475,13 +1558,15 @@ pull_error:
     if (res == GST_FLOW_UNEXPECTED)
       goto found_eos;
 
-    GST_DEBUG_OBJECT (wav, "Error getting %" G_GINT64_FORMAT " bytes from the "
+    GST_WARNING_OBJECT (wav,
+        "Error getting %" G_GINT64_FORMAT " bytes from the "
         "sinkpad (dataleft = %" G_GINT64_FORMAT ")", desired, wav->dataleft);
     return res;
   }
 push_error:
   {
-    GST_DEBUG_OBJECT (wav, "Error pushing on srcpad");
+    GST_WARNING_OBJECT (wav, "Error pushing on srcpad %p, is linked? = %d",
+        wav->srcpad, gst_pad_is_linked (wav->srcpad));
     return res;
   }
 }
@@ -1761,10 +1846,15 @@ gst_wavparse_pad_query (GstPad * pad, GstQuery * query)
       gst_query_parse_duration (query, &format, NULL);
 
       switch (format) {
-        case GST_FORMAT_TIME:
-          res &=
-              gst_wavparse_pad_convert (pad, GST_FORMAT_BYTES, endb,
-              &format, &end);
+        case GST_FORMAT_TIME:{
+          if (wav->fact) {
+            end = GST_SECOND * wav->fact / wav->rate;
+          } else {
+            res &=
+                gst_wavparse_pad_convert (pad, GST_FORMAT_BYTES, endb,
+                &format, &end);
+          }
+        }
           break;
         default:
           format = GST_FORMAT_BYTES;
index a8c88d6..b0a4dad 100644 (file)
@@ -81,6 +81,7 @@ struct _GstWavParse {
   guint16 blockalign;
   guint16 width;
   guint32 bps;
+  guint32 fact;
 
   guint bytes_per_sample;