gst/wavparse/gstwavparse.*: Implement seek-query. Refactor duration calculations...
authorStefan Kost <ensonic@users.sourceforge.net>
Tue, 4 Sep 2007 07:58:36 +0000 (07:58 +0000)
committerStefan Kost <ensonic@users.sourceforge.net>
Tue, 4 Sep 2007 07:58:36 +0000 (07:58 +0000)
Original commit message from CVS:
* gst/wavparse/gstwavparse.c:
* gst/wavparse/gstwavparse.h:
Implement seek-query. Refactor duration calculations. Appropriate use
of uint64_scale_int and uint64_scale. Move repeadedly calculated stuff
out of loops.

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

index 7f1ae7c..f8175a5 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2007-09-04  Stefan Kost  <ensonic@users.sf.net>
+
+       * gst/wavparse/gstwavparse.c:
+       * gst/wavparse/gstwavparse.h:
+         Implement seek-query. Refactor duration calculations. Appropriate use
+         of uint64_scale_int and uint64_scale. Move repeadedly calculated stuff
+         out of loops.
+
 2007-09-03  Stefan Kost  <ensonic@users.sf.net>
 
        * gst/avi/gstavidemux.c:
index 68f7637..ed1a9b7 100644 (file)
@@ -175,6 +175,7 @@ gst_wavparse_reset (GstWavParse * wavparse)
   wavparse->dataleft = 0;
   wavparse->datasize = 0;
   wavparse->datastart = 0;
+  wavparse->duration = 0;
   wavparse->got_fmt = FALSE;
   wavparse->first = TRUE;
 
@@ -243,10 +244,22 @@ gst_wavparse_create_sourcepad (GstWavParse * wavparse)
 #define uint64_scale_modulo(val, nom, denom) \
   ((val % denom) * (nom % denom) % denom)
 
-/* Like gst_util_uint64_scale_int, but performs ceiling division. */
+/* Like gst_util_uint64_scale, but performs ceiling division. */
 static guint64
 uint64_ceiling_scale_int (guint64 val, gint num, gint denom)
 {
+  guint64 result = gst_util_uint64_scale (val, num, denom);
+
+  if (uint64_scale_modulo (val, num, denom) == 0)
+    return result;
+  else
+    return result + 1;
+}
+
+/* Like gst_util_uint64_scale, but performs ceiling division. */
+static guint64
+uint64_ceiling_scale (guint64 val, guint64 num, guint64 denom)
+{
   guint64 result = gst_util_uint64_scale_int (val, num, denom);
 
   if (uint64_scale_modulo (val, num, denom) == 0)
@@ -768,6 +781,7 @@ gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event)
   } else {
     GST_DEBUG_OBJECT (wav, "doing seek without event");
     flags = 0;
+    rate = 1.0;
     cur_type = GST_SEEK_TYPE_SET;
     stop_type = GST_SEEK_TYPE_SET;
   }
@@ -821,11 +835,16 @@ gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event)
     /* bring offset to bytes, if the bps is 0, we have the segment in BYTES and
      * we can just copy the last_stop. If not, we use the bps to convert TIME to
      * bytes. */
-    if (wav->bps)
+    if (wav->bps > 0)
       wav->offset =
