SDL: Add SDL Video Capture, with back-end for linux/macos/ios/android

From 59f93e20a714cf4f6ff3dd3ed7daf3a73e7e7656 Mon Sep 17 00:00:00 2001
From: Sylvain <[EMAIL REDACTED]>
Date: Thu, 9 Nov 2023 11:11:07 +0100
Subject: [PATCH] Add SDL Video Capture, with back-end for
 linux/macos/ios/android

---
 CMakeLists.txt                                |   9 +
 VisualC-WinRT/SDL-UWP.vcxproj                 |   3 +
 VisualC-WinRT/SDL-UWP.vcxproj.filters         |   9 +
 VisualC/SDL/SDL.vcxproj                       |   1 +
 VisualC/SDL/SDL.vcxproj.filters               |   3 +
 include/SDL3/SDL.h                            |   1 +
 include/SDL3/SDL_video_capture.h              | 369 +++++++
 include/build_config/SDL_build_config.h.cmake |   4 +
 include/build_config/SDL_build_config_ios.h   |   2 +
 include/build_config/SDL_build_config_macos.h |   2 +
 src/dynapi/SDL_dynapi.sym                     |  16 +
 src/dynapi/SDL_dynapi_overrides.h             |  16 +
 src/dynapi/SDL_dynapi_procs.h                 |  16 +
 src/video/SDL_sysvideocapture.h               |  90 ++
 src/video/SDL_video.c                         |  10 +
 src/video/SDL_video_capture.c                 | 948 +++++++++++++++++
 src/video/SDL_video_capture_apple.m           | 615 +++++++++++
 src/video/SDL_video_capture_c.h               |  33 +
 src/video/SDL_video_capture_v4l2.c            | 965 ++++++++++++++++++
 src/video/android/SDL_android_video_capture.c | 678 ++++++++++++
 test/CMakeLists.txt                           |   2 +
 test/testvideocapture.c                       | 770 ++++++++++++++
 test/testvideocaptureminimal.c                | 206 ++++
 23 files changed, 4768 insertions(+)
 create mode 100644 include/SDL3/SDL_video_capture.h
 create mode 100644 src/video/SDL_sysvideocapture.h
 create mode 100644 src/video/SDL_video_capture.c
 create mode 100644 src/video/SDL_video_capture_apple.m
 create mode 100644 src/video/SDL_video_capture_c.h
 create mode 100644 src/video/SDL_video_capture_v4l2.c
 create mode 100644 src/video/android/SDL_android_video_capture.c
 create mode 100644 test/testvideocapture.c
 create mode 100644 test/testvideocaptureminimal.c

diff --git a/CMakeLists.txt b/CMakeLists.txt
index ca03351edb19..5564c756014a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -343,6 +343,7 @@ set_option(SDL_METAL               "Enable Metal support" ${APPLE})
 set_option(SDL_KMSDRM              "Use KMS DRM video driver" ${UNIX_SYS})
 dep_option(SDL_KMSDRM_SHARED       "Dynamically load KMS DRM support" ON "SDL_KMSDRM" OFF)
 set_option(SDL_OFFSCREEN           "Use offscreen video driver" ON)
+dep_option(SDL_VIDEO_CAPTURE       "Enable video capturing" ON SDL_VIDEO OFF)
 option_string(SDL_BACKGROUNDING_SIGNAL "number to use for magic backgrounding signal or 'OFF'" OFF)
 option_string(SDL_FOREGROUNDING_SIGNAL "number to use for magic foregrounding signal or 'OFF'" OFF)
 dep_option(SDL_HIDAPI              "Enable the HIDAPI subsystem" ON "NOT VISIONOS" OFF)
@@ -2047,6 +2048,10 @@ elseif(APPLE)
     set(HAVE_SDL_FILE TRUE)
   endif()
 
+  if(IOS OR TVOS OR MACOSX OR DARWIN)
+    sdl_sources("${SDL3_SOURCE_DIR}/src/video/SDL_video_capture_apple.m")
+  endif()
+
   if(SDL_MISC)
     if(IOS OR TVOS OR VISIONOS)
       sdl_glob_sources("${SDL3_SOURCE_DIR}/src/misc/ios/*.m")
