audio{en,de}coder: Track input and output segments separately
authorSebastian Dröge <sebastian.droege@collabora.co.uk>
Fri, 30 Mar 2012 11:21:09 +0000 (13:21 +0200)
committerSebastian Dröge <sebastian.droege@collabora.co.uk>
Fri, 30 Mar 2012 11:21:09 +0000 (13:21 +0200)
They can go out of sync for some time if processing of buffers
on the old segment happens after the segment was received.

gst-libs/gst/audio/gstaudiodecoder.c
gst-libs/gst/audio/gstaudiodecoder.h
gst-libs/gst/audio/gstaudioencoder.c
gst-libs/gst/audio/gstaudioencoder.h

index 3eef6af..31253e4 100644 (file)
@@ -460,7 +460,8 @@ gst_audio_decoder_reset (GstAudioDecoder * dec, gboolean full)
       dec->priv->taglist = NULL;
     }
 
-    gst_segment_init (&dec->segment, GST_FORMAT_TIME);
+    gst_segment_init (&dec->input_segment, GST_FORMAT_TIME);
+    gst_segment_init (&dec->output_segment, GST_FORMAT_TIME);
 
     g_list_foreach (dec->priv->pending_events, (GFunc) gst_event_unref, NULL);
     g_list_free (dec->priv->pending_events);
@@ -644,7 +645,7 @@ gst_audio_decoder_push_forward (GstAudioDecoder * dec, GstBuffer * buf)
       GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
 
   /* clip buffer */
-  buf = gst_audio_buffer_clip (buf, &dec->segment, ctx->info.rate,
+  buf = gst_audio_buffer_clip (buf, &dec->output_segment, ctx->info.rate,
       ctx->info.bpf);
   if (G_UNLIKELY (!buf)) {
     GST_DEBUG_OBJECT (dec, "no data after clipping to segment");
@@ -662,7 +663,7 @@ gst_audio_decoder_push_forward (GstAudioDecoder * dec, GstBuffer * buf)
   if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buf))) {
     /* duration should always be valid for raw audio */
     g_assert (GST_BUFFER_DURATION_IS_VALID (buf));
-    dec->segment.position =
+    dec->output_segment.position =
         GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf);
   }
 
@@ -759,7 +760,7 @@ again:
   }
 
   if (G_LIKELY (buf)) {
-    if (dec->segment.rate > 0.0) {
+    if (dec->output_segment.rate > 0.0) {
       ret = gst_audio_decoder_push_forward (dec, buf);
       GST_LOG_OBJECT (dec, "buffer pushed: %s", gst_flow_get_name (ret));
     } else {
@@ -777,6 +778,29 @@ again:
   return ret;
 }
 
+static gboolean
+gst_audio_decoder_push_event (GstAudioDecoder * dec, GstEvent * event)
+{
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_SEGMENT:{
+      GstSegment seg;
+
+      GST_AUDIO_DECODER_STREAM_LOCK (dec);
+      gst_event_copy_segment (event, &seg);
+
+      GST_DEBUG_OBJECT (dec, "starting segment %" GST_SEGMENT_FORMAT, &seg);
+
+      dec->output_segment = seg;
+      GST_AUDIO_DECODER_STREAM_UNLOCK (dec);
+      break;
+    }
+    default:
+      break;
+  }
+
+  return gst_pad_push_event (dec->srcpad, event);
+}
+
 /**
  * gst_audio_decoder_finish_frame:
  * @dec: a #GstAudioDecoder
@@ -841,7 +865,7 @@ gst_audio_decoder_finish_frame (GstAudioDecoder * dec, GstBuffer * buf,
 
     GST_DEBUG_OBJECT (dec, "Pushing pending events");
     for (l = pending_events; l; l = l->next)
-      gst_pad_push_event (dec->srcpad, l->data);
+      gst_audio_decoder_push_event (dec, l->data);
     g_list_free (pending_events);
   }
 
@@ -926,7 +950,7 @@ gst_audio_decoder_finish_frame (GstAudioDecoder * dec, GstBuffer * buf,
     if (gst_tag_list_is_empty (priv->taglist)) {
       gst_tag_list_free (priv->taglist);
     } else {
-      gst_pad_push_event (dec->srcpad, gst_event_new_tag (priv->taglist));
+      gst_audio_decoder_push_event (dec, gst_event_new_tag (priv->taglist));
     }
     priv->taglist = NULL;
   }
@@ -1125,7 +1149,7 @@ gst_audio_decoder_drain (GstAudioDecoder * dec)
     /* dispatch reverse pending buffers */
     /* chain eventually calls upon drain as well, but by that time
      * gather list should be clear, so ok ... */
-    if (dec->segment.rate < 0.0 && dec->priv->gather)
+    if (dec->output_segment.rate < 0.0 && dec->priv->gather)
       gst_audio_decoder_chain_reverse (dec, NULL);
     /* have subclass give all it can */
     ret = gst_audio_decoder_push_buffers (dec, TRUE);
@@ -1161,7 +1185,8 @@ gst_audio_decoder_flush (GstAudioDecoder * dec, gboolean hard)
     ret = gst_audio_decoder_drain (dec);
   } else {
     gst_audio_decoder_clear_queues (dec);
-    gst_segment_init (&dec->segment, GST_FORMAT_TIME);
+    gst_segment_init (&dec->input_segment, GST_FORMAT_TIME);
+    gst_segment_init (&dec->output_segment, GST_FORMAT_TIME);
     dec->priv->error_count = 0;
   }
   /* only bother subclass with flushing if known it is already alive
@@ -1433,14 +1458,15 @@ gst_audio_decoder_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
     /* buffer may claim DISCONT loudly, if it can't tell us where we are now,
      * we'll stick to where we were ...
      * Particularly useful/needed for upstream BYTE based */
