v4l2h264dec: Copy frames when GstVideoMeta is not supported
authorNicolas Dufresne <nicolas.dufresne@collabora.com>
Thu, 12 Mar 2020 20:15:40 +0000 (16:15 -0400)
committerNicolas Dufresne <nicolas.dufresne@collabora.com>
Tue, 31 Mar 2020 13:34:05 +0000 (09:34 -0400)
In some case, when downstream does not support GstVideoMeta, we need to
normalize the stride and offset of the buffer so that downstream can render
properly with a GstVideoMeta. This code is not called when GstVideoMeta is
supported downstream.

sys/v4l2codecs/gstv4l2codech264dec.c

index 73071f7..7422aa8 100644 (file)
@@ -66,6 +66,7 @@ struct _GstV4l2CodecH264Dec
   gint min_pool_size;
   gboolean has_videometa;
   gboolean need_negotiation;
+  gboolean copy_frames;
 
   struct v4l2_ctrl_h264_sps sps;
   struct v4l2_ctrl_h264_pps pps;
@@ -462,6 +463,27 @@ gst_v4l2_codec_h264_dec_new_sequence (GstH264Decoder * decoder,
     }
   }
 
+  /* Check if we can zero-copy buffers */
+  if (!self->has_videometa) {
+    GstVideoInfo ref_vinfo;
+    gint i;
+
+    gst_video_info_set_format (&ref_vinfo, GST_VIDEO_INFO_FORMAT (&self->vinfo),
+        self->display_width, self->display_height);
+
+    for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&self->vinfo); i++) {
+      if (self->vinfo.stride[i] != ref_vinfo.stride[i] ||
+          self->vinfo.offset[i] != ref_vinfo.offset[i]) {
+        GST_WARNING_OBJECT (self,
+            "GstVideoMeta support required, copying frames.");
+        self->copy_frames = TRUE;
+        break;
+      }
+    }
+  } else {
+    self->copy_frames = FALSE;
+  }
+
   return TRUE;
 }
 
@@ -505,6 +527,54 @@ gst_v4l2_codec_h264_dec_start_picture (GstH264Decoder * decoder,
   return TRUE;
 }
 
+static gboolean
+gst_v4l2_codec_h264_dec_copy_output_buffer (GstV4l2CodecH264Dec * self,
+    GstVideoCodecFrame * codec_frame)
+{
+  GstVideoFrame src_frame;
+  GstVideoFrame dest_frame;
+  GstVideoInfo dest_vinfo;
+  GstBuffer *buffer;
+
+  gst_video_info_set_format (&dest_vinfo, GST_VIDEO_INFO_FORMAT (&self->vinfo),
+      self->display_width, self->display_height);
+
+  buffer = gst_video_decoder_allocate_output_buffer (GST_VIDEO_DECODER (self));
+  if (!buffer)
+    goto fail;
+
+  if (!gst_video_frame_map (&src_frame, &self->vinfo,
+          codec_frame->output_buffer, GST_MAP_READ))
+    goto fail;
+
+  if (!gst_video_frame_map (&dest_frame, &dest_vinfo, buffer, GST_MAP_WRITE)) {
+    gst_video_frame_unmap (&dest_frame);
+    goto fail;
+  }
+
+  /* gst_video_frame_copy can crop this, but does not know, so let make it
+   * think it's all right */
+  GST_VIDEO_INFO_WIDTH (&src_frame.info) = self->display_width;
+  GST_VIDEO_INFO_HEIGHT (&src_frame.info) = self->display_height;
+
+  if (!gst_video_frame_copy (&dest_frame, &src_frame)) {
+    gst_video_frame_unmap (&src_frame);
+    gst_video_frame_unmap (&dest_frame);
+    goto fail;
+  }
+
+  gst_video_frame_unmap (&src_frame);
+  gst_video_frame_unmap (&dest_frame);
+  gst_buffer_replace (&codec_frame->output_buffer, buffer);
+  gst_buffer_unref (buffer);
+
+  return TRUE;
+
+fail:
+  GST_ERROR_OBJECT (self, "Failed copy output buffer.");
+  return FALSE;
+}
+
 static GstFlowReturn
 gst_v4l2_codec_h264_dec_output_picture (GstH264Decoder * decoder,
     GstH264Picture * picture)
@@ -561,6 +631,10 @@ finish_frame:
   /* Hold on reference buffers for the rest of the picture lifetime */
   gst_h264_picture_set_user_data (picture,
       gst_buffer_ref (frame->output_buffer), (GDestroyNotify) gst_buffer_unref);
+
+  if (self->copy_frames)
+    gst_v4l2_codec_h264_dec_copy_output_buffer (self, frame);
+
   return gst_video_decoder_finish_frame (GST_VIDEO_DECODER (self), frame);
 }