@@ -2230,6 +2235,10 @@ elseif(APPLE)
 
   # Actually load the frameworks at the end so we don't duplicate include.
   if(SDL_FRAMEWORK_COREVIDEO)
+    find_library(COREMEDIA CoreMedia)
+    if(COREMEDIA)
+      sdl_link_dependency(corevideo LINK_OPTIONS "-Wl,-framework,CoreMedia")
+    endif()
     sdl_link_dependency(corevideo LINK_OPTIONS "-Wl,-framework,CoreVideo")
   endif()
   if(SDL_FRAMEWORK_COCOA)
diff --git a/VisualC-WinRT/SDL-UWP.vcxproj b/VisualC-WinRT/SDL-UWP.vcxproj
index 1085c0f45200..f7eca1ef796b 100644
--- a/VisualC-WinRT/SDL-UWP.vcxproj
+++ b/VisualC-WinRT/SDL-UWP.vcxproj
@@ -89,6 +89,7 @@
     <ClInclude Include="..\include\SDL3\SDL_types.h" />
     <ClInclude Include="..\include\SDL3\SDL_version.h" />
     <ClInclude Include="..\include\SDL3\SDL_video.h" />
+    <ClInclude Include="..\include\SDL3\SDL_video_capture.h" />
     <ClInclude Include="..\src\audio\disk\SDL_diskaudio.h" />
     <ClInclude Include="..\src\audio\dummy\SDL_dummyaudio.h" />
     <ClInclude Include="..\src\audio\SDL_audiodev_c.h" />
@@ -180,6 +181,7 @@
     <ClInclude Include="..\src\video\SDL_RLEaccel_c.h" />
     <ClInclude Include="..\src\video\SDL_shape_internals.h" />
     <ClInclude Include="..\src\video\SDL_sysvideo.h" />
+    <ClInclude Include="..\src\video\SDL_sysvidocapture.h" />
     <ClInclude Include="..\src\video\SDL_yuv_c.h" />
     <ClInclude Include="..\src\video\winrt\SDL_winrtevents_c.h" />
     <ClInclude Include="..\src\video\winrt\SDL_winrtgamebar_cpp.h" />
@@ -520,6 +522,7 @@
     <ClCompile Include="..\src\video\SDL_stretch.c" />
     <ClCompile Include="..\src\video\SDL_surface.c" />
     <ClCompile Include="..\src\video\SDL_video.c" />
+    <ClCompile Include="..\src\video\SDL_video_capture.c" />
     <ClCompile Include="..\src\video\SDL_video_unsupported.c" />
     <ClCompile Include="..\src\video\SDL_yuv.c" />
     <ClCompile Include="..\src\video\winrt\SDL_winrtevents.cpp">
diff --git a/VisualC-WinRT/SDL-UWP.vcxproj.filters b/VisualC-WinRT/SDL-UWP.vcxproj.filters
index ab0ca0cfb7ab..fc905ac9e852 100644
--- a/VisualC-WinRT/SDL-UWP.vcxproj.filters
+++ b/VisualC-WinRT/SDL-UWP.vcxproj.filters
@@ -165,6 +165,9 @@
     <ClInclude Include="..\include\SDL3\SDL_video.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\include\SDL3\SDL_video_capture.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
     <ClInclude Include="..\src\joystick\SDL_gamepad_c.h">
       <Filter>Header Files</Filter>
     </ClInclude>
@@ -405,6 +408,9 @@
     <ClInclude Include="..\src\video\SDL_sysvideo.h">
       <Filter>Source Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\src\video\SDL_sysvideocapture.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
     <ClInclude Include="..\src\video\winrt\SDL_winrtevents_c.h">
       <Filter>Source Files</Filter>
     </ClInclude>
@@ -807,6 +813,9 @@
     <ClCompile Include="..\src\video\SDL_video.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\video\SDL_video_capture.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="..\src\video\SDL_video_unsupported.c">
       <Filter>Source Files</Filter>
     </ClCompile>
diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj
index 35edb9b8a6a4..fc53003e73fb 100644
--- a/VisualC/SDL/SDL.vcxproj
+++ b/VisualC/SDL/SDL.vcxproj
@@ -653,6 +653,7 @@
     <ClCompile Include="..\..\src\video\SDL_surface.c" />
     <ClCompile Include="..\..\src\video\SDL_video.c" />
     <ClCompile Include="..\..\src\video\SDL_video_unsupported.c" />