-    if (dec->segment.rate > 0.0 && !GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
+    if (dec->input_segment.rate > 0.0
+        && !GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
       GST_DEBUG_OBJECT (dec, "... but restoring previous ts tracking");
       dec->priv->base_ts = ts;
       dec->priv->samples = samples;
     }
   }
 
-  if (dec->segment.rate > 0.0)
+  if (dec->input_segment.rate > 0.0)
     ret = gst_audio_decoder_chain_forward (dec, buffer);
   else
     ret = gst_audio_decoder_chain_reverse (dec, buffer);
@@ -1524,16 +1550,17 @@ gst_audio_decoder_sink_eventfunc (GstAudioDecoder * dec, GstEvent * event)
         GST_DEBUG_OBJECT (dec,
             "segment update: plc %d, do_plc %d, position %" GST_TIME_FORMAT,
             dec->priv->plc, dec->priv->ctx.do_plc,
-            GST_TIME_ARGS (dec->segment.position));
+            GST_TIME_ARGS (dec->input_segment.position));
         if (dec->priv->plc && dec->priv->ctx.do_plc &&
-            dec->segment.rate > 0.0 && dec->segment.position < start) {
+            dec->input_segment.rate > 0.0
+            && dec->input_segment.position < start) {
           GstAudioDecoderClass *klass;
           GstBuffer *buf;
 
           klass = GST_AUDIO_DECODER_GET_CLASS (dec);
           /* hand subclass empty frame with duration that needs covering */
           buf = gst_buffer_new ();
-          GST_BUFFER_DURATION (buf) = start - dec->segment.position;
+          GST_BUFFER_DURATION (buf) = start - dec->input_segment.position;
           /* best effort, not much error handling */
           gst_audio_decoder_handle_frame (dec, klass, buf);
         }
@@ -1552,7 +1579,7 @@ gst_audio_decoder_sink_eventfunc (GstAudioDecoder * dec, GstEvent * event)
       }
 
       /* and follow along with segment */
-      dec->segment = seg;
+      dec->input_segment = seg;
       dec->priv->pending_events =
           g_list_append (dec->priv->pending_events, event);
       GST_AUDIO_DECODER_STREAM_UNLOCK (dec);
@@ -1573,7 +1600,7 @@ gst_audio_decoder_sink_eventfunc (GstAudioDecoder * dec, GstEvent * event)
 
       /* Forward FLUSH_STOP, it is expected to be forwarded immediately
        * and no buffers are queued anyway. */
-      ret = gst_pad_push_event (dec->srcpad, event);
+      ret = gst_audio_decoder_push_event (dec, event);
       break;
 
     case GST_EVENT_EOS:
@@ -1583,7 +1610,7 @@ gst_audio_decoder_sink_eventfunc (GstAudioDecoder * dec, GstEvent * event)
 
       /* Forward EOS because no buffer or serialized event will come after
        * EOS and nothing could trigger another _finish_frame() call. */
-      ret = gst_pad_push_event (dec->srcpad, event);
+      ret = gst_audio_decoder_push_event (dec, event);
       break;
 
     case GST_EVENT_CAPS:
@@ -1671,7 +1698,7 @@ gst_audio_decoder_do_seek (GstAudioDecoder * dec, GstEvent * event)
     return FALSE;
   }
 
-  memcpy (&seek_segment, &dec->segment, sizeof (seek_segment));
+  memcpy (&seek_segment, &dec->output_segment, sizeof (seek_segment));
   gst_segment_do_seek (&seek_segment, rate, format, flags, start_type,
       start_time, end_type, end_time, NULL);
   start_time = seek_segment.position;
