base: audioencoder: Keep serialize event behind buffers
authorNicolas Dufresne <nicolas.dufresne@collabora.com>
Thu, 28 Oct 2021 21:09:34 +0000 (17:09 -0400)
committerNicolas Dufresne <nicolas.dufresne@collabora.com>
Fri, 18 Feb 2022 19:40:29 +0000 (14:40 -0500)
If a serialized event arrives behind a buffer, it should not be send before
it. This fixes the pending event handling so that only early pending events,
the one that arrrived or was generated while the adapter was empty get send
before pushing buffer. All other events are not pushed after.

This issue lead the latency tracer to think our audio encoder did not have any
latency. This was testing with opusenc in a live pipeline.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1266>

subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioencoder.c

index 60cc6af..da7f508 100644 (file)
@@ -171,6 +171,7 @@ typedef struct _GstAudioEncoderContext
   /* MT-protected (with LOCK) */
   GstClockTime min_latency;
   GstClockTime max_latency;
+  gboolean negotiated;
 
   GList *headers;
   gboolean new_headers;
@@ -239,6 +240,11 @@ struct _GstAudioEncoderPrivate
 
   /* pending serialized sink events, will be sent from finish_frame() */
   GList *pending_events;
+
+  /* these are initial events or events that came in while there was nothing
+   * in the adapter. these events shall be sent after negotiation but before
+   * we push the following buffer. */
+  GList *early_pending_events;
 };
 
 
@@ -499,8 +505,11 @@ gst_audio_encoder_reset (GstAudioEncoder * enc, gboolean full)
     enc->priv->tags_merge_mode = GST_TAG_MERGE_APPEND;
     enc->priv->tags_changed = FALSE;
 
-    g_list_foreach (enc->priv->pending_events, (GFunc) gst_event_unref, NULL);
-    g_list_free (enc->priv->pending_events);
+    g_list_free_full (enc->priv->early_pending_events,
+        (GDestroyNotify) gst_event_unref);
+    enc->priv->early_pending_events = NULL;
+    g_list_free_full (enc->priv->pending_events,
+        (GDestroyNotify) gst_event_unref);
     enc->priv->pending_events = NULL;
   }
 
@@ -598,10 +607,30 @@ gst_audio_encoder_push_event (GstAudioEncoder * enc, GstEvent * event)
 }
 
 static inline void
+gst_audio_encoder_push_early_pending_events (GstAudioEncoder * enc)
+{
+  GstAudioEncoderPrivate *priv = enc->priv;
+
+  if (priv->early_pending_events) {
+    GList *pending_events, *l;
+
+    pending_events = priv->early_pending_events;
+    priv->early_pending_events = NULL;
+
+    GST_DEBUG_OBJECT (enc, "Pushing early pending events");
+    for (l = pending_events; l; l = l->next)
+      gst_audio_encoder_push_event (enc, l->data);
+    g_list_free (pending_events);
+  }
+}
+
+static inline void
 gst_audio_encoder_push_pending_events (GstAudioEncoder * enc)
 {
   GstAudioEncoderPrivate *priv = enc->priv;
 
+  gst_audio_encoder_push_early_pending_events (enc);
+
   if (priv->pending_events) {
     GList *pending_events, *l;
 
@@ -795,9 +824,9 @@ gst_audio_encoder_finish_frame (GstAudioEncoder * enc, GstBuffer * buf,
   if (G_LIKELY (buf))
     priv->got_data = TRUE;
 
-  gst_audio_encoder_push_pending_events (enc);
+  gst_audio_encoder_push_early_pending_events (enc);
 
-  /* send after pending events, which likely includes segment event */
+  /* send after early pending events, which likely includes segment event */
   gst_audio_encoder_check_and_push_pending_tags (enc);
 
   /* remove corresponding samples from input */
@@ -1013,6 +1042,10 @@ gst_audio_encoder_finish_frame (GstAudioEncoder * enc, GstBuffer * buf,
 
     ret = gst_pad_push (enc->srcpad, buf);
     GST_LOG_OBJECT (enc, "buffer pushed: %s", gst_flow_get_name (ret));
+
+    /* Now push the events that followed after the buffer got into the
+     * adapter. */
+    gst_audio_encoder_push_pending_events (enc);
   } else {
     /* merely advance samples, most work for that already done above */
     priv->samples += samples;
@@ -1578,8 +1611,8 @@ gst_audio_encoder_sink_event_default (GstAudioEncoder * enc, GstEvent * event)
       /* and follow along with segment */
       enc->input_segment = seg;
 
-      enc->priv->pending_events =
-          g_list_append (enc->priv->pending_events, event);
+      enc->priv->early_pending_events =
+          g_list_append (enc->priv->early_pending_events, event);
       GST_AUDIO_ENCODER_STREAM_UNLOCK (enc);
 
       res = TRUE;
@@ -1696,8 +1729,13 @@ gst_audio_encoder_sink_event_default (GstAudioEncoder * enc, GstEvent * event)
             gst_pad_event_default (enc->sinkpad, GST_OBJECT_CAST (enc), event);
       } else {
         GST_AUDIO_ENCODER_STREAM_LOCK (enc);
-        enc->priv->pending_events =
-            g_list_append (enc->priv->pending_events, event);
+        if (gst_adapter_available (enc->priv->adapter) == 0) {
+          enc->priv->early_pending_events =
+              g_list_append (enc->priv->early_pending_events, event);
+        } else {
+          enc->priv->pending_events =
+              g_list_append (enc->priv->pending_events, event);
+        }
         GST_AUDIO_ENCODER_STREAM_UNLOCK (enc);
         res = TRUE;
       }
@@ -2732,10 +2770,10 @@ gst_audio_encoder_negotiate_default (GstAudioEncoder * enc)
 
   GST_DEBUG_OBJECT (enc, "Setting srcpad caps %" GST_PTR_FORMAT, caps);
 
-  if (enc->priv->pending_events) {
+  if (enc->priv->early_pending_events) {
     GList **pending_events, *l;
 
-    pending_events = &enc->priv->pending_events;
+    pending_events = &enc->priv->early_pending_events;
 
     GST_DEBUG_OBJECT (enc, "Pushing pending events");
     for (l = *pending_events; l;) {
@@ -2814,6 +2852,8 @@ gst_audio_encoder_negotiate_unlocked (GstAudioEncoder * enc)
   if (G_LIKELY (klass->negotiate))
     ret = klass->negotiate (enc);
 
+  enc->priv->ctx.negotiated = TRUE;
+
   return ret;
 }