v4l2: encoder: Add dynamic framerate support
authorNicolas Dufresne <nicolas.dufresne@collabora.com>
Tue, 13 Aug 2024 20:38:37 +0000 (16:38 -0400)
committerBackport Bot <gitlab-backport-bot@gstreamer-foundation.org>
Wed, 18 Sep 2024 23:29:23 +0000 (00:29 +0100)
This is not trully supported in V4L2, but we can emulate this similar to
what other elements do. In this patch we ensure that 0/1 is supported by
encoders (caps query),and uses a default of 30fps whenever we need to
set a framerate into the driver.

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

subprojects/gst-plugins-good/sys/v4l2/gstv4l2object.c
subprojects/gst-plugins-good/sys/v4l2/gstv4l2videoenc.c

index 0134de4756e0bb337898f40894997627be98570d..26743967c27f6cf52bd5e63fb599e0c3ad6b4b36 100644 (file)
@@ -4355,15 +4355,25 @@ gst_v4l2_object_set_format_full (GstV4l2Object * v4l2object, GstCaps * caps,
       goto done;
     }
 
-    /* Note: V4L2 wants the frame interval, we have the frame rate */
-    streamparm.parm.capture.timeperframe.numerator = fps_d;
-    streamparm.parm.capture.timeperframe.denominator = fps_n;
+    /* Variable framerate is not a V4L2 concept, simply default to 30fps */
+    if (fps_n == 0 && fps_d == 1) {
+      GST_DEBUG_OBJECT (v4l2object->dbg_obj,
+          "Variable framerate not supported, assuming 30fps");
+      streamparm.parm.capture.timeperframe.numerator = 1;
+      streamparm.parm.capture.timeperframe.denominator = 30;
+    } else {
+      /* Note: V4L2 wants the frame interval, we have the frame rate */
+      streamparm.parm.capture.timeperframe.numerator = fps_d;
+      streamparm.parm.capture.timeperframe.denominator = fps_n;
+    }
 
     /* some cheap USB cam's won't accept any change */
     if (v4l2object->ioctl (fd, VIDIOC_S_PARM, &streamparm) < 0)
       goto set_parm_failed;
 
-    if (streamparm.parm.capture.timeperframe.numerator > 0 &&
+    if (fps_n == 0 && fps_d == 1) {
+      /* just keep reporting variable framerate */
+    } else if (streamparm.parm.capture.timeperframe.numerator > 0 &&
         streamparm.parm.capture.timeperframe.denominator > 0) {
       /* get new values */
       fps_d = streamparm.parm.capture.timeperframe.numerator;
@@ -4398,14 +4408,24 @@ gst_v4l2_object_set_format_full (GstV4l2Object * v4l2object, GstCaps * caps,
       goto done;
     }
 
-    /* Note: V4L2 wants the frame interval, we have the frame rate */
-    streamparm.parm.output.timeperframe.numerator = fps_d;
-    streamparm.parm.output.timeperframe.denominator = fps_n;
+    /* Variable framerate is not a V4L2 concept, simply default to 30fps */
+    if (fps_n == 0 && fps_d == 1) {
+      GST_DEBUG_OBJECT (v4l2object->dbg_obj,
+          "Variable framerate not supported, assuming 30fps");
+      streamparm.parm.output.timeperframe.numerator = 1;
+      streamparm.parm.output.timeperframe.denominator = 30;
+    } else {
+      /* Note: V4L2 wants the frame interval, we have the frame rate */
+      streamparm.parm.output.timeperframe.numerator = fps_d;
+      streamparm.parm.output.timeperframe.denominator = fps_n;
+    }
 
     if (v4l2object->ioctl (fd, VIDIOC_S_PARM, &streamparm) < 0)
       goto set_parm_failed;
 
-    if (streamparm.parm.output.timeperframe.numerator > 0 &&
+    if (fps_n == 0 && fps_d == 1) {
+      /* just keep reporting variable framerate */
+    } else if (streamparm.parm.output.timeperframe.numerator > 0 &&
         streamparm.parm.output.timeperframe.denominator > 0) {
       /* get new values */
       fps_d = streamparm.parm.output.timeperframe.numerator;
index 83490e13bcc2db83a40e9815022cb13b636fdfe0..1edb88e6469f9121655e134ca69c05e676040721 100644 (file)
@@ -111,7 +111,7 @@ gst_v4l2_video_enc_open (GstVideoEncoder * encoder)
 {
   GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (encoder);
   GstV4l2Error error = GST_V4L2_ERROR_INIT;
-  GstCaps *codec_caps;
+  GstCaps *tmp_caps, *codec_caps;
 
   GST_DEBUG_OBJECT (self, "Opening");
 
@@ -124,6 +124,15 @@ gst_v4l2_video_enc_open (GstVideoEncoder * encoder)
   self->probed_sinkcaps = gst_v4l2_object_probe_caps (self->v4l2output,
       gst_v4l2_object_get_raw_caps ());
 
+  /*
+   * Relax the framerate to always allow 0/1 regardless of what the driver
+   * wants. This improve compatibility of the encoder with sources which may
+   * not know the frame rate
+   */
+  tmp_caps = gst_caps_copy (self->probed_sinkcaps);
+  gst_caps_set_simple (tmp_caps, "framerate", GST_TYPE_FRACTION, 0, 1, NULL);
+  gst_caps_append (self->probed_sinkcaps, tmp_caps);
+
   if (gst_caps_is_empty (self->probed_sinkcaps))
     goto no_raw_format;