camera: Added SDL_GetCameraDevicePosition.

Otherwise, as a property, you have to open each camera device to figure out
which ones are which.
main
Ryan C. Gordon 2024-02-19 14:19:57 -05:00
parent b1ed49772c
commit 70b89ab70d
8 changed files with 75 additions and 49 deletions

View File

@ -70,6 +70,19 @@ typedef struct SDL_CameraSpec
int interval_denominator; /**< Frame rate demoninator ((dom / num) == fps, (num / dom) == duration) */
} SDL_CameraSpec;
/**
* The position of camera in relation to system device.
*
* \sa SDL_GetCameraDevicePosition
*/
typedef enum SDL_CameraPosition
{
SDL_CAMERA_POSITION_UNKNOWN,
SDL_CAMERA_POSITION_FRONT_FACING,
SDL_CAMERA_POSITION_BACK_FACING
} SDL_CameraPosition;
/**
* Use this function to get the number of built-in camera drivers.
*
@ -210,6 +223,25 @@ extern DECLSPEC SDL_CameraSpec *SDLCALL SDL_GetCameraDeviceSupportedFormats(SDL_
*/
extern DECLSPEC char * SDLCALL SDL_GetCameraDeviceName(SDL_CameraDeviceID instance_id);
/**
* Get the position of the camera in relation to the system.
*
* Most platforms will report UNKNOWN, but mobile devices, like phones, can
* often make a distiction between cameras on the front of the device (that
* points towards the user, for taking "selfies") and cameras on the back
* (for filming in the direction the user is facing).
*
* \param instance_id the camera device instance ID
* \returns The position of the camera on the system hardware.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetCameraDevices
*/
extern DECLSPEC SDL_CameraPosition SDLCALL SDL_GetCameraDevicePosition(SDL_CameraDeviceID instance_id);
/**
* Open a video capture device (a "camera").
*
@ -305,16 +337,6 @@ extern DECLSPEC SDL_CameraDeviceID SDLCALL SDL_GetCameraInstanceID(SDL_Camera *c
/**
* Get the properties associated with an opened camera.
*
* The following read-only properties are provided by SDL:
*
* - `SDL_PROP_CAMERA_POSITION_STRING`: the position of the camera in
* relation to the hardware it is connected to. This is currently either
* the string "front" or "back", to signify which side of the user's
* device a camera is on. Future versions of SDL may add other position
* strings. This property is only set if this information can be
* determined by SDL. Most platforms do not set this attribute at all,
* but mobile devices tend to.
*
* \param camera the SDL_Camera obtained from SDL_OpenCameraDevice()
* \returns a valid property ID on success or 0 on failure; call
* SDL_GetError() for more information.
@ -328,8 +350,6 @@ extern DECLSPEC SDL_CameraDeviceID SDLCALL SDL_GetCameraInstanceID(SDL_Camera *c
*/
extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetCameraProperties(SDL_Camera *camera);
#define SDL_PROP_CAMERA_POSITION_STRING "SDL.camera.position"
/**
* Get the spec that a camera is using when generating images.
*

View File

@ -398,9 +398,8 @@ static int SDLCALL CameraSpecCmp(const void *vpa, const void *vpb)
return 0; // apparently, they're equal.
}
// The camera backends call this when a new device is plugged in.
SDL_CameraDevice *SDL_AddCameraDevice(const char *name, int num_specs, const SDL_CameraSpec *specs, void *handle)
SDL_CameraDevice *SDL_AddCameraDevice(const char *name, SDL_CameraPosition position, int num_specs, const SDL_CameraSpec *specs, void *handle)
{
SDL_assert(name != NULL);
SDL_assert(num_specs >= 0);
@ -425,6 +424,8 @@ SDL_CameraDevice *SDL_AddCameraDevice(const char *name, int num_specs, const SDL
return NULL;
}
device->position = position;
device->lock = SDL_CreateMutex();
if (!device->lock) {
SDL_free(device->name);
@ -457,7 +458,13 @@ SDL_CameraDevice *SDL_AddCameraDevice(const char *name, int num_specs, const SDL
}
#if DEBUG_CAMERA
SDL_Log("CAMERA: Adding device ('%s') with %d spec%s%s", name, num_specs, (num_specs == 1) ? "" : "s", (num_specs == 0) ? "" : ":");
const char *posstr = "unknown position";
if (position == SDL_CAMERA_POSITION_FRONT_FACING) {
posstr = "front-facing";
} else if (position == SDL_CAMERA_POSITION_BACK_FACING) {
posstr = "back-facing";
}
SDL_Log("CAMERA: Adding device '%s' (%s) with %d spec%s%s", name, posstr, num_specs, (num_specs == 1) ? "" : "s", (num_specs == 0) ? "" : ":");
for (int i = 0; i < num_specs; i++) {
const SDL_CameraSpec *spec = &device->all_specs[i];
SDL_Log("CAMERA: - fmt=%s, w=%d, h=%d, numerator=%d, denominator=%d", SDL_GetPixelFormatName(spec->format), spec->width, spec->height, spec->interval_numerator, spec->interval_denominator);
@ -658,6 +665,18 @@ char *SDL_GetCameraDeviceName(SDL_CameraDeviceID instance_id)
return retval;
}
SDL_CameraPosition SDL_GetCameraDevicePosition(SDL_CameraDeviceID instance_id)
{
SDL_CameraPosition retval = SDL_CAMERA_POSITION_UNKNOWN;
SDL_CameraDevice *device = ObtainPhysicalCameraDevice(instance_id);
if (device) {
retval = device->position;
ReleaseCameraDevice(device);
}
return retval;
}
SDL_CameraDeviceID *SDL_GetCameraDevices(int *count)
{
int dummy_count;

View File

@ -25,14 +25,14 @@
#include "../SDL_hashtable.h"
#define DEBUG_CAMERA 0
#define DEBUG_CAMERA 1
typedef struct SDL_CameraDevice SDL_CameraDevice;
/* Backends should call this as devices are added to the system (such as
a USB camera being plugged in), and should also be called for
for every device found during DetectDevices(). */
extern SDL_CameraDevice *SDL_AddCameraDevice(const char *name, int num_specs, const SDL_CameraSpec *specs, void *handle);
extern SDL_CameraDevice *SDL_AddCameraDevice(const char *name, SDL_CameraPosition position, int num_specs, const SDL_CameraSpec *specs, void *handle);
/* Backends should call this if an opened camera device is lost.
This can happen due to i/o errors, or a device being unplugged, etc. */
@ -82,6 +82,9 @@ struct SDL_CameraDevice
// Human-readable device name.
char *name;
// Position of camera (front-facing, back-facing, etc).
SDL_CameraPosition position;
// When refcount hits zero, we destroy the device object.
SDL_AtomicInt refcount;

