diff --git a/include/SDL3/SDL_camera.h b/include/SDL3/SDL_camera.h index 57c41a130..e4ac69a88 100644 --- a/include/SDL3/SDL_camera.h +++ b/include/SDL3/SDL_camera.h @@ -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. * diff --git a/src/camera/SDL_camera.c b/src/camera/SDL_camera.c index f70cd75f5..8710be864 100644 --- a/src/camera/SDL_camera.c +++ b/src/camera/SDL_camera.c @@ -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; diff --git a/src/camera/SDL_syscamera.h b/src/camera/SDL_syscamera.h index 82032a563..1ff527108 100644 --- a/src/camera/SDL_syscamera.h +++ b/src/camera/SDL_syscamera.h @@ -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; diff --git a/src/camera/android/SDL_camera_android.c b/src/camera/android/SDL_camera_android.c index 5da1cc0cb..621f7c7dc 100644 --- a/src/camera/android/SDL_camera_android.c +++ b/src/camera/android/SDL_camera_android.c @@ -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); - } } } } diff --git a/src/camera/coremedia/SDL_camera_coremedia.m b/src/camera/coremedia/SDL_camera_coremedia.m index 4d2e40f35..049c4a406 100644 --- a/src/camera/coremedia/SDL_camera_coremedia.m +++ b/src/camera/coremedia/SDL_camera_coremedia.m @@ -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); } diff --git a/src/camera/emscripten/SDL_camera_emscripten.c b/src/camera/emscripten/SDL_camera_emscripten.c index 45be94969..9a605a752 100644 --- a/src/camera/emscripten/SDL_camera_emscripten.c +++ b/src/camera/emscripten/SDL_camera_emscripten.c @@ -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); } } diff --git a/src/camera/mediafoundation/SDL_camera_mediafoundation.c b/src/camera/mediafoundation/SDL_camera_mediafoundation.c index d2d79f94c..dfbfc5799 100644 --- a/src/camera/mediafoundation/SDL_camera_mediafoundation.c +++ b/src/camera/mediafoundation/SDL_camera_mediafoundation.c @@ -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); diff --git a/src/camera/v4l2/SDL_camera_v4l2.c b/src/camera/v4l2/SDL_camera_v4l2.c index 0e2915910..4a94b2563 100644 --- a/src/camera/v4l2/SDL_camera_v4l2.c +++ b/src/camera/v4l2/SDL_camera_v4l2.c @@ -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. }