-          uint64_ceiling_scale_int (seeksegment.last_stop, wav->bps,
+          uint64_ceiling_scale (seeksegment.last_stop, (guint64) wav->bps,
           GST_SECOND);
-    else
+    else if (wav->fact) {
+      guint64 bps =
+          gst_util_uint64_scale_int (wav->datasize, wav->rate, wav->fact);
+      wav->offset =
+          uint64_ceiling_scale (seeksegment.last_stop, bps, GST_SECOND);
+    } else
       wav->offset = seeksegment.last_stop;
     GST_LOG_OBJECT (wav, "offset=%" G_GUINT64_FORMAT, wav->offset);
     wav->offset -= (wav->offset % wav->bytes_per_sample);
@@ -838,9 +857,14 @@ gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event)
   }
 
   if (stop_type != GST_SEEK_TYPE_NONE) {
-    if (wav->bps)
-      wav->end_offset = uint64_ceiling_scale_int (stop, wav->bps, GST_SECOND);
-    else
+    if (wav->bps > 0)
+      wav->end_offset =
+          uint64_ceiling_scale (stop, (guint64) wav->bps, GST_SECOND);
+    else if (wav->fact) {
+      guint64 bps =
+          gst_util_uint64_scale_int (wav->datasize, wav->rate, wav->fact);
+      wav->end_offset = uint64_ceiling_scale (stop, bps, GST_SECOND);
+    } else
       wav->end_offset = stop;
     GST_LOG_OBJECT (wav, "end_offset=%" G_GUINT64_FORMAT, wav->end_offset);
     wav->end_offset -= (wav->end_offset % wav->bytes_per_sample);
@@ -863,9 +887,9 @@ gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event)
   wav->dataleft = wav->end_offset - wav->offset;
 
   GST_DEBUG_OBJECT (wav,
-      "seek: offset %" G_GUINT64_FORMAT ", end %" G_GUINT64_FORMAT ", segment %"
-      GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, wav->offset, wav->end_offset,
-      GST_TIME_ARGS (seeksegment.start), GST_TIME_ARGS (stop));
+      "seek: rate %lf, offset %" G_GUINT64_FORMAT ", end %" G_GUINT64_FORMAT
+      ", segment %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, wav->offset,
+      wav->end_offset, GST_TIME_ARGS (seeksegment.start), GST_TIME_ARGS (stop));
 
   /* prepare for streaming again */
   if (wav->srcpad) {
@@ -1002,19 +1026,34 @@ gst_wavparse_peek_chunk (GstWavParse * wav, guint32 * tag, guint32 * size)
   }
 }
 
+/*
+ * gst_wavparse_calculate_duration:
+ * @wav Wavparse object
+ *
+ * Calculate duration on demand and store in @wav. Prefer bps, but use fact as a
+ * fallback.
+ *
+ * Returns: %TRUE if duration is available.
+ */
 static gboolean
