SDL icon indicating copy to clipboard operation
SDL copied to clipboard

SDL_OpenCameraDevice doesn't handle some width and height

Open Kaktus514 opened this issue 1 year ago • 2 comments

I've noticed that some width and height values causes the camera thread to crash a second or so after calling SDL_OpenCameraDevice. For me this happens for example when height=100 or when width=30.

SDL_GetCameraDeviceSupportedFormats says the supported format is 640x480 but according to the docs it should work with other specs.

The following program can be used to reproduce the problem:

#include <SDL3/SDL.h>

int main()
{
	SDL_Init(SDL_INIT_VIDEO | SDL_INIT_CAMERA);

	int camera_count;
	SDL_CameraDeviceID* camera_ids = SDL_GetCameraDevices(&camera_count);
	if (camera_ids != NULL && camera_count > 0)
	{
		SDL_CameraSpec spec = {0};
		spec.format = SDL_PIXELFORMAT_RGBA8888;
		spec.width = 100;
		spec.height = 100;
		spec.interval_numerator = 1;
		spec.interval_denominator = 30;
		SDL_Camera* camera = SDL_OpenCameraDevice(*camera_ids, &spec);
		if (camera != NULL)
		{
			SDL_Delay(3000); // wait for the bug to happen
			SDL_CloseCamera(camera);
		}
	}
	
	SDL_Quit();
}

The error message is:

double free or corruption (!prev)
Aborted

Running it through Valgrind gives the following message:

==12201== Thread 2 SDLCamera2:
==12201== Invalid write of size 4
==12201==    at 0x49BCE2F: scale_mat_nearest_4 (in /usr/local/lib/libSDL3.so.0.1.1)
==12201==    by 0x49BCF4D: SDL_LowerSoftStretchNearest (in /usr/local/lib/libSDL3.so.0.1.1)
==12201==    by 0x49BAD8C: SDL_SoftStretch_REAL (in /usr/local/lib/libSDL3.so.0.1.1)
==12201==    by 0x4891ADF: SDL_CameraThreadIterate (in /usr/local/lib/libSDL3.so.0.1.1)
==12201==    by 0x4891CB5: CameraThread (in /usr/local/lib/libSDL3.so.0.1.1)
==12201==    by 0x4941AE9: SDL_RunThread (in /usr/local/lib/libSDL3.so.0.1.1)
==12201==    by 0x4AB9D64: RunThread (in /usr/local/lib/libSDL3.so.0.1.1)
==12201==    by 0x4EB5EA6: start_thread (pthread_create.c:477)
==12201==    by 0x4C8BA2E: clone (clone.S:95)
==12201==  Address 0x6678c68 is 0 bytes after a block of size 20,040 alloc'd
==12201==    at 0x483877F: malloc (vg_replace_malloc.c:307)
==12201==    by 0x493C193: real_malloc (in /usr/local/lib/libSDL3.so.0.1.1)
==12201==    by 0x493C3DF: SDL_malloc_REAL (in /usr/local/lib/libSDL3.so.0.1.1)
==12201==    by 0x493EC18: SDL_aligned_alloc_REAL (in /usr/local/lib/libSDL3.so.0.1.1)
==12201==    by 0x49BD67F: SDL_CreateSurface_REAL (in /usr/local/lib/libSDL3.so.0.1.1)
==12201==    by 0x489268E: SDL_OpenCameraDevice_REAL (in /usr/local/lib/libSDL3.so.0.1.1)
==12201==    by 0x48A3B17: SDL_OpenCameraDevice (in /usr/local/lib/libSDL3.so.0.1.1)
==12201==    by 0x109202: main (test.c:17)

Kaktus514 avatar Apr 09 '24 18:04 Kaktus514

Sam made some fixes to image size choice recently; if you get a moment, can you try the latest and see if it fixes this issue?

icculus avatar May 18 '24 16:05 icculus

It's not fixed.