+    <ClCompile Include="..\..\src\video\SDL_video_capture.c" />
     <ClCompile Include="..\..\src\video\SDL_vulkan_utils.c" />
     <ClCompile Include="..\..\src\video\SDL_yuv.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsclipboard.c" />
diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters
index b367e7695fe6..c9d6f36e5d47 100644
--- a/VisualC/SDL/SDL.vcxproj.filters
+++ b/VisualC/SDL/SDL.vcxproj.filters
@@ -1185,6 +1185,9 @@
     <ClCompile Include="..\..\src\video\SDL_video_unsupported.c">
       <Filter>video</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\video\SDL_video_capture.c">
+      <Filter>video</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\src\video\SDL_yuv.c">
       <Filter>video</Filter>
     </ClCompile>
diff --git a/include/SDL3/SDL.h b/include/SDL3/SDL.h
index 498f0520c15c..6db0e69d47e5 100644
--- a/include/SDL3/SDL.h
+++ b/include/SDL3/SDL.h
@@ -76,6 +76,7 @@
 #include <SDL3/SDL_touch.h>
 #include <SDL3/SDL_version.h>
 #include <SDL3/SDL_video.h>
+#include "SDL3/SDL_video_capture.h"
 #include <SDL3/SDL_oldnames.h>
 
 #endif /* SDL_h_ */
