ext/vorbis/vorbisenc.*: Handle discontinuities in the input vorbis stream correctly...
authorMichael Smith <msmith@xiph.org>
Fri, 28 Jul 2006 12:48:21 +0000 (12:48 +0000)
committerMichael Smith <msmith@xiph.org>
Fri, 28 Jul 2006 12:48:21 +0000 (12:48 +0000)
Original commit message from CVS:
* ext/vorbis/vorbisenc.c: (gst_vorbis_enc_generate_sink_caps),
(gst_vorbis_enc_sink_getcaps), (gst_vorbis_enc_buffer_from_packet),
(gst_vorbis_enc_push_buffer),
(gst_vorbis_enc_buffer_check_discontinuous),
(gst_vorbis_enc_chain), (gst_vorbis_enc_change_state):
* ext/vorbis/vorbisenc.h:
Handle discontinuities in the input vorbis stream correctly,
so that the output is properly timestamped (and has good granulepos
values). Needs some oggmux fixes too.

ChangeLog
ext/vorbis/vorbisenc.c
ext/vorbis/vorbisenc.h

index 4636563..c6c911f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2006-07-28  Michael Smith  <msmith@fluendo.com>
+
+       * ext/vorbis/vorbisenc.c: (gst_vorbis_enc_generate_sink_caps),
+       (gst_vorbis_enc_sink_getcaps), (gst_vorbis_enc_buffer_from_packet),
+       (gst_vorbis_enc_push_buffer),
+       (gst_vorbis_enc_buffer_check_discontinuous),
+       (gst_vorbis_enc_chain), (gst_vorbis_enc_change_state):
+       * ext/vorbis/vorbisenc.h:
+         Handle discontinuities in the input vorbis stream correctly,
+         so that the output is properly timestamped (and has good granulepos
+         values). Needs some oggmux fixes too.
+
 2006-07-27  Wim Taymans  <wim@fluendo.com>
 
        patch by: Kai Vehmanen <kv2004 eca cx>
index de74f83..39c3f4b 100644 (file)
@@ -899,6 +899,11 @@ gst_vorbis_enc_buffer_from_packet (GstVorbisEnc * vorbisenc,
   GST_BUFFER_DURATION (outbuf) =
       vorbisenc->next_ts - GST_BUFFER_TIMESTAMP (outbuf);
 
+  if (vorbisenc->next_discont) {
+    GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
+    vorbisenc->next_discont = FALSE;
+  }
+
   GST_LOG_OBJECT (vorbisenc, "encoded buffer of %d bytes",
       GST_BUFFER_SIZE (outbuf));
   return outbuf;
@@ -932,6 +937,8 @@ gst_vorbis_enc_push_buffer (GstVorbisEnc * vorbisenc, GstBuffer * buffer)
 {
   vorbisenc->bytes_out += GST_BUFFER_SIZE (buffer);
 
+  GST_DEBUG_OBJECT (vorbisenc, "Pushing buffer with GP %lld",
+      GST_BUFFER_OFFSET_END (buffer));
   return gst_pad_push (vorbisenc->srcpad, buffer);
 }
 
@@ -1027,6 +1034,33 @@ gst_vorbis_enc_sink_event (GstPad * pad, GstEvent * event)
   return res;
 }
 
+static gboolean
+gst_vorbis_enc_buffer_check_discontinuous (GstVorbisEnc * vorbisenc,
+    GstBuffer * buffer)
+{
+  gboolean ret = FALSE;
+
+  if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
+    GST_DEBUG_OBJECT (vorbisenc, "Discont set on incoming buffer");
+    ret = TRUE;
+  } else if (vorbisenc->expected_ts != GST_CLOCK_TIME_NONE &&
+      GST_BUFFER_TIMESTAMP (buffer) != vorbisenc->expected_ts) {
+    GST_DEBUG_OBJECT (vorbisenc, "Expected TS % " GST_TIME_FORMAT
+        ", buffer TS % " GST_TIME_FORMAT,
+        GST_TIME_ARGS (vorbisenc->expected_ts),
+        GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
+    ret = TRUE;
+  }
+
+  if (vorbisenc->expected_ts != GST_CLOCK_TIME_NONE) {
+    vorbisenc->expected_ts = GST_BUFFER_TIMESTAMP (buffer) +
+        GST_BUFFER_DURATION (buffer);
+  } else
+    vorbisenc->expected_ts = GST_CLOCK_TIME_NONE;
+
+  return ret;
+}
+
 static GstFlowReturn
 gst_vorbis_enc_chain (GstPad * pad, GstBuffer * buffer)
 {
@@ -1037,6 +1071,7 @@ gst_vorbis_enc_chain (GstPad * pad, GstBuffer * buffer)
   gulong i, j;
   float **vorbis_buffer;
   GstBuffer *buf1, *buf2, *buf3;
+  gboolean first = FALSE;
 
   vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad));
 