-gst_wavparse_get_upstream_size (GstWavParse * wav, gint64 * len)
+gst_wavparse_calculate_duration (GstWavParse * wav)
 {
-  gboolean res = FALSE;
-  GstFormat fmt = GST_FORMAT_BYTES;
-  GstPad *peer;
+  if (wav->duration > 0)
+    return TRUE;
 
-  if ((peer = gst_pad_get_peer (wav->sinkpad))) {
-    res = gst_pad_query_duration (peer, &fmt, len);
-    gst_object_unref (peer);
+  if (wav->bps > 0) {
+    wav->duration =
+        uint64_ceiling_scale (wav->datasize, GST_SECOND, (guint64) wav->bps);
+    GST_INFO_OBJECT (wav, "Got duration (bps) %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (wav->duration));
+    return TRUE;
+  } else if (wav->fact) {
+    wav->duration = uint64_ceiling_scale_int (GST_SECOND, wav->fact, wav->rate);
+    GST_INFO_OBJECT (wav, "Got duration (fact) %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (wav->duration));
+    return TRUE;
   }
-
-  return res;
+  return FALSE;
 }
 
 static GstFlowReturn
@@ -1026,9 +1065,10 @@ gst_wavparse_stream_headers (GstWavParse * wav)
   guint32 tag, size;
   gboolean gotdata = FALSE;
   GstCaps *caps;
-  gint64 duration;
   gchar *codec_name = NULL;
   GstEvent **event_p;
+  GstFormat bformat;
+  gint64 upstream_size = 0;
 
   while (!wav->got_fmt) {
     GstBuffer *extra;
@@ -1162,10 +1202,12 @@ gst_wavparse_stream_headers (GstWavParse * wav)
 
   }
 
+  bformat = GST_FORMAT_BYTES;
+  gst_pad_query_peer_duration (wav->sinkpad, &bformat, &upstream_size);
+  GST_DEBUG_OBJECT (wav, "upstream size %" G_GUINT64_FORMAT, upstream_size);
+
   /* 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;
@@ -1181,8 +1223,6 @@ gst_wavparse_stream_headers (GstWavParse * wav)
     GST_DEBUG_OBJECT (wav, "Got TAG: %" GST_FOURCC_FORMAT,
         GST_FOURCC_ARGS (tag));
 
-    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
      */
@@ -1264,19 +1304,19 @@ gst_wavparse_stream_headers (GstWavParse * wav)
   GST_DEBUG_OBJECT (wav, "Finished parsing headers");
 
   if (wav->bps <= 0 && wav->fact) {
+#if 0
+    /* not a good idea, as for embedded mp2/mp3 we set bps to 0 earlier */
     wav->bps =
         (guint32) gst_util_uint64_scale ((guint64) wav->rate, wav->datasize,
         (guint64) wav->fact);
-    GST_DEBUG_OBJECT (wav, "calculated bps : %d", wav->bps);
+    GST_INFO_OBJECT (wav, "calculated bps : %d, enabling VBR", wav->bps);
+#endif
     wav->vbr = TRUE;
   }
 
-  if (wav->bps > 0) {
-    duration = uint64_ceiling_scale_int (wav->datasize, GST_SECOND, wav->bps);
-    GST_DEBUG_OBJECT (wav, "Got duration %" GST_TIME_FORMAT,
-        GST_TIME_ARGS (duration));
+  if (gst_wavparse_calculate_duration (wav)) {
     gst_segment_init (&wav->segment, GST_FORMAT_TIME);
-    gst_segment_set_duration (&wav->segment, GST_FORMAT_TIME, duration);
+    gst_segment_set_duration (&wav->segment, GST_FORMAT_TIME, wav->duration);
   } else {
     /* no bitrate, let downstream peer do the math, we'll feed it bytes. */
     gst_segment_init (&wav->segment, GST_FORMAT_BYTES);
@@ -1563,23 +1603,21 @@ iterate_adapter:
   GST_BUFFER_OFFSET_END (buf) = nextpos / wav->bytes_per_sample;
 
   if (wav->bps > 0) {
-    /* and timestamps if we have a bitrate, be carefull for overflows */
-    timestamp = uint64_ceiling_scale_int (pos, GST_SECOND, wav->bps);
-    next_timestamp = uint64_ceiling_scale_int (nextpos, GST_SECOND, wav->bps);
+    /* and timestamps if we have a bitrate, be careful for overflows */
+    timestamp = uint64_ceiling_scale (pos, GST_SECOND, (guint64) wav->bps);
+    next_timestamp =
+        uint64_ceiling_scale (nextpos, GST_SECOND, (guint64) wav->bps);
     duration = next_timestamp - timestamp;
 
     /* update current running segment position */
     gst_segment_set_last_stop (&wav->segment, GST_FORMAT_TIME, next_timestamp);
-
-    if (wav->discont) {
-      GST_DEBUG_OBJECT (wav, "marking DISCONT");
-      GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
-      wav->discont = FALSE;
-    } else if (wav->vbr) {
-      /* don't set timestamps for VBR files if it's not the first buffer */
-      timestamp = GST_CLOCK_TIME_NONE;
-      duration = GST_CLOCK_TIME_NONE;
-    }
+  } else if (wav->fact) {
+    guint64 bps =
+        gst_util_uint64_scale_int (wav->datasize, wav->rate, wav->fact);
+    /* and timestamps if we have a bitrate, be careful for overflows */
+    timestamp = uint64_ceiling_scale (pos, GST_SECOND, bps);
+    next_timestamp = uint64_ceiling_scale (nextpos, GST_SECOND, bps);
+    duration = next_timestamp - timestamp;
   } else {
     /* no bitrate, all we know is that the first sample has timestamp 0, all
      * other positions and durations have unknown timestamp. */
@@ -1591,6 +1629,16 @@ iterate_adapter:
     /* update current running segment position with byte offset */
     gst_segment_set_last_stop (&wav->segment, GST_FORMAT_BYTES, nextpos);
   }
+  if ((pos > 0) && wav->vbr) {
+    /* don't set timestamps for VBR files if it's not the first buffer */
+    timestamp = GST_CLOCK_TIME_NONE;
+    duration = GST_CLOCK_TIME_NONE;
+  }
+  if (wav->discont) {
+    GST_DEBUG_OBJECT (wav, "marking DISCONT");
+    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
+    wav->discont = FALSE;
+  }
 
   GST_BUFFER_TIMESTAMP (buf) = timestamp;
   GST_BUFFER_DURATION (buf) = duration;
@@ -1641,8 +1689,10 @@ pull_error:
   }
 push_error:
   {
-    GST_WARNING_OBJECT (wav, "Error pushing on srcpad %p, is linked? = %d",
-        wav->srcpad, gst_pad_is_linked (wav->srcpad));
+    GST_INFO_OBJECT (wav,
+        "Error pushing on srcpad %s:%s, reason %s, is linked? = %d",
+        GST_DEBUG_PAD_NAME (wav->srcpad), gst_flow_get_name (res),
+        gst_pad_is_linked (wav->srcpad));
     return res;
   }
 }
@@ -1798,8 +1848,8 @@ gst_wavparse_pad_convert (GstPad * pad,
     return TRUE;
   }
 
-  if (wavparse->bps == 0)
-    goto no_bps;
+  if ((wavparse->bps == 0) && !wavparse->fact)
+    goto no_bps_fact;
 
   switch (src_format) {
     case GST_FORMAT_BYTES:
@@ -1810,8 +1860,16 @@ gst_wavparse_pad_convert (GstPad * pad,
           *dest_value -= *dest_value % wavparse->bytes_per_sample;
           break;
         case GST_FORMAT_TIME:
-          *dest_value =
-              gst_util_uint64_scale_int (src_value, GST_SECOND, wavparse->bps);
+          if (wavparse->bps > 0)
+            *dest_value = gst_util_uint64_scale (src_value, GST_SECOND,
+                (guint64) wavparse->bps);
+          else {
+            guint64 bps = gst_util_uint64_scale_int (wavparse->datasize,
+                wavparse->rate, wavparse->fact);
+
+            *dest_value =
+                gst_util_uint64_scale_int (src_value, GST_SECOND, bps);
+          }
           break;
         default:
           res = FALSE;
@@ -1825,8 +1883,8 @@ gst_wavparse_pad_convert (GstPad * pad,
           *dest_value = src_value * wavparse->bytes_per_sample;
           break;
         case GST_FORMAT_TIME:
-          *dest_value =
-              gst_util_uint64_scale_int (src_value, GST_SECOND, wavparse->rate);
+          *dest_value = gst_util_uint64_scale (src_value, GST_SECOND,
+              (guint64) wavparse->rate);
           break;
         default:
           res = FALSE;
@@ -1837,14 +1895,21 @@ gst_wavparse_pad_convert (GstPad * pad,
     case GST_FORMAT_TIME:
       switch (*dest_format) {
         case GST_FORMAT_BYTES:
+          if (wavparse->bps > 0)
+            *dest_value = gst_util_uint64_scale (src_value,
+                (guint64) wavparse->bps, GST_SECOND);
+          else {
+            guint64 bps = gst_util_uint64_scale_int (wavparse->datasize,
+                wavparse->rate, wavparse->fact);
+
+            *dest_value = gst_util_uint64_scale (src_value, bps, GST_SECOND);
+          }
           /* make sure we end up on a sample boundary */
-          *dest_value =
-              gst_util_uint64_scale_int (src_value, wavparse->bps, GST_SECOND);
           *dest_value -= *dest_value % wavparse->blockalign;
           break;
         case GST_FORMAT_DEFAULT:
-          *dest_value =
-              gst_util_uint64_scale_int (src_value, wavparse->rate, GST_SECOND);
+          *dest_value = gst_util_uint64_scale (src_value,
+              (guint64) wavparse->rate, GST_SECOND);
           break;
         default:
           res = FALSE;
@@ -1863,9 +1928,9 @@ done:
   return res;
 
   /* ERRORS */
-no_bps:
+no_bps_fact:
   {
-    GST_DEBUG_OBJECT (wavparse, "bps 0, cannot convert");
+    GST_DEBUG_OBJECT (wavparse, "bps 0 or no fact chunk, cannot convert");
     res = FALSE;
     goto done;
   }
@@ -1878,6 +1943,7 @@ gst_wavparse_get_query_types (GstPad * pad)
     GST_QUERY_POSITION,
     GST_QUERY_DURATION,
     GST_QUERY_CONVERT,
+    GST_QUERY_SEEKING,
     0
   };
 
@@ -1907,8 +1973,7 @@ gst_wavparse_pad_query (GstPad * pad, GstQuery * query)
 
       switch (format) {
         case GST_FORMAT_TIME:
-          res &=
-              gst_wavparse_pad_convert (pad, GST_FORMAT_BYTES, curb,
+          res = gst_wavparse_pad_convert (pad, GST_FORMAT_BYTES, curb,
               &format, &cur);
           break;
         default:
@@ -1922,31 +1987,27 @@ gst_wavparse_pad_query (GstPad * pad, GstQuery * query)
     }
     case GST_QUERY_DURATION:
     {
-      gint64 endb;
-      gint64 end;
+      gint64 duration;
       GstFormat format;
 
-      endb = wav->datasize;
       gst_query_parse_duration (query, &format, NULL);
 
       switch (format) {
         case GST_FORMAT_TIME:{
-          if (wav->fact) {
-            end = GST_SECOND * wav->fact / wav->rate;
+          if (gst_wavparse_calculate_duration (wav)) {
+            duration = wav->duration;
           } else {
-            res &=
-                gst_wavparse_pad_convert (pad, GST_FORMAT_BYTES, endb,
-                &format, &end);
+            format = GST_FORMAT_BYTES;
+            duration = wav->datasize;
           }
           break;
         }
         default:
           format = GST_FORMAT_BYTES;
-          end = endb;
+          duration = wav->datasize;
           break;
       }
-      if (res)
-        gst_query_set_duration (query, format, end);
+      gst_query_set_duration (query, format, duration);
       break;
     }
     case GST_QUERY_CONVERT:
@@ -1963,6 +2024,24 @@ gst_wavparse_pad_query (GstPad * pad, GstQuery * query)
         gst_query_set_convert (query, srcformat, srcvalue, dstformat, dstvalue);
       break;
     }
+    case GST_QUERY_SEEKING:{
+      GstFormat fmt;
+
+      gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
+      if (fmt == GST_FORMAT_TIME) {
+        gboolean seekable = TRUE;
+
+        if ((wav->bps == 0) && !wav->fact) {
+          seekable = FALSE;
+        } else if (!gst_wavparse_calculate_duration (wav)) {
+          seekable = FALSE;
+        }
+        gst_query_set_seeking (query, GST_FORMAT_TIME, seekable,
+            0, wav->duration);
+        res = TRUE;
+      }
+      break;
+    }
     default:
       res = gst_pad_query_default (pad, query);
       break;
index 2fad91d..97cf27f 100644 (file)
@@ -97,6 +97,8 @@ struct _GstWavParse {
   /* offset/length of data part */
   guint64      datastart;
   guint64      datasize;
+  /* duration in time */
+  guint64      duration;
 
   /* pending seek */
   GstEvent *seek_event;