diff --git a/include/SDL3/SDL_video_capture.h b/include/SDL3/SDL_video_capture.h
new file mode 100644
index 000000000000..3d3ce7e64198
--- /dev/null
+++ b/include/SDL3/SDL_video_capture.h
@@ -0,0 +1,369 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+/**
+ *  \file SDL_video_capture.h
+ *
+ *  Video Capture for the SDL library.
+ */
+
+#ifndef SDL_video_capture_h_
+#define SDL_video_capture_h_
+
+#include "SDL3/SDL_video.h"
+
+#include <SDL3/SDL_begin_code.h>
+/* Set up for C function definitions, even when using C++ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * This is a unique ID for a video capture device for the time it is connected to the system,
+ * and is never reused for the lifetime of the application. If the device is
+ * disconnected and reconnected, it will get a new ID.
+ *
+ * The ID value starts at 1 and increments from there. The value 0 is an invalid ID.
+ *
+ * \sa SDL_GetVideoCaptureDevices
+ */
+typedef Uint32 SDL_VideoCaptureDeviceID;
+
+
+/**
+ * The structure used to identify an SDL video capture device
+ */
+struct SDL_VideoCaptureDevice;
+typedef struct SDL_VideoCaptureDevice SDL_VideoCaptureDevice;
+
+#define SDL_VIDEO_CAPTURE_ALLOW_ANY_CHANGE          1
+
+/**
+ *  SDL_VideoCaptureSpec structure
+ *
+ *  Only those field can be 'desired' when configuring the device:
+ *  - format
+ *  - width
+ *  - height
+ *
+ *  \sa SDL_GetVideoCaptureFormat
+ *  \sa SDL_GetVideoCaptureFrameSize
+ *
+ */
+typedef struct SDL_VideoCaptureSpec
+{
+    Uint32 format;          /**< Frame SDL_PixelFormatEnum format */
+    int width;              /**< Frame width */
+    int height;             /**< Frame height */
+} SDL_VideoCaptureSpec;
+
+/**
+ *  SDL Video Capture Status
+ *
+ *  Change states but calling the function in this order:
+ *
+ *  SDL_OpenVideoCapture()
+ *  SDL_SetVideoCaptureSpec()  -> Init
+ *  SDL_StartVideoCapture()    -> Playing
+ *  SDL_StopVideoCapture()     -> Stopped
+ *  SDL_CloseVideoCapture()
+ *
+ */
+typedef enum
+{
+    SDL_VIDEO_CAPTURE_FAIL = -1,    /**< Failed */
+    SDL_VIDEO_CAPTURE_INIT = 0,     /**< Init, spec hasn't been set */
+    SDL_VIDEO_CAPTURE_STOPPED,      /**< Stopped */
+    SDL_VIDEO_CAPTURE_PLAYING       /**< Playing */
+} SDL_VideoCaptureStatus;
+
+/**
+ *  SDL Video Capture Status
+ */
+typedef struct SDL_VideoCaptureFrame
+{
+    Uint64 timestampNS;         /**< Frame timestamp in nanoseconds when read from the driver */
+    int num_planes;             /**< Number of planes */
+    Uint8 *data[3];             /**< Pointer to data of i-th plane */
+    int pitch[3];               /**< Pitch of i-th plane */
+    void *internal;             /**< Private field */
+} SDL_VideoCaptureFrame;
+
+
+/**
+ * Get a list of currently connected video capture devices.
+ *
+ * \param count a pointer filled in with the number of video capture devices
+ * \returns a 0 terminated array of video capture instance IDs which should be
+ *          freed with SDL_free(), or NULL on error; call SDL_GetError() for
+ *          more details.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_OpenVideoCapture
+ */
+extern DECLSPEC SDL_VideoCaptureDeviceID *SDLCALL SDL_GetVideoCaptureDevices(int *count);
+
+/**
+ * Open a Video Capture device
+ *
+ * \param instance_id the video capture device instance ID
+ * \returns device, or NULL on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_GetVideoCaptureDeviceName
+ * \sa SDL_GetVideoCaptureDevices
+ * \sa SDL_OpenVideoCaptureWithSpec
+ */
+extern DECLSPEC SDL_VideoCaptureDevice *SDLCALL SDL_OpenVideoCapture(SDL_VideoCaptureDeviceID instance_id);
+
+/**
+ * Set specification
+ *
+ * \param device opened video capture device
+ * \param desired desired video capture spec
+ * \param obtained obtained video capture spec
+ * \param allowed_changes allow changes or not
+ * \returns 0 on success or a negative error code on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_OpenVideoCapture
+ * \sa SDL_OpenVideoCaptureWithSpec
+ * \sa SDL_GetVideoCaptureSpec
+ */
+extern DECLSPEC int SDLCALL SDL_SetVideoCaptureSpec(SDL_VideoCaptureDevice *device,
+                                                    const SDL_VideoCaptureSpec *desired,
+                                                    SDL_VideoCaptureSpec *obtained,
+                                                    int allowed_changes);
+
+/**
+ * Open a Video Capture device and set specification
+ *
+ * \param instance_id the video capture device instance ID
+ * \param desired desired video capture spec
+ * \param obtained obtained video capture spec
+ * \param allowed_changes allow changes or not
+ * \returns device, or NULL on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_OpenVideoCapture
+ * \sa SDL_SetVideoCaptureSpec
+ * \sa SDL_GetVideoCaptureSpec
+ */
+extern DECLSPEC SDL_VideoCaptureDevice *SDLCALL SDL_OpenVideoCaptureWithSpec(SDL_VideoCaptureDeviceID instance_id,
+                                                                              const SDL_VideoCaptureSpec *desired,
+                                                                              SDL_VideoCaptureSpec *obtained,
+                                                                              int allowed_changes);
+/**
+ * Get device name
+ *
+ * \param instance_id the video capture device instance ID
+ * \returns device name, shouldn't be freed
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_GetVideoCaptureDevices
+ */
+extern DECLSPEC const char * SDLCALL SDL_GetVideoCaptureDeviceName(SDL_VideoCaptureDeviceID instance_id);
+
+/**
+ * Get the obtained video capture spec
+ *
+ * \param device opened video capture device
+ * \param spec The SDL_VideoCaptureSpec to be initialized by this function.
+ * \returns 0 on success or a negative error code on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_SetVideoCaptureSpec
+ * \sa SDL_OpenVideoCaptureWithSpec
+ */
+extern DECLSPEC int SDLCALL SDL_GetVideoCaptureSpec(SDL_VideoCaptureDevice *device, SDL_VideoCaptureSpec *spec);
+
+
+/**
+ * Get frame format of video capture device.
+ * The value can be used to fill SDL_VideoCaptureSpec structure.
+ *
+ * \param device opened video capture device
+ * \param index format between 0 and num -1
+ * \param format pointer output format (SDL_PixelFormatEnum)
+ * \returns 0 on success or a negative error code on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_GetNumVideoCaptureFormats
+ */
+extern DECLSPEC int SDLCALL SDL_GetVideoCaptureFormat(SDL_VideoCaptureDevice *device,
+                                                      int index,
+                                                      Uint32 *format);
+
+/**
+ * Number of available formats for the device
+ *
+ * \param device opened video capture device
+ * \returns number of formats or a negative error code on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_GetVideoCaptureFormat
+ * \sa SDL_SetVideoCaptureSpec
+ */
+extern DECLSPEC int SDLCALL SDL_GetNumVideoCaptureFormats(SDL_VideoCaptureDevice *device);
+
+/**
+ * Get frame sizes of the device and the specified input format.
+ * The value can be used to fill SDL_VideoCaptureSpec structure.
+ *
+ * \param device opened video capture device
+ * \param format a format that can be used by the device (SDL_PixelFormatEnum)
+ * \param index framesize between 0 and num -1
+ * \param width output width
+ * \param height output height
+ * \returns 0 on success or a negative error code on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_GetNumVideoCaptureFrameSizes
+ */
+extern DECLSPEC int SDLCALL SDL_GetVideoCaptureFrameSize(SDL_VideoCaptureDevice *device, Uint32 format, int index, int *width, int *height);
+
+/**
+ * Number of different framesizes available for the device and pixel format.
+ *
+ * \param device opened video capture device
+ * \param format frame pixel format (SDL_PixelFormatEnum)
+ * \returns number of framesizes or a negative error code on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_GetVideoCaptureFrameSize
+ * \sa SDL_SetVideoCaptureSpec
+ */
+extern DECLSPEC int SDLCALL SDL_GetNumVideoCaptureFrameSizes(SDL_VideoCaptureDevice *device, Uint32 format);
+
+
+/**
+ * Get video capture status
+ *
+ * \param device opened video capture device
+ * \returns 0 on success or a negative error code on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_VideoCaptureStatus
+ */
+extern DECLSPEC SDL_VideoCaptureStatus SDLCALL SDL_GetVideoCaptureStatus(SDL_VideoCaptureDevice *device);
+
+/**
+ * Start video capture
+ *
+ * \param device opened video capture device
+ * \returns 0 on success or a negative error code on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_StopVideoCapture
+ */
+extern DECLSPEC int SDLCALL SDL_StartVideoCapture(SDL_VideoCaptureDevice *device);
+
+/**
+ * Acquire a frame.
+ * The frame is a memory pointer to the image data, whose size and format
+ * are given by the the obtained spec.
+ *
+ * Non blocking API. If there is a frame available, frame->num_planes is non 0.
+ * If frame->num_planes is 0 and returned code is 0, there is no frame at that time.
+ *
+ * After used, the frame should be released with SDL_ReleaseVideoCaptureFrame
+ *
+ * \param device opened video capture device
+ * \param frame pointer to get the frame
+ * \returns 0 on success or a negative error code on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_ReleaseVideoCaptureFrame
+ */
+extern DECLSPEC int SDLCALL SDL_AcquireVideoCaptureFrame(SDL_VideoCaptureDevice *device, SDL_VideoCaptureFrame *frame);
+
+/**
+ * Release a frame. Let the back-end re-use the internal buffer for video capture.
+ *
+ * All acquired frames should be released before closing the device.
+ *
+ * \param device opened video capture device
+ * \param frame frame pointer.
+ * \returns 0 on success or a negative error code on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_AcquireVideoCaptureFrame
+ */
+extern DECLSPEC int SDLCALL SDL_ReleaseVideoCaptureFrame(SDL_VideoCaptureDevice *device, SDL_VideoCaptureFrame *frame);
+
+/**
+ * Stop Video Capture
+ *
+ * \param device opened video capture device
+ * \returns 0 on success or a negative error code on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_StartVideoCapture
+ */
+extern DECLSPEC int SDLCALL SDL_StopVideoCapture(SDL_VideoCaptureDevice *device);
+
+/**
+ * Use this function to shut down video_capture processing and close the video_capture device.
+ *
+ * \param device opened video capture device
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_OpenVideoCaptureWithSpec
+ * \sa SDL_OpenVideoCapture
+ */
+extern DECLSPEC void SDLCALL SDL_CloseVideoCapture(SDL_VideoCaptureDevice *device);
+
+/* Ends C function definitions when using C++ */
+#ifdef __cplusplus
+}
+#endif
+#include <SDL3/SDL_close_code.h>
+
+#endif /* SDL_video_capture_h_ */
diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake
index b776251f81e4..8b26e0dea929 100644
--- a/include/build_config/SDL_build_config.h.cmake
+++ b/include/build_config/SDL_build_config.h.cmake
@@ -244,11 +244,15 @@
 
 #cmakedefine USE_POSIX_SPAWN @USE_POSIX_SPAWN@
 