I looked a bit deeper and I think I know what's going wrong now.

  • SDL will use SDL_SoftStretch when the specified size is not among the sizes returned from SDL_GetCameraDeviceSupportedFormats.
  • My camera uses YUY2 format.
  • SDL_SoftStretch does not handle the YUY2 format properly which leads to memory corruption.
  • Specifying a different format does not help because stretching still happens in YUY2 format (presumably format conversion happens after stretching).

Two other functions that also have issues with the YUY2 format are SDL_FillSurfaceRect and SDL_FillSurfaceRects.

Kaktus514 avatar Jun 24 '24 21:06 Kaktus514

Can you try this patch?

diff --git a/src/video/SDL_stretch.c b/src/video/SDL_stretch.c
index fb97cf6f6..1778ccc04 100644
--- a/src/video/SDL_stretch.c
+++ b/src/video/SDL_stretch.c
@@ -35,9 +35,53 @@ int SDL_SoftStretch(SDL_Surface *src, const SDL_Rect *srcrect,
     SDL_Rect full_src;
     SDL_Rect full_dst;

+    if (!src) {
+        return SDL_InvalidParamError("src");
+    }
+    if (!dst) {
+        return SDL_InvalidParamError("dst");
+    }

     if (src->format->format != dst->format->format) {
-        return SDL_SetError("Only works with same format surfaces");
+        // Slow!
+        SDL_Surface *src_tmp = SDL_ConvertSurfaceFormat(src, dst->format->format);
+        if (!src_tmp) {
+            return -1;
+        }
+        ret = SDL_SoftStretch(src_tmp, srcrect, dst, dstrect, scaleMode);
+        SDL_DestroySurface(src_tmp);
+        return ret;
+    }
+
+    if (SDL_ISPIXELFORMAT_FOURCC(src->format->format)) {
+        // Slow!
+        if (!dstrect) {
+            full_dst.x = 0;
+            full_dst.y = 0;
+            full_dst.w = dst->w;
+            full_dst.h = dst->h;
+            dstrect = &full_dst;
+        }
+
+        SDL_Surface *src_tmp = SDL_ConvertSurfaceFormat(src, SDL_PIXELFORMAT_XRGB8888);
+        SDL_Surface *dst_tmp = SDL_CreateSurface(dstrect->w, dstrect->h, SDL_PIXELFORMAT_XRGB8888);
+        if (src_tmp && dst_tmp) {
+            ret = SDL_SoftStretch(src_tmp, srcrect, dst_tmp, NULL, scaleMode);
+            if (ret == 0) {
+                SDL_Colorspace dst_colorspace = SDL_COLORSPACE_UNKNOWN;
+                SDL_GetSurfaceColorspace(dst, &dst_colorspace);
+                SDL_ConvertPixelsAndColorspace(dstrect->w, dstrect->h,
+                    dst_tmp->format->format, SDL_COLORSPACE_SRGB, 0,
+                    dst_tmp->pixels, dst_tmp->pitch,
+                    dst->format->format, dst_colorspace, SDL_GetSurfaceProperties(dst),
+                    (Uint8 *)dst->pixels + dstrect->y * dst->pitch + dstrect->x * dst->format->bytes_per_pixel, dst->pitch);
+            }
+        } else {
+            ret = -1;
+        }
+        SDL_DestroySurface(src_tmp);
+        SDL_DestroySurface(dst_tmp);
+        return ret;
     }

     if (scaleMode != SDL_SCALEMODE_NEAREST && scaleMode != SDL_SCALEMODE_LINEAR && scaleMode != SDL_SCALEMODE_BEST) {

slouken avatar Jul 06 '24 05:07 slouken

That patch fixes the issue. I no longer experience any crashes or memory corruption.

Kaktus514 avatar Jul 06 '24 12:07 Kaktus514

Great, thanks!

slouken avatar Jul 06 '24 12:07 slouken