View File

@ -575,7 +575,7 @@ static void ANDROIDCAMERA_FreeDeviceHandle(SDL_CameraDevice *device)
}
}
static void GatherCameraSpecs(const char *devid, CameraFormatAddData *add_data, char **fullname, const char **posstr)
static void GatherCameraSpecs(const char *devid, CameraFormatAddData *add_data, char **fullname, SDL_CameraPosition *position)
{
SDL_zerop(add_data);
@ -608,16 +608,15 @@ static void GatherCameraSpecs(const char *devid, CameraFormatAddData *add_data,
}
}
*posstr = NULL;
ACameraMetadata_const_entry posentry;
if (pACameraMetadata_getConstEntry(metadata, ACAMERA_LENS_FACING, &posentry) == ACAMERA_OK) { // ignore this if it fails.
if (*posentry.data.u8 == ACAMERA_LENS_FACING_FRONT) {
*posstr = "front";
*position = SDL_CAMERA_POSITION_FRONT_FACING;
if (!*fullname) {
*fullname = SDL_strdup("Front-facing camera");
}
} else if (*posentry.data.u8 == ACAMERA_LENS_FACING_BACK) {
*posstr = "back";
*position = SDL_CAMERA_POSITION_BACK_FACING;
if (!*fullname) {
*fullname = SDL_strdup("Back-facing camera");
}
@ -680,22 +679,16 @@ static void MaybeAddDevice(const char *devid)
return; // already have this one.
}
const char *posstr = NULL;
SDL_CameraPosition position = SDL_CAMERA_POSITION_UNKNOWN;
char *fullname = NULL;
CameraFormatAddData add_data;
GatherCameraSpecs(devid, &add_data, &fullname, &posstr);
GatherCameraSpecs(devid, &add_data, &fullname, &position);
if (add_data.num_specs > 0) {
char *namecpy = SDL_strdup(devid);
if (namecpy) {
SDL_CameraDevice *device = SDL_AddCameraDevice(fullname, add_data.num_specs, add_data.specs, namecpy);
SDL_CameraDevice *device = SDL_AddCameraDevice(fullname, position, add_data.num_specs, add_data.specs, namecpy);
if (!device) {
SDL_free(namecpy);
} else if (device && posstr) {
SDL_Camera *camera = (SDL_Camera *) device; // currently there's no separation between physical and logical device.
SDL_PropertiesID props = SDL_GetCameraProperties(camera);
if (props) {
SDL_SetStringProperty(props, SDL_PROP_CAMERA_POSITION_STRING, posstr);
}
}
}
}

View File

@ -399,24 +399,15 @@ static void MaybeAddDevice(AVCaptureDevice *avdevice)
CameraFormatAddData add_data;
GatherCameraSpecs(avdevice, &add_data);
if (add_data.num_specs > 0) {
SDL_CameraDevice *device = SDL_AddCameraDevice(avdevice.localizedName.UTF8String, add_data.num_specs, add_data.specs, (void *) CFBridgingRetain(avdevice));
if (device) {
const char *posstr = NULL;
if (avdevice.position == AVCaptureDevicePositionFront) {
posstr = "front";
} else if (avdevice.position == AVCaptureDevicePositionBack) {
posstr = "back";
}
if (posstr) {
SDL_Camera *camera = (SDL_Camera *) device; // currently there's no separation between physical and logical device.
SDL_PropertiesID props = SDL_GetCameraProperties(camera);
if (props) {
SDL_SetStringProperty(props, SDL_PROP_CAMERA_POSITION_STRING, posstr);
}
}
SDL_CameraPosition position = SDL_CAMERA_POSITION_UNKNOWN;
if (avdevice.position == AVCaptureDevicePositionFront) {
position = SDL_CAMERA_POSITION_FRONT_FACING;
} else if (avdevice.position == AVCaptureDevicePositionBack) {
position = SDL_CAMERA_POSITION_BACK_FACING;
}
SDL_AddCameraDevice(avdevice.localizedName.UTF8String, position, add_data.num_specs, add_data.specs, (void *) CFBridgingRetain(avdevice));
}
SDL_free(add_data.specs);
}

View File

@ -228,7 +228,7 @@ static void EMSCRIPTENCAMERA_DetectDevices(void)
// will pop up a user permission dialog warning them we're trying to access the camera, and we generally
// don't want that during SDL_Init().
if (supported) {
SDL_AddCameraDevice("Web browser's camera", 0, NULL, (void *) (size_t) 0x1);
SDL_AddCameraDevice("Web browser's camera", SDL_CAMERA_POSITION_UNKNOWN, 0, NULL, (void *) (size_t) 0x1);
}
}

View File

@ -777,7 +777,7 @@ static void MaybeAddDevice(IMFActivate *activation)
CameraFormatAddData add_data;
GatherCameraSpecs(source, &add_data);
if (add_data.num_specs > 0) {
SDL_AddCameraDevice(name, add_data.num_specs, add_data.specs, symlink);
SDL_AddCameraDevice(name, SDL_CAMERA_POSITION_UNKNOWN, add_data.num_specs, add_data.specs, symlink);
}
SDL_free(add_data.specs);
IMFActivate_ShutdownObject(activation);

View File

@ -778,7 +778,7 @@ static void MaybeAddDevice(const char *path)
if (handle->path) {
handle->bus_info = SDL_strdup((char *)vcap.bus_info);
if (handle->bus_info) {
if (SDL_AddCameraDevice((const char *) vcap.card, add_data.num_specs, add_data.specs, handle)) {
if (SDL_AddCameraDevice((const char *) vcap.card, SDL_CAMERA_POSITION_UNKNOWN, add_data.num_specs, add_data.specs, handle)) {
SDL_free(add_data.specs);
return; // good to go.
}