+#cmakedefine HAVE_COREMEDIA
+
 /* SDL internal assertion support */
 #if @SDL_DEFAULT_ASSERT_LEVEL_CONFIGURED@
 #cmakedefine SDL_DEFAULT_ASSERT_LEVEL @SDL_DEFAULT_ASSERT_LEVEL@
 #endif
 
+#cmakedefine SDL_VIDEO_CAPTURE
+
 /* Allow disabling of core subsystems */
 #cmakedefine SDL_ATOMIC_DISABLED @SDL_ATOMIC_DISABLED@
 #cmakedefine SDL_AUDIO_DISABLED @SDL_AUDIO_DISABLED@
diff --git a/include/build_config/SDL_build_config_ios.h b/include/build_config/SDL_build_config_ios.h
index 96391f848f57..340bbc0c2997 100644
--- a/include/build_config/SDL_build_config_ios.h
+++ b/include/build_config/SDL_build_config_ios.h
@@ -197,6 +197,8 @@
 #define SDL_VIDEO_METAL 1
 #endif
 
+#define HAVE_COREMEDIA  1
+
 /* Enable system power support */
 #define SDL_POWER_UIKIT 1
 
diff --git a/include/build_config/SDL_build_config_macos.h b/include/build_config/SDL_build_config_macos.h
index 43400d3f6ef4..bf1b682ed02b 100644
--- a/include/build_config/SDL_build_config_macos.h
+++ b/include/build_config/SDL_build_config_macos.h
@@ -260,6 +260,8 @@
 #endif
 #endif
 