@@ -1956,9 +1983,11 @@ gst_audio_decoder_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
       }
 
       /* we start from the last seen time */
-      time = dec->segment.position;
+      time = dec->output_segment.position;
       /* correct for the segment values */
-      time = gst_segment_to_stream_time (&dec->segment, GST_FORMAT_TIME, time);
+      time =
+          gst_segment_to_stream_time (&dec->output_segment, GST_FORMAT_TIME,
+          time);
 
       GST_LOG_OBJECT (dec,
           "query %p: our time: %" GST_TIME_FORMAT, query, GST_TIME_ARGS (time));
index 25d56d8..967be93 100644 (file)
@@ -84,6 +84,26 @@ G_BEGIN_DECLS
 #define GST_AUDIO_DECODER_STREAM_LOCK(dec)   g_rec_mutex_lock (&GST_AUDIO_DECODER (dec)->stream_lock)
 #define GST_AUDIO_DECODER_STREAM_UNLOCK(dec) g_rec_mutex_unlock (&GST_AUDIO_DECODER (dec)->stream_lock)
 
+/**
+ * GST_AUDIO_DECODER_INPUT_SEGMENT:
+ * @obj: base parse instance
+ *
+ * Gives the input segment of the element.
+ *
+ * Since: 0.10.36
+ */
+#define GST_AUDIO_DECODER_INPUT_SEGMENT(obj)   (GST_AUDIO_DECODER_CAST (obj)->input_segment)
+
+/**
+ * GST_AUDIO_DECODER_OUTPUT_SEGMENT:
+ * @obj: base parse instance
+ *
+ * Gives the output segment of the element.
+ *
+ * Since: 0.10.36
+ */
+#define GST_AUDIO_DECODER_OUTPUT_SEGMENT(obj)   (GST_AUDIO_DECODER_CAST (obj)->output_segment)
+
 typedef struct _GstAudioDecoder GstAudioDecoder;
 typedef struct _GstAudioDecoderClass GstAudioDecoderClass;
 
@@ -161,7 +181,8 @@ struct _GstAudioDecoder
   GRecMutex       stream_lock;
 
   /* MT-protected (with STREAM_LOCK) */
-  GstSegment      segment;
+  GstSegment      input_segment;
+  GstSegment      output_segment;
 
   /*< private >*/
   GstAudioDecoderPrivate *priv;
index 94f523b..d845e21 100644 (file)
@@ -456,7 +456,8 @@ gst_audio_encoder_reset (GstAudioEncoder * enc, gboolean full)
     enc->priv->pending_events = NULL;
   }
 
-  gst_segment_init (&enc->segment, GST_FORMAT_TIME);
+  gst_segment_init (&enc->input_segment, GST_FORMAT_TIME);
+  gst_segment_init (&enc->output_segment, GST_FORMAT_TIME);
 
   gst_adapter_clear (enc->priv->adapter);
   enc->priv->got_data = FALSE;
@@ -525,6 +526,29 @@ close_failed:
   }
 }
 
+static gboolean
+gst_audio_encoder_push_event (GstAudioEncoder * enc, GstEvent * event)
+{
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_SEGMENT:{
+      GstSegment seg;
+
+      GST_AUDIO_ENCODER_STREAM_LOCK (enc);
+      gst_event_copy_segment (event, &seg);
+
+      GST_DEBUG_OBJECT (enc, "starting segment %" GST_SEGMENT_FORMAT, &seg);
+
+      enc->output_segment = seg;
+      GST_AUDIO_ENCODER_STREAM_UNLOCK (enc);
+      break;
+    }
+    default:
+      break;
+  }
+
+  return gst_pad_push_event (enc->srcpad, event);
+}
+
 /**
  * gst_audio_encoder_finish_frame:
  * @enc: a #GstAudioEncoder
@@ -585,7 +609,7 @@ gst_audio_encoder_finish_frame (GstAudioEncoder * enc, GstBuffer * buf,
 
     GST_DEBUG_OBJECT (enc, "Pushing pending events");
     for (l = pending_events; l; l = l->next)
-      gst_pad_push_event (enc->srcpad, l->data);
+      gst_audio_encoder_push_event (enc, l->data);
     g_list_free (pending_events);
   }
 
@@ -607,7 +631,7 @@ gst_audio_encoder_finish_frame (GstAudioEncoder * enc, GstBuffer * buf,
         caps);
 #endif
     GST_DEBUG_OBJECT (enc, "sending tags %" GST_PTR_FORMAT, tags);
-    gst_pad_push_event (enc->srcpad, gst_event_new_tag (tags));
+    gst_audio_encoder_push_event (enc, gst_event_new_tag (tags));
   }
 
   /* remove corresponding samples from input */
