Browse Source

camera: Don't try to fake entire range for FRMIVAL_TYPE_CONTINUOUS

V4L2 is able to advertise that a video device is able to display any frame
interval within a continuous range. SDL does not allow advertising this and
only exposes discrete frame intervals. To work around this, SDL attempted to
generate a subset of the range with a fixed interval. Unfortunately, the way
this was accomplish is inherently broken and led to attempting to allocate a
very large number of formats per resolution and colorspace. With the Magewell
Pro Capture HDMI, which can expose FRMSIZE_TYPE_CONTINUOUS as well, this can
expose a truly astronomical number of formats, exceeding 1 PB of RAM. This will
lead to an OOM kill for any process that tries to initialize the camera
subsystem.

This patch just tests to see if some common frame rates are within the
contiuous range and expose those. SDL still does not handle
FRMSIZE_TYPE_CONTINUOUS in a graceful way so it still uses over a gigabyte of
RAM for each possible combination of sizes, but with this patch it no longer
leads to an OOM kill. The API will need amending for proper support for both
continuous frame sizes and frame intervals.

(cherry picked from commit ab6dd970ac8a427c1aafb5f2f66ac6cce8fc4bf4)
Vicki Pfau 3 days ago
parent
commit
27439467ce
1 changed files with 45 additions and 1 deletions
  1. 45 1
      src/camera/v4l2/SDL_camera_v4l2.c

+ 45 - 1
src/camera/v4l2/SDL_camera_v4l2.c

@@ -698,7 +698,7 @@ static bool AddCameraFormat(const int fd, CameraFormatAddData *data, SDL_PixelFo
                 return false;  // Probably out of memory; we'll go with what we have, if anything.
             }
             frmivalenum.index++;  // set up for the next one.
-        } else if ((frmivalenum.type == V4L2_FRMIVAL_TYPE_STEPWISE) || (frmivalenum.type == V4L2_FRMIVAL_TYPE_CONTINUOUS)) {
+        } else if (frmivalenum.type == V4L2_FRMIVAL_TYPE_STEPWISE) {
             int d = frmivalenum.stepwise.min.denominator;
             // !!! FIXME: should we step by the numerator...?
             for (int n = (int) frmivalenum.stepwise.min.numerator; n <= (int) frmivalenum.stepwise.max.numerator; n += (int) frmivalenum.stepwise.step.numerator) {
@@ -713,6 +713,50 @@ static bool AddCameraFormat(const int fd, CameraFormatAddData *data, SDL_PixelFo
                 d += (int) frmivalenum.stepwise.step.denominator;
             }
             break;
+        } else if (frmivalenum.type == V4L2_FRMIVAL_TYPE_CONTINUOUS) {
+            // FIXME: The current API does not enable exposing continuous ranges, so for now let's expose some common values that are within the range
+            const int min_numer = frmivalenum.stepwise.min.numerator;
+            const int min_denom = frmivalenum.stepwise.min.denominator;
+            const int max_numer = frmivalenum.stepwise.max.numerator;
+            const int max_denom = frmivalenum.stepwise.max.denominator;
+            const float minrate = (float) min_numer / (float) min_denom;
+            const float maxrate = (float) max_numer / (float) max_denom;
+            if (minrate <= 1.001 / 24 && maxrate >= 1.001 / 24) {
+                if (!SDL_AddCameraFormat(data, sdlfmt, colorspace, w, h, 24000, 1001)) {
+                    return false;
+                }
+            }
+            if (minrate <= 1.000 / 24 && maxrate >= 1.000 / 24) {
+                if (!SDL_AddCameraFormat(data, sdlfmt, colorspace, w, h, 24000, 1000)) {
+                    return false;
+                }
+            }
+            if (minrate <= 1.001 / 30 && maxrate >= 1.001 / 30) {
+                if (!SDL_AddCameraFormat(data, sdlfmt, colorspace, w, h, 30000, 1001)) {
+                    return false;
+                }
+            }
+            if (minrate <= 1.000 / 30 && maxrate >= 1.000 / 30) {
+                if (!SDL_AddCameraFormat(data, sdlfmt, colorspace, w, h, 30000, 1000)) {
+                    return false;
+                }
+            }
+            if (minrate <= 1.000 / 50 && maxrate >= 1.000 / 50) {
+                if (!SDL_AddCameraFormat(data, sdlfmt, colorspace, w, h, 50000, 1000)) {
+                    return false;
+                }
+            }
+            if (minrate <= 1.001 / 60 && maxrate >= 1.001 / 60) {
+                if (!SDL_AddCameraFormat(data, sdlfmt, colorspace, w, h, 60000, 1001)) {
+                    return false;
+                }
+            }
+            if (minrate <= 1.000 / 60 && maxrate >= 1.000 / 60) {
+                if (!SDL_AddCameraFormat(data, sdlfmt, colorspace, w, h, 60000, 1000)) {
+                    return false;
+                }
+            }
+            break;
         }
     }