+#define HAVE_COREMEDIA  1
+
 /* Enable system power support */
 #define SDL_POWER_MACOSX 1
 
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index e6fd996c6429..96e662f2c51e 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -923,6 +923,22 @@ SDL3_0.0.0 {
     SDL_SetPropertyWithCleanup;
     SDL_SetX11EventHook;
     SDL_GetGlobalProperties;
+    SDL_OpenVideoCapture;
+    SDL_SetVideoCaptureSpec;
+    SDL_OpenVideoCaptureWithSpec;
+    SDL_GetVideoCaptureDeviceName;
+    SDL_GetVideoCaptureSpec;
+    SDL_GetVideoCaptureFormat;
+    SDL_GetNumVideoCaptureFormats;
+    SDL_GetVideoCaptureFrameSize;
+    SDL_GetNumVideoCaptureFrameSizes;
+    SDL_GetVideoCaptureStatus;
+    SDL_StartVideoCapture;
+    SDL_AcquireVideoCaptureFrame;
+    SDL_ReleaseVideoCaptureFrame;
+    SDL_StopVideoCapture;
+    SDL_CloseVideoCapture;
+    SDL_GetVideoCaptureDevices;
     # extra symbols go here (don't modify this line)
   local: *;
 };
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 97d05e88ba0b..adb202523cbc 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -948,3 +948,19 @@
 #define SDL_SetPropertyWithCleanup SDL_SetPropertyWithCleanup_REAL
 #define SDL_SetX11EventHook SDL_SetX11EventHook_REAL
 #define SDL_GetGlobalProperties SDL_GetGlobalProperties_REAL
