msdkenc: Try to find corresponding codec frame for encoded output buffer
authorSeungha Yang <seungha@centricular.com>
Tue, 24 Mar 2020 07:00:30 +0000 (16:00 +0900)
committerGStreamer Merge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Mon, 30 Mar 2020 07:59:10 +0000 (07:59 +0000)
The input and output buffers should be matched as much as possible
so that various metadata and its ordering to be preserved.

sys/msdk/gstmsdkenc.c

index 84e5e00..27cc8a5 100644 (file)
@@ -854,19 +854,68 @@ gst_msdkenc_reset_task (MsdkEncTask * task)
   task->sync_point = NULL;
 }
 
+static GstVideoCodecFrame *
+gst_msdkenc_find_best_frame (GstMsdkEnc * thiz, GList * frames,
+    mfxBitstream * bitstream)
+{
+  GList *iter;
+  GstVideoCodecFrame *ret = NULL;
+  GstClockTime pts;
+  GstClockTimeDiff best_diff = GST_CLOCK_STIME_NONE;
+
+  if (!bitstream)
+    return NULL;
+
+  if (bitstream->TimeStamp == MFX_TIMESTAMP_UNKNOWN) {
+    pts = GST_CLOCK_TIME_NONE;
+  } else {
+    pts = gst_util_uint64_scale (bitstream->TimeStamp, GST_SECOND, 90000);
+  }
+
+  for (iter = frames; iter; iter = g_list_next (iter)) {
+    GstVideoCodecFrame *frame = (GstVideoCodecFrame *) iter->data;
+
+    /* if we don't know the time stamp, find the first frame which
+     * has unknown timestamp */
+    if (!GST_CLOCK_TIME_IS_VALID (pts)) {
+      if (!GST_CLOCK_TIME_IS_VALID (frame->pts)) {
+        ret = frame;
+        break;
+      }
+    } else {
+      GstClockTimeDiff abs_diff = ABS (GST_CLOCK_DIFF (frame->pts, pts));
+      if (abs_diff == 0) {
+        ret = frame;
+        break;
+      }
+
+      if (!GST_CLOCK_STIME_IS_VALID (best_diff) || abs_diff < best_diff) {
+        ret = frame;
+        best_diff = abs_diff;
+      }
+    }
+  }
+
+  if (ret)
+    gst_video_codec_frame_ref (ret);
+
+  return ret;
+}
+
 static GstFlowReturn
 gst_msdkenc_finish_frame (GstMsdkEnc * thiz, MsdkEncTask * task,
     gboolean discard)
 {
   GstVideoCodecFrame *frame;
+  GList *list;
 
   if (!task->sync_point)
     return GST_FLOW_OK;
 
-  frame = gst_video_encoder_get_oldest_frame (GST_VIDEO_ENCODER (thiz));
+  list = gst_video_encoder_get_frames (GST_VIDEO_ENCODER (thiz));
 
-  if (!frame) {
-    GST_ERROR_OBJECT (thiz, "failed to get a frame");
+  if (!list) {
+    GST_ERROR_OBJECT (thiz, "failed to get list of frame");
     return GST_FLOW_ERROR;
   }
 
@@ -883,6 +932,13 @@ gst_msdkenc_finish_frame (GstMsdkEnc * thiz, MsdkEncTask * task,
     guint8 *data =
         task->output_bitstream.Data + task->output_bitstream.DataOffset;
     gsize size = task->output_bitstream.DataLength;
+
+    frame = gst_msdkenc_find_best_frame (thiz, list, &task->output_bitstream);
+    if (!frame) {
+      /* just pick the oldest one */
+      frame = gst_video_encoder_get_oldest_frame (GST_VIDEO_ENCODER (thiz));
+    }
+
     out_buf = gst_buffer_new_allocate (NULL, size, NULL);
     gst_buffer_fill (out_buf, 0, data, size);
     frame->output_buffer = out_buf;
@@ -900,8 +956,12 @@ gst_msdkenc_finish_frame (GstMsdkEnc * thiz, MsdkEncTask * task,
 
     /* Mark task as available */
     gst_msdkenc_reset_task (task);
+  } else {
+    frame = gst_video_encoder_get_oldest_frame (GST_VIDEO_ENCODER (thiz));
   }
 
+  g_list_free_full (list, (GDestroyNotify) gst_video_codec_frame_unref);
+
   gst_video_codec_frame_unref (frame);
   gst_msdkenc_dequeue_frame (thiz, frame);