3rdParty: apply SDL3 gameboy camera backend patch to mupen64plus-core

This commit is contained in:
Rosalie Wanders
2025-09-24 23:26:16 +02:00
parent 51b712a8c9
commit e9dc24d947
6 changed files with 299 additions and 4 deletions
@@ -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)))
@@ -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 */
@@ -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
@@ -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 <SDL3/SDL.h>
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 <stdlib.h>
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
};
@@ -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)
+1 -4
View File
@@ -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);