@@ -940,7 +964,7 @@ gst_audio_encoder_set_base_gp (GstAudioEncoder * enc)
 
   /* use running time for granule */
   /* incoming data is clipped, so a valid input should yield a valid output */
-  ts = gst_segment_to_running_time (&enc->segment, GST_FORMAT_TIME,
+  ts = gst_segment_to_running_time (&enc->input_segment, GST_FORMAT_TIME,
       enc->priv->base_ts);
   if (GST_CLOCK_TIME_IS_VALID (ts)) {
     enc->priv->base_gp =
@@ -1018,7 +1042,7 @@ gst_audio_encoder_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
 
   /* clip to segment */
   /* NOTE: slightly painful linking -laudio only for this one ... */
-  buffer = gst_audio_buffer_clip (buffer, &enc->segment, ctx->info.rate,
+  buffer = gst_audio_buffer_clip (buffer, &enc->input_segment, ctx->info.rate,
       ctx->info.bpf);
   if (G_UNLIKELY (!buffer)) {
     GST_DEBUG_OBJECT (buffer, "no data after clipping to segment");
@@ -1368,7 +1392,7 @@ gst_audio_encoder_sink_event_default (GstAudioEncoder * enc, GstEvent * event)
       /* reset partially for new segment */
       gst_audio_encoder_reset (enc, FALSE);
       /* and follow along with segment */
-      enc->segment = seg;
+      enc->input_segment = seg;
 
       enc->priv->pending_events =
           g_list_append (enc->priv->pending_events, event);
@@ -1379,7 +1403,7 @@ gst_audio_encoder_sink_event_default (GstAudioEncoder * enc, GstEvent * event)
     }
 
     case GST_EVENT_FLUSH_START:
-      res = gst_pad_push_event (enc->srcpad, event);
+      res = gst_audio_encoder_push_event (enc, event);
       break;
 
     case GST_EVENT_FLUSH_STOP:
@@ -1396,7 +1420,7 @@ gst_audio_encoder_sink_event_default (GstAudioEncoder * enc, GstEvent * event)
       enc->priv->pending_events = NULL;
       GST_AUDIO_ENCODER_STREAM_UNLOCK (enc);
 
-      res = gst_pad_push_event (enc->srcpad, event);
+      res = gst_audio_encoder_push_event (enc, event);
       break;
 
     case GST_EVENT_EOS:
@@ -1407,7 +1431,7 @@ gst_audio_encoder_sink_event_default (GstAudioEncoder * enc, GstEvent * event)
       /* forward immediately because no buffer or serialized event
        * will come after EOS and nothing could trigger another
        * _finish_frame() call. */
-      res = gst_pad_push_event (enc->srcpad, event);
+      res = gst_audio_encoder_push_event (enc, event);
       break;
 
     case GST_EVENT_TAG:
index ce96fba..b4fce54 100644 (file)
@@ -73,14 +73,24 @@ G_BEGIN_DECLS
 #define GST_AUDIO_ENCODER_SINK_PAD(obj)        (GST_AUDIO_ENCODER_CAST (obj)->sinkpad)
 
 /**
- * GST_AUDIO_ENCODER_SEGMENT:
+ * GST_AUDIO_ENCODER_INPUT_SEGMENT:
  * @obj: base parse instance
  *
- * Gives the segment of the element.
+ * Gives the input segment of the element.
  *
  * Since: 0.10.36
  */
-#define GST_AUDIO_ENCODER_SEGMENT(obj)     (GST_AUDIO_ENCODER_CAST (obj)->segment)
+#define GST_AUDIO_ENCODER_INPUT_SEGMENT(obj)     (GST_AUDIO_ENCODER_CAST (obj)->input_segment)
+
+/**
+ * GST_AUDIO_ENCODER_OUTPUT_SEGMENT:
+ * @obj: base parse instance
+ *
+ * Gives the output segment of the element.
+ *
+ * Since: 0.10.36
+ */
+#define GST_AUDIO_ENCODER_OUTPUT_SEGMENT(obj)     (GST_AUDIO_ENCODER_CAST (obj)->output_segment)
 
 #define GST_AUDIO_ENCODER_STREAM_LOCK(enc)   g_rec_mutex_lock (&GST_AUDIO_ENCODER (enc)->stream_lock)
 #define GST_AUDIO_ENCODER_STREAM_UNLOCK(enc) g_rec_mutex_unlock (&GST_AUDIO_ENCODER (enc)->stream_lock)
@@ -111,7 +121,8 @@ struct _GstAudioEncoder {
   GRecMutex       stream_lock;
 
   /* MT-protected (with STREAM_LOCK) */
-  GstSegment      segment;
+  GstSegment      input_segment;
+  GstSegment      output_segment;
 
   /*< private >*/
   GstAudioEncoderPrivate *priv;