@@ -1099,6 +1134,7 @@ gst_vorbis_enc_chain (GstPad * pad, GstBuffer * buffer)
     /* now adjust starting granulepos accordingly if the buffer's timestamp is
        nonzero */
     vorbisenc->next_ts = GST_BUFFER_TIMESTAMP (buffer);
+    vorbisenc->expected_ts = GST_BUFFER_TIMESTAMP (buffer);
     vorbisenc->granulepos_offset = gst_util_uint64_scale
         (GST_BUFFER_TIMESTAMP (buffer), vorbisenc->frequency, GST_SECOND);
     vorbisenc->subgranule_offset = 0;
@@ -1106,6 +1142,41 @@ gst_vorbis_enc_chain (GstPad * pad, GstBuffer * buffer)
         vorbisenc->next_ts - granulepos_to_timestamp_offset (vorbisenc, 0);
 
     vorbisenc->header_sent = TRUE;
+    first = TRUE;
+  }
+
+  if (vorbisenc->expected_ts != GST_CLOCK_TIME_NONE &&
+      GST_BUFFER_TIMESTAMP (buffer) < vorbisenc->expected_ts) {
+    GST_WARNING_OBJECT (vorbisenc, "Buffer is older than previous "
+        "timestamp + duration, cannot handle. Dropping buffer.");
+    gst_buffer_unref (buffer);
+    return GST_FLOW_OK;
+  }
+
+  if (gst_vorbis_enc_buffer_check_discontinuous (vorbisenc, buffer) && !first) {
+    GST_WARNING_OBJECT (vorbisenc, "Buffer is discontinuous, flushing encoder "
+        "and restarting (Discont from %" GST_TIME_FORMAT
+        " to %" GST_TIME_FORMAT ")", GST_TIME_ARGS (vorbisenc->next_ts),
+        GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
+    /* Re-initialise encoder (there's unfortunately no API to flush it) */
+    if ((ret = gst_vorbis_enc_clear (vorbisenc)) != GST_FLOW_OK)
+      return ret;
+    if (!gst_vorbis_enc_setup (vorbisenc))
+      return GST_FLOW_ERROR;    /* Should be impossible, we can only get here if
+                                   we successfully initialised earlier */
+
+    /* Now, set our granulepos offset appropriately. */
+    vorbisenc->next_ts = GST_BUFFER_TIMESTAMP (buffer);
+    /* We need to round to the nearest whole number of samples, not just do
+     * a truncating division here */
+    vorbisenc->granulepos_offset = gst_util_uint64_scale
+        (GST_BUFFER_TIMESTAMP (buffer) + GST_SECOND / vorbisenc->frequency / 2
+        - vorbisenc->subgranule_offset, vorbisenc->frequency, GST_SECOND);
+
+    vorbisenc->header_sent = TRUE;
+
+    /* And our next output buffer must have DISCONT set on it */
+    vorbisenc->next_discont = TRUE;
   }
 
   /* data to encode */
@@ -1304,6 +1375,7 @@ gst_vorbis_enc_change_state (GstElement * element, GstStateChange transition)
       break;
     case GST_STATE_CHANGE_READY_TO_PAUSED:
       vorbisenc->setup = FALSE;
+      vorbisenc->next_discont = FALSE;
       vorbisenc->header_sent = FALSE;
       break;
     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
index 40f009e..d837ee3 100644 (file)
@@ -76,6 +76,8 @@ struct _GstVorbisEnc {
   guint64          samples_in;
   guint64          bytes_out;
   GstClockTime     next_ts;
+  GstClockTime     expected_ts;
+  gboolean         next_discont;
   guint64          granulepos_offset;
   gint64           subgranule_offset;