From e9dc24d9471d803a6a31aaf6627cdf993387eb02 Mon Sep 17 00:00:00 2001 From: Rosalie Wanders Date: Wed, 24 Sep 2025 23:26:16 +0200 Subject: [PATCH] 3rdParty: apply SDL3 gameboy camera backend patch to mupen64plus-core --- .../mupen64plus-core/projects/unix/Makefile | 8 + .../src/backends/api/video_capture_backend.c | 6 + .../src/backends/api/video_capture_backend.h | 2 + .../src/backends/sdl3_video_capture.c | 279 ++++++++++++++++++ .../mupen64plus-core/src/device/gb/gb_cart.c | 3 + .../3rdParty/mupen64plus-core/src/main/main.c | 5 +- 6 files changed, 299 insertions(+), 4 deletions(-) create mode 100644 Source/3rdParty/mupen64plus-core/src/backends/sdl3_video_capture.c diff --git a/Source/3rdParty/mupen64plus-core/projects/unix/Makefile b/Source/3rdParty/mupen64plus-core/projects/unix/Makefile index b26eb4a4..f5e07497 100644 --- a/Source/3rdParty/mupen64plus-core/projects/unix/Makefile +++ b/Source/3rdParty/mupen64plus-core/projects/unix/Makefile @@ -355,9 +355,13 @@ ifeq ($(origin SDL_CFLAGS) $(origin SDL_LDLIBS), undefined undefined) ifeq ($(shell $(PKG_CONFIG) --modversion sdl2 2>/dev/null),) $(error No SDL3 or SDL2 development libraries found!) endif + ifeq ($(SDL3_CAMERA), 1) + $(error SDL3_CAMERA requires SDL3!) + endif SDL_CFLAGS += $(shell $(PKG_CONFIG) --cflags sdl2) SDL_LDLIBS += $(shell $(PKG_CONFIG) --libs sdl2) else + SDL3_CAMERA := 1 SDL_CFLAGS += -DUSE_SDL3 SDL_CFLAGS += $(shell $(PKG_CONFIG) --cflags sdl3) SDL_LDLIBS += $(shell $(PKG_CONFIG) --libs sdl3) @@ -762,6 +766,10 @@ ifeq ($(OPENCV), 1) CFLAGS += -DM64P_OPENCV endif +ifeq ($(SDL3_CAMERA), 1) + SOURCE += $(SRCDIR)/backends/sdl3_video_capture.cpp + CFLAGS += -DSDL3_CAMERA +endif # generate a list of object files to build, make a temporary directory for them OBJECTS := $(patsubst $(SRCDIR)/%.c, $(OBJDIR)/%.o, $(filter $(SRCDIR)/%.c, $(SOURCE))) diff --git a/Source/3rdParty/mupen64plus-core/src/backends/api/video_capture_backend.c b/Source/3rdParty/mupen64plus-core/src/backends/api/video_capture_backend.c index 975d0e18..fb36d683 100644 --- a/Source/3rdParty/mupen64plus-core/src/backends/api/video_capture_backend.c +++ b/Source/3rdParty/mupen64plus-core/src/backends/api/video_capture_backend.c @@ -28,12 +28,18 @@ extern const struct video_capture_backend_interface g_idummy_video_capture_backe #if defined(M64P_OPENCV) extern const struct video_capture_backend_interface g_iopencv_video_capture_backend; #endif +#if defined(SDL3_CAMERA) +extern const struct video_capture_backend_interface g_isdl3_video_capture_backend; +#endif const struct video_capture_backend_interface* g_video_capture_backend_interfaces[] = { #if defined(M64P_OPENCV) &g_iopencv_video_capture_backend, +#endif +#if defined(SDL3_CAMERA) + &g_isdl3_video_capture_backend, #endif &g_idummy_video_capture_backend, NULL /* sentinel - must be last element */ diff --git a/Source/3rdParty/mupen64plus-core/src/backends/api/video_capture_backend.h b/Source/3rdParty/mupen64plus-core/src/backends/api/video_capture_backend.h index 5e078878..728b50ac 100644 --- a/Source/3rdParty/mupen64plus-core/src/backends/api/video_capture_backend.h +++ b/Source/3rdParty/mupen64plus-core/src/backends/api/video_capture_backend.h @@ -30,6 +30,8 @@ #if !defined(DEFAULT_VIDEO_CAPTURE_BACKEND) #if defined(M64P_OPENCV) #define DEFAULT_VIDEO_CAPTURE_BACKEND "opencv" +#elif defined(SDL3_CAMERA) +#define DEFAULT_VIDEO_CAPTURE_BACKEND "sdl3" #else #define DEFAULT_VIDEO_CAPTURE_BACKEND "" #endif diff --git a/Source/3rdParty/mupen64plus-core/src/backends/sdl3_video_capture.c b/Source/3rdParty/mupen64plus-core/src/backends/sdl3_video_capture.c new file mode 100644 index 00000000..545f7419 --- /dev/null +++ b/Source/3rdParty/mupen64plus-core/src/backends/sdl3_video_capture.c @@ -0,0 +1,279 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus - sdl3_video_capture.cpp * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2025 Rosalie Wanders * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include + +struct sdl3_video_capture +{ + unsigned int width; + unsigned int height; + + SDL_Camera* camera; + SDL_CameraID camera_id; + + char* target_camera_name; +}; + +#include "backends/api/video_capture_backend.h" + +#define M64P_CORE_PROTOTYPES 1 +#include "api/callbacks.h" +#include "api/m64p_types.h" +#include "api/m64p_config.h" + +#include + +const struct video_capture_backend_interface g_isdl3_video_capture_backend; + +static m64p_error sdl3_init(void** vcap, const char* section) +{ + /* initialize data */ + *vcap = malloc(sizeof(struct sdl3_video_capture)); + if (*vcap == NULL) + { + return M64ERR_NO_MEMORY; + } + + memset(*vcap, 0, sizeof(struct sdl3_video_capture)); + struct sdl3_video_capture* sdl = (struct sdl3_video_capture*)(*vcap); + + if (!SDL_Init(SDL_INIT_CAMERA)) + { + DebugMessage(M64MSG_ERROR, "Failed to initialize SDL camera subsystem: %s", SDL_GetError()); + return M64ERR_SYSTEM_FAIL; + } + + /* default parameters */ + const char* device = NULL; + + if (section && strlen(section) > 0) + { + m64p_handle config = NULL; + + if (ConfigOpenSection(section, &config) != M64ERR_SUCCESS) + { + DebugMessage(M64MSG_WARNING, "Failed to open video configuration section: %s, falling back to default video device", section); + return M64ERR_SUCCESS; + } + + /* set default parameters */ + ConfigSetDefaultString(config, "device", "", "Device name to use for capture or empty for default."); + + /* get parameters */ + device = ConfigGetParamString(config, "device"); + + /* fallback to NULL when default value has been used */ + if (strlen(device) == 0) + { + device = NULL; + } + } + + /* store device name for later */ + if (device != NULL) + { + sdl->target_camera_name = strdup(device); + } + + return M64ERR_SUCCESS; +} + +static void sdl3_release(void* vcap) +{ + struct sdl3_video_capture* sdl = (struct sdl3_video_capture*)(vcap); + if (sdl != NULL) + { + if (sdl->target_camera_name != NULL) + { + free(sdl->target_camera_name); + } + + free(sdl); + } + + if (SDL_WasInit(SDL_INIT_CAMERA)) + { + SDL_QuitSubSystem(SDL_INIT_CAMERA); + } +} + +static m64p_error sdl3_open(void* vcap, unsigned int width, unsigned int height) +{ + struct sdl3_video_capture* sdl = (struct sdl3_video_capture*)(vcap); + if (sdl == NULL) + { + return M64ERR_NOT_INIT; + } + + sdl->width = width; + sdl->height = height; + + int cameras_count = 0; + SDL_CameraID* cameras = SDL_GetCameras(&cameras_count); + if (cameras == NULL) + { + DebugMessage(M64MSG_ERROR, "Failed to retrieve list of video devices: %s", SDL_GetError()); + return M64ERR_SYSTEM_FAIL; + } + if (cameras_count == 0) + { + DebugMessage(M64MSG_WARNING, "Failed to find video devices"); + return M64ERR_INPUT_NOT_FOUND; + } + + /* fallback to default camera */ + sdl->camera_id = cameras[0]; + + /* print name of every camera to user, and + * attempt to find the camera that the user + * specified in the config */ + int found_camera = 0; + for (int i = 0; i < cameras_count; i++) + { + const char* name = SDL_GetCameraName(cameras[i]); + if (name == NULL) + { + DebugMessage(M64MSG_ERROR, "Failed to retrieve video device name: %s", SDL_GetError()); + continue; + } + + DebugMessage(M64MSG_INFO, "Found video device: \"%s\"", name); + + if (sdl->target_camera_name != NULL && + strcmp(sdl->target_camera_name, name) == 0) + { + sdl->camera_id = cameras[i]; + found_camera = 1; + break; + } + } + + /* show warning when device was not found */ + if (sdl->target_camera_name != NULL && found_camera == 0) + { + DebugMessage(M64MSG_WARNING, "Failed to find video device with name \"%s\", falling back to default", sdl->target_camera_name); + } + + SDL_free(cameras); + + /* attempt to open camera */ + sdl->camera = SDL_OpenCamera(sdl->camera_id, NULL); + if (sdl->camera == NULL) + { + DebugMessage(M64MSG_ERROR, "Failed to open video device: %s", SDL_GetError()); + return M64ERR_SYSTEM_FAIL; + } + + /* attempt to get permission for the camera */ + int permission_state = SDL_GetCameraPermissionState(sdl->camera); + if (permission_state == 0) + { + DebugMessage(M64MSG_INFO, "Waiting until user has approved video access"); + do + { + SDL_Delay(250); + permission_state = SDL_GetCameraPermissionState(sdl->camera); + } while (permission_state == 0); + } + + if (permission_state == -1) + { + DebugMessage(M64MSG_ERROR, "Failed to open video device: permission denied"); + return M64ERR_SYSTEM_FAIL; + } + + DebugMessage(M64MSG_INFO, "Video successfully opened: %s", SDL_GetCameraName(sdl->camera_id)); + return M64ERR_SUCCESS; +} + +static void sdl3_close(void* vcap) +{ + struct sdl3_video_capture* sdl = (struct sdl3_video_capture*)(vcap); + if (sdl == NULL) + { + return; + } + + if (sdl->camera != NULL) + { + SDL_CloseCamera(sdl->camera); + } + + DebugMessage(M64MSG_INFO, "Video closed"); +} + +static m64p_error sdl3_grab_image(void* vcap, void* data) +{ + struct sdl3_video_capture* sdl = (struct sdl3_video_capture*)(vcap); + if (sdl == NULL || sdl->camera == NULL) + { + return M64ERR_NOT_INIT; + } + + SDL_Surface* frame_surface = SDL_AcquireCameraFrame(sdl->camera, NULL); + if (frame_surface == NULL) + { + DebugMessage(M64MSG_ERROR, "Failed to grab video frame: %s", SDL_GetError()); + return M64ERR_SYSTEM_FAIL; + } + + SDL_Surface* target_surface = SDL_CreateSurface(sdl->width, sdl->height, SDL_PIXELFORMAT_BGR24); + if (target_surface == NULL) + { + DebugMessage(M64MSG_ERROR, "Failed to create target surface: %s\n", SDL_GetError()); + SDL_ReleaseCameraFrame(sdl->camera, frame_surface); + return M64ERR_SYSTEM_FAIL; + } + + int frame_size = SDL_min(frame_surface->w, frame_surface->h); + SDL_Rect frame_rect; + frame_rect.x = (frame_surface->w / 2) - (frame_size / 2); + frame_rect.y = 0; + frame_rect.w = frame_size; + frame_rect.h = frame_size; + + if (!SDL_BlitSurfaceScaled(frame_surface, &frame_rect, target_surface, NULL, SDL_SCALEMODE_NEAREST)) + { + DebugMessage(M64MSG_ERROR, "Failed to blit surface: %s", SDL_GetError()); + SDL_ReleaseCameraFrame(sdl->camera, frame_surface); + SDL_DestroySurface(target_surface); + return M64ERR_SYSTEM_FAIL; + } + + memcpy(data, target_surface->pixels, target_surface->w * target_surface->h * 3); + + SDL_ReleaseCameraFrame(sdl->camera, frame_surface); + SDL_DestroySurface(target_surface); + return M64ERR_SUCCESS; +} + + + +const struct video_capture_backend_interface g_isdl3_video_capture_backend = +{ + "sdl3", + sdl3_init, + sdl3_release, + sdl3_open, + sdl3_close, + sdl3_grab_image +}; + diff --git a/Source/3rdParty/mupen64plus-core/src/device/gb/gb_cart.c b/Source/3rdParty/mupen64plus-core/src/device/gb/gb_cart.c index b25992ad..5721e230 100644 --- a/Source/3rdParty/mupen64plus-core/src/device/gb/gb_cart.c +++ b/Source/3rdParty/mupen64plus-core/src/device/gb/gb_cart.c @@ -715,6 +715,9 @@ static void init_pocket_cam(struct pocket_cam* cam, uint8_t* ram, void* vcap, co cam->vcap = vcap; cam->ivcap = ivcap; + + /* open video device */ + cam->ivcap->open(cam->vcap, M64282FP_SENSOR_W, M64282FP_SENSOR_H); } static void poweron_pocket_cam(struct pocket_cam* cam) diff --git a/Source/3rdParty/mupen64plus-core/src/main/main.c b/Source/3rdParty/mupen64plus-core/src/main/main.c index f20ac265..73fbd3d3 100644 --- a/Source/3rdParty/mupen64plus-core/src/main/main.c +++ b/Source/3rdParty/mupen64plus-core/src/main/main.c @@ -1704,10 +1704,7 @@ m64p_error main_run(void) /* init GbCamera backend specified in the configuration file */ init_video_capture_backend(&igbcam_backend, &gbcam_backend, - g_CoreConfig, "GbCameraVideoCaptureBackend1"); - - /* open GB cam video device */ - igbcam_backend->open(gbcam_backend, M64282FP_SENSOR_W, M64282FP_SENSOR_H); + g_CoreConfig, "GbCameraVideoCaptureBackend1"); /* open storage files, provide default content if not present */ open_mpk_file(&mpk);