+#define SDL_OpenVideoCapture SDL_OpenVideoCapture_REAL
+#define SDL_SetVideoCaptureSpec SDL_SetVideoCaptureSpec_REAL
+#define SDL_OpenVideoCaptureWithSpec SDL_OpenVideoCaptureWithSpec_REAL
+#define SDL_GetVideoCaptureDeviceName SDL_GetVideoCaptureDeviceName_REAL
+#define SDL_GetVideoCaptureSpec SDL_GetVideoCaptureSpec_REAL
+#define SDL_GetVideoCaptureFormat SDL_GetVideoCaptureFormat_REAL
+#define SDL_GetNumVideoCaptureFormats SDL_GetNumVideoCaptureFormats_REAL
+#define SDL_GetVideoCaptureFrameSize SDL_GetVideoCaptureFrameSize_REAL
+#define SDL_GetNumVideoCaptureFrameSizes SDL_GetNumVideoCaptureFrameSizes_REAL
+#define SDL_GetVideoCaptureStatus SDL_GetVideoCaptureStatus_REAL
+#define SDL_StartVideoCapture SDL_StartVideoCapture_REAL
+#define SDL_AcquireVideoCaptureFrame SDL_AcquireVideoCaptureFrame_REAL
+#define SDL_ReleaseVideoCaptureFrame SDL_ReleaseVideoCaptureFrame_REAL
+#define SDL_StopVideoCapture SDL_StopVideoCapture_REAL
+#define SDL_CloseVideoCapture SDL_CloseVideoCapture_REAL
+#define SDL_GetVideoCaptureDevices SDL_GetVideoCaptureDevices_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 64022522244f..7f3b0e521125 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -981,3 +981,19 @@ SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_GetDisplayProperties,(SDL_DisplayID a),(a),
 SDL_DYNAPI_PROC(int,SDL_SetPropertyWithCleanup,(SDL_PropertiesID a, const char *b, void *c, void (SDLCALL *d)(void *userdata, void *value), void *e),(a,b,c,d,e),return)
 SDL_DYNAPI_PROC(void,SDL_SetX11EventHook,(SDL_X11EventHook a, void *b),(a,b),)
 SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_GetGlobalProperties,(void),(),return)
+SDL_DYNAPI_PROC(SDL_VideoCaptureDevice*,SDL_OpenVideoCapture,(SDL_VideoCaptureDeviceID a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_SetVideoCaptureSpec,(SDL_VideoCaptureDevice *a, const SDL_VideoCaptureSpec *b, SDL_VideoCaptureSpec *c, int d),(a,b,c,d),return)
+SDL_DYNAPI_PROC(SDL_VideoCaptureDevice*,SDL_OpenVideoCaptureWithSpec,(SDL_VideoCaptureDeviceID a, const SDL_VideoCaptureSpec *b, SDL_VideoCaptureSpec *c, int d),(a,b,c,d),return)
+SDL_DYNAPI_PROC(const char*,SDL_GetVideoCaptureDeviceName,(SDL_VideoCaptureDeviceID a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_GetVideoCaptureSpec,(SDL_VideoCaptureDevice *a, SDL_VideoCaptureSpec *b),(a,b),return)
+SDL_DYNAPI_PROC(int,SDL_GetVideoCaptureFormat,(SDL_VideoCaptureDevice *a, int b, Uint32 *c),(a,b,c),return)
+SDL_DYNAPI_PROC(int,SDL_GetNumVideoCaptureFormats,(SDL_VideoCaptureDevice *a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_GetVideoCaptureFrameSize,(SDL_VideoCaptureDevice *a, Uint32 b, int c, int *d, int *e),(a,b,c,d,e),return)
+SDL_DYNAPI_PROC(int,SDL_GetNumVideoCaptureFrameSizes,(SDL_VideoCaptureDevice *a, Uint32 b),(a,b),return)
+SDL_DYNAPI_PROC(SDL_VideoCaptureStatus,SDL_GetVideoCaptureStatus,(SDL_VideoCaptureDevice *a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_StartVideoCapture,(SDL_VideoCaptureDevice *a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_AcquireVideoCaptureFrame,(SDL_VideoCaptureDevice *a, SDL_VideoCaptureFrame *b),(a,b),return)
+SDL_DYNAPI_PROC(int,SDL_ReleaseVideoCaptureFrame,(SDL_VideoCaptureDevice *a, SDL_VideoCaptureFrame *b),(a,b),return)
+SDL_DYNAPI_PROC(int,SDL_StopVideoCapture,(SDL_VideoCaptureDevice *a),(a),return)
+SDL_DYNAPI_PROC(void,SDL_CloseVideoCapture,(SDL_VideoCaptureDevice *a),(a),)
+SDL_DYNAPI_PROC(SDL_VideoCaptureDeviceID*,SDL_GetVideoCaptureDevices,(int *a),(a),return)
diff --git a/src/video/SDL_sysvideocapture.h b/src/video/SDL_sysvideocapture.h
new file mode 100644
index 000000000000..fe71664f5195
--- /dev/null
+++ b/src/video/SDL_sysvideocapture.h
@@ -0,0 +1,90 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../SDL_internal.h"
+
+#ifndef SDL_sysvideocapture_h_
+#define SDL_sysvideocapture_h_
+
+#include "../SDL_list.h"
+
+/* The SDL video_capture driver */
+typedef struct SDL_VideoCaptureDevice SDL_VideoCaptureDevice;
+
+/* Define the SDL video_capture driver structure */
+struct SDL_VideoCaptureDevice
+{
+    /* * * */
+    /* Data common to all devices */
+
+    /* The device's current video_capture specification */
+    SDL_VideoCaptureSpec spec;
+
+    /* Device name */
+    char *dev_name;
+
+    /* Current state flags */
+    SDL_AtomicInt shutdown;
+    SDL_AtomicInt enabled;
+    SDL_bool is_spec_set;
+
+    /* A mutex for locking the queue buffers */
+    SDL_Mutex *device_lock;
+    SDL_Mutex *acquiring_lock;
+
+    /* A thread to feed the video_capture device */
+    SDL_Thread *thread;
+    SDL_threadID threadid;
+
+    /* Queued buffers (if app not using callback). */
+    SDL_ListNode *buffer_queue;
+
+    /* * * */
+    /* Data private to this driver */
+    struct SDL_PrivateVideoCaptureData *hidden;
+};
+
+extern int OpenDevice(SDL_VideoCaptureDevice *_this);
+extern void CloseDevice(SDL_VideoCaptureDevice *_this);
+
+extern int InitDevice(SDL_VideoCaptureDevice *_this);
+
+extern int GetDeviceSpec(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureSpec *spec);
+
+extern int StartCapture(SDL_VideoCaptureDevice *_this);
+extern int StopCapture(SDL_VideoCaptureDevice *_this);
+
+extern int AcquireFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame);
+extern int ReleaseFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame);
+
+extern int GetNumFormats(SDL_VideoCaptureDevice *_this);
+extern int GetFormat(SDL_VideoCaptureDevice *_this, int index, Uint32 *format);
+
+extern int GetNumFrameSizes(SDL_VideoCaptureDevice *_this, Uint32 format);
+extern int GetFrameSize(SDL_VideoCaptureDevice *_this, Uint32 format, int index, int *width, int *height);
+
+extern int GetDeviceName(int index, char *buf, int size);
+extern int GetNumDevices(void);
+
+
+extern SDL_bool check_all_device_closed(void);
+extern SDL_bool check_device_playing(void);
+
+#endif /* SDL_sysvideocapture_h_ */
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index df0ad2351137..d4ab9a475d1a 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -31,6 +31,7 @@
 #include "SDL_video_c.h"
 #include "../events/SDL_events_c.h"
 #include "../timer/SDL_timer_c.h"
+#include "SDL_video_capture_c.h"
 
 #ifdef SDL_VIDEO_OPENGL
 #include <SDL3/SDL_opengl.h>
@@ -443,6 +444,7 @@ int SDL_VideoInit(const char *driver_name)
     SDL_bool init_keyboard = SDL_FALSE;
     SDL_bool init_mouse = SDL_FALSE;
     SDL_bool init_touch = SDL_FALSE;
+    SDL_bool init_video_capture = SDL_FALSE;
     int i = 0;
 
     /* Check to make sure we don't overwrite '_this' */
@@ -471,6 +473,10 @@ int SDL_VideoInit(const char *driver_name)
         goto pre_driver_error;
     }
     init_touch = SDL_TRUE;
+    if (SDL_VideoCaptureInit() < 0) {
+        goto pre_driver_error;
+    }
+    init_video_capture = SDL_TRUE;
 
     /* Select the proper video driver */
     video = NULL;
@@ -565,6 +571,9 @@ int SDL_VideoInit(const char *driver_name)
 
 pre_driver_error:
     SDL_assert(_this == NULL);
+    if (init_video_capture) {
+        SDL_QuitVideoCapture();
+    }
     if (init_touch) {
         SDL_QuitTouch();
     }
@@ -3684,6 +3693,7 @@ void SDL_VideoQuit(void)
     SDL_ClearClipboardData

(Patch may be truncated, please check the link at the top of this post.)