Atlas - SDL_camera_android.c
Home / ext / SDL / src / camera / android Lines: 1 | Size: 39932 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22 23#include "../SDL_syscamera.h" 24#include "../SDL_camera_c.h" 25#include "../../video/SDL_pixels_c.h" 26#include "../../video/SDL_surface_c.h" 27#include "../../thread/SDL_systhread.h" 28 29#ifdef SDL_CAMERA_DRIVER_ANDROID 30 31/* 32 * AndroidManifest.xml: 33 * <uses-permission android:name="android.permission.CAMERA"></uses-permission> 34 * <uses-feature android:name="android.hardware.camera" /> 35 * 36 * Very likely SDL must be build with YUV support (done by default) 37 * 38 * https://developer.android.com/reference/android/hardware/camera2/CameraManager 39 * "All camera devices intended to be operated concurrently, must be opened using openCamera(String, CameraDevice.StateCallback, Handler), 40 * before configuring sessions on any of the camera devices." 41 */ 42 43// this is kinda gross, but on older NDK headers all the camera stuff is 44// gated behind __ANDROID_API__. We'll dlopen() it at runtime, so we'll do 45// the right thing on pre-Android 7.0 devices, but we still 46// need the struct declarations and such in those headers. 47// The other option is to make a massive jump in minimum Android version we 48// support--going from ancient to merely really old--but this seems less 49// distasteful and using dlopen matches practices on other SDL platforms. 50// We'll see if it works out. 51#if __ANDROID_API__ < 24 52#undef __ANDROID_API__ 53#define __ANDROID_API__ 24 54#endif 55 56#include <dlfcn.h> 57#include <camera/NdkCameraDevice.h> 58#include <camera/NdkCameraManager.h> 59#include <media/NdkImage.h> 60#include <media/NdkImageReader.h> 61 62#include "../../core/android/SDL_android.h" 63 64static void *libcamera2ndk = NULL; 65typedef ACameraManager* (*pfnACameraManager_create)(void); 66typedef camera_status_t (*pfnACameraManager_registerAvailabilityCallback)(ACameraManager*, const ACameraManager_AvailabilityCallbacks*); 67typedef camera_status_t (*pfnACameraManager_unregisterAvailabilityCallback)(ACameraManager*, const ACameraManager_AvailabilityCallbacks*); 68typedef camera_status_t (*pfnACameraManager_getCameraIdList)(ACameraManager*, ACameraIdList**); 69typedef void (*pfnACameraManager_deleteCameraIdList)(ACameraIdList*); 70typedef void (*pfnACameraCaptureSession_close)(ACameraCaptureSession*); 71typedef void (*pfnACaptureRequest_free)(ACaptureRequest*); 72typedef void (*pfnACameraOutputTarget_free)(ACameraOutputTarget*); 73typedef camera_status_t (*pfnACameraDevice_close)(ACameraDevice*); 74typedef void (*pfnACameraManager_delete)(ACameraManager*); 75typedef void (*pfnACaptureSessionOutputContainer_free)(ACaptureSessionOutputContainer*); 76typedef void (*pfnACaptureSessionOutput_free)(ACaptureSessionOutput*); 77typedef camera_status_t (*pfnACameraManager_openCamera)(ACameraManager*, const char*, ACameraDevice_StateCallbacks*, ACameraDevice**); 78typedef camera_status_t (*pfnACameraDevice_createCaptureRequest)(const ACameraDevice*, ACameraDevice_request_template, ACaptureRequest**); 79typedef camera_status_t (*pfnACameraDevice_createCaptureSession)(ACameraDevice*, const ACaptureSessionOutputContainer*, const ACameraCaptureSession_stateCallbacks*,ACameraCaptureSession**); 80typedef camera_status_t (*pfnACameraManager_getCameraCharacteristics)(ACameraManager*, const char*, ACameraMetadata**); 81typedef void (*pfnACameraMetadata_free)(ACameraMetadata*); 82typedef camera_status_t (*pfnACameraMetadata_getConstEntry)(const ACameraMetadata*, uint32_t tag, ACameraMetadata_const_entry*); 83typedef camera_status_t (*pfnACameraCaptureSession_setRepeatingRequest)(ACameraCaptureSession*, ACameraCaptureSession_captureCallbacks*, int numRequests, ACaptureRequest**, int*); 84typedef camera_status_t (*pfnACameraOutputTarget_create)(ACameraWindowType*,ACameraOutputTarget**); 85typedef camera_status_t (*pfnACaptureRequest_addTarget)(ACaptureRequest*, const ACameraOutputTarget*); 86typedef camera_status_t (*pfnACaptureSessionOutputContainer_add)(ACaptureSessionOutputContainer*, const ACaptureSessionOutput*); 87typedef camera_status_t (*pfnACaptureSessionOutputContainer_create)(ACaptureSessionOutputContainer**); 88typedef camera_status_t (*pfnACaptureSessionOutput_create)(ACameraWindowType*, ACaptureSessionOutput**); 89static pfnACameraManager_create pACameraManager_create = NULL; 90static pfnACameraManager_registerAvailabilityCallback pACameraManager_registerAvailabilityCallback = NULL; 91static pfnACameraManager_unregisterAvailabilityCallback pACameraManager_unregisterAvailabilityCallback = NULL; 92static pfnACameraManager_getCameraIdList pACameraManager_getCameraIdList = NULL; 93static pfnACameraManager_deleteCameraIdList pACameraManager_deleteCameraIdList = NULL; 94static pfnACameraCaptureSession_close pACameraCaptureSession_close = NULL; 95static pfnACaptureRequest_free pACaptureRequest_free = NULL; 96static pfnACameraOutputTarget_free pACameraOutputTarget_free = NULL; 97static pfnACameraDevice_close pACameraDevice_close = NULL; 98static pfnACameraManager_delete pACameraManager_delete = NULL; 99static pfnACaptureSessionOutputContainer_free pACaptureSessionOutputContainer_free = NULL; 100static pfnACaptureSessionOutput_free pACaptureSessionOutput_free = NULL; 101static pfnACameraManager_openCamera pACameraManager_openCamera = NULL; 102static pfnACameraDevice_createCaptureRequest pACameraDevice_createCaptureRequest = NULL; 103static pfnACameraDevice_createCaptureSession pACameraDevice_createCaptureSession = NULL; 104static pfnACameraManager_getCameraCharacteristics pACameraManager_getCameraCharacteristics = NULL; 105static pfnACameraMetadata_free pACameraMetadata_free = NULL; 106static pfnACameraMetadata_getConstEntry pACameraMetadata_getConstEntry = NULL; 107static pfnACameraCaptureSession_setRepeatingRequest pACameraCaptureSession_setRepeatingRequest = NULL; 108static pfnACameraOutputTarget_create pACameraOutputTarget_create = NULL; 109static pfnACaptureRequest_addTarget pACaptureRequest_addTarget = NULL; 110static pfnACaptureSessionOutputContainer_add pACaptureSessionOutputContainer_add = NULL; 111static pfnACaptureSessionOutputContainer_create pACaptureSessionOutputContainer_create = NULL; 112static pfnACaptureSessionOutput_create pACaptureSessionOutput_create = NULL; 113 114static void *libmediandk = NULL; 115typedef void (*pfnAImage_delete)(AImage*); 116typedef media_status_t (*pfnAImage_getTimestamp)(const AImage*, int64_t*); 117typedef media_status_t (*pfnAImage_getNumberOfPlanes)(const AImage*, int32_t*); 118typedef media_status_t (*pfnAImage_getPlaneRowStride)(const AImage*, int, int32_t*); 119typedef media_status_t (*pfnAImage_getPlaneData)(const AImage*, int, uint8_t**, int*); 120typedef media_status_t (*pfnAImageReader_acquireNextImage)(AImageReader*, AImage**); 121typedef void (*pfnAImageReader_delete)(AImageReader*); 122typedef media_status_t (*pfnAImageReader_setImageListener)(AImageReader*, AImageReader_ImageListener*); 123typedef media_status_t (*pfnAImageReader_getWindow)(AImageReader*, ANativeWindow**); 124typedef media_status_t (*pfnAImageReader_new)(int32_t, int32_t, int32_t, int32_t, AImageReader**); 125static pfnAImage_delete pAImage_delete = NULL; 126static pfnAImage_getTimestamp pAImage_getTimestamp = NULL; 127static pfnAImage_getNumberOfPlanes pAImage_getNumberOfPlanes = NULL; 128static pfnAImage_getPlaneRowStride pAImage_getPlaneRowStride = NULL; 129static pfnAImage_getPlaneData pAImage_getPlaneData = NULL; 130static pfnAImageReader_acquireNextImage pAImageReader_acquireNextImage = NULL; 131static pfnAImageReader_delete pAImageReader_delete = NULL; 132static pfnAImageReader_setImageListener pAImageReader_setImageListener = NULL; 133static pfnAImageReader_getWindow pAImageReader_getWindow = NULL; 134static pfnAImageReader_new pAImageReader_new = NULL; 135 136typedef media_status_t (*pfnAImage_getWidth)(const AImage*, int32_t*); 137typedef media_status_t (*pfnAImage_getHeight)(const AImage*, int32_t*); 138static pfnAImage_getWidth pAImage_getWidth = NULL; 139static pfnAImage_getHeight pAImage_getHeight = NULL; 140 141struct SDL_PrivateCameraData 142{ 143 ACameraDevice *device; 144 AImageReader *reader; 145 ANativeWindow *window; 146 ACaptureSessionOutput *sessionOutput; 147 ACaptureSessionOutputContainer *sessionOutputContainer; 148 ACameraOutputTarget *outputTarget; 149 ACaptureRequest *request; 150 ACameraCaptureSession *session; 151 SDL_CameraSpec requested_spec; 152 int rotation; // degrees to rotate clockwise to get from camera's static orientation to device's native orientation. Apply this plus current phone rotation to get upright image! 153}; 154 155static bool SetErrorStr(const char *what, const char *errstr, const int rc) 156{ 157 char errbuf[128]; 158 if (!errstr) { 159 SDL_snprintf(errbuf, sizeof (errbuf), "Unknown error #%d", rc); 160 errstr = errbuf; 161 } 162 return SDL_SetError("%s: %s", what, errstr); 163} 164 165static const char *CameraStatusStr(const camera_status_t rc) 166{ 167 switch (rc) { 168 case ACAMERA_OK: return "no error"; 169 case ACAMERA_ERROR_UNKNOWN: return "unknown error"; 170 case ACAMERA_ERROR_INVALID_PARAMETER: return "invalid parameter"; 171 case ACAMERA_ERROR_CAMERA_DISCONNECTED: return "camera disconnected"; 172 case ACAMERA_ERROR_NOT_ENOUGH_MEMORY: return "not enough memory"; 173 case ACAMERA_ERROR_METADATA_NOT_FOUND: return "metadata not found"; 174 case ACAMERA_ERROR_CAMERA_DEVICE: return "camera device error"; 175 case ACAMERA_ERROR_CAMERA_SERVICE: return "camera service error"; 176 case ACAMERA_ERROR_SESSION_CLOSED: return "session closed"; 177 case ACAMERA_ERROR_INVALID_OPERATION: return "invalid operation"; 178 case ACAMERA_ERROR_STREAM_CONFIGURE_FAIL: return "configure failure"; 179 case ACAMERA_ERROR_CAMERA_IN_USE: return "camera in use"; 180 case ACAMERA_ERROR_MAX_CAMERA_IN_USE: return "max cameras in use"; 181 case ACAMERA_ERROR_CAMERA_DISABLED: return "camera disabled"; 182 case ACAMERA_ERROR_PERMISSION_DENIED: return "permission denied"; 183 case ACAMERA_ERROR_UNSUPPORTED_OPERATION: return "unsupported operation"; 184 default: break; 185 } 186 187 return NULL; // unknown error 188} 189 190static bool SetCameraError(const char *what, const camera_status_t rc) 191{ 192 return SetErrorStr(what, CameraStatusStr(rc), (int) rc); 193} 194 195static const char *MediaStatusStr(const media_status_t rc) 196{ 197 switch (rc) { 198 case AMEDIA_OK: return "no error"; 199 case AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE: return "insufficient resources"; 200 case AMEDIACODEC_ERROR_RECLAIMED: return "reclaimed"; 201 case AMEDIA_ERROR_UNKNOWN: return "unknown error"; 202 case AMEDIA_ERROR_MALFORMED: return "malformed"; 203 case AMEDIA_ERROR_UNSUPPORTED: return "unsupported"; 204 case AMEDIA_ERROR_INVALID_OBJECT: return "invalid object"; 205 case AMEDIA_ERROR_INVALID_PARAMETER: return "invalid parameter"; 206 case AMEDIA_ERROR_INVALID_OPERATION: return "invalid operation"; 207 case AMEDIA_ERROR_END_OF_STREAM: return "end of stream"; 208 case AMEDIA_ERROR_IO: return "i/o error"; 209 case AMEDIA_ERROR_WOULD_BLOCK: return "operation would block"; 210 case AMEDIA_DRM_NOT_PROVISIONED: return "DRM not provisioned"; 211 case AMEDIA_DRM_RESOURCE_BUSY: return "DRM resource busy"; 212 case AMEDIA_DRM_DEVICE_REVOKED: return "DRM device revoked"; 213 case AMEDIA_DRM_SHORT_BUFFER: return "DRM short buffer"; 214 case AMEDIA_DRM_SESSION_NOT_OPENED: return "DRM session not opened"; 215 case AMEDIA_DRM_TAMPER_DETECTED: return "DRM tampering detected"; 216 case AMEDIA_DRM_VERIFY_FAILED: return "DRM verify failed"; 217 case AMEDIA_DRM_NEED_KEY: return "DRM need key"; 218 case AMEDIA_DRM_LICENSE_EXPIRED: return "DRM license expired"; 219 case AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE: return "no buffer available"; 220 case AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED: return "maximum images acquired"; 221 case AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE: return "cannot lock image"; 222 case AMEDIA_IMGREADER_CANNOT_UNLOCK_IMAGE: return "cannot unlock image"; 223 case AMEDIA_IMGREADER_IMAGE_NOT_LOCKED: return "image not locked"; 224 default: break; 225 } 226 227 return NULL; // unknown error 228} 229 230static bool SetMediaError(const char *what, const media_status_t rc) 231{ 232 return SetErrorStr(what, MediaStatusStr(rc), (int) rc); 233} 234 235 236static ACameraManager *cameraMgr = NULL; 237 238static bool CreateCameraManager(void) 239{ 240 SDL_assert(cameraMgr == NULL); 241 242 cameraMgr = pACameraManager_create(); 243 if (!cameraMgr) { 244 return SDL_SetError("Error creating ACameraManager"); 245 } 246 return true; 247} 248 249static void DestroyCameraManager(void) 250{ 251 if (cameraMgr) { 252 pACameraManager_delete(cameraMgr); 253 cameraMgr = NULL; 254 } 255} 256 257static void format_android_to_sdl(Uint32 fmt, SDL_PixelFormat *format, SDL_Colorspace *colorspace) 258{ 259 switch (fmt) { 260 #define CASE(x, y, z) case x: *format = y; *colorspace = z; return 261 CASE(AIMAGE_FORMAT_YUV_420_888, SDL_PIXELFORMAT_NV12, SDL_COLORSPACE_BT709_LIMITED); 262 CASE(AIMAGE_FORMAT_RGB_565, SDL_PIXELFORMAT_RGB565, SDL_COLORSPACE_SRGB); 263 CASE(AIMAGE_FORMAT_RGB_888, SDL_PIXELFORMAT_XRGB8888, SDL_COLORSPACE_SRGB); 264 CASE(AIMAGE_FORMAT_RGBA_8888, SDL_PIXELFORMAT_RGBA8888, SDL_COLORSPACE_SRGB); 265 CASE(AIMAGE_FORMAT_RGBX_8888, SDL_PIXELFORMAT_RGBX8888, SDL_COLORSPACE_SRGB); 266 CASE(AIMAGE_FORMAT_RGBA_FP16, SDL_PIXELFORMAT_RGBA64_FLOAT, SDL_COLORSPACE_SRGB); 267 #undef CASE 268 default: break; 269 } 270 271 #if DEBUG_CAMERA 272 //SDL_Log("Unknown format AIMAGE_FORMAT '%d'", fmt); 273 #endif 274 275 *format = SDL_PIXELFORMAT_UNKNOWN; 276 *colorspace = SDL_COLORSPACE_UNKNOWN; 277} 278 279static Uint32 format_sdl_to_android(SDL_PixelFormat fmt) 280{ 281 switch (fmt) { 282 #define CASE(x, y) case y: return x 283 CASE(AIMAGE_FORMAT_YUV_420_888, SDL_PIXELFORMAT_NV12); 284 CASE(AIMAGE_FORMAT_RGB_565, SDL_PIXELFORMAT_RGB565); 285 CASE(AIMAGE_FORMAT_RGB_888, SDL_PIXELFORMAT_XRGB8888); 286 CASE(AIMAGE_FORMAT_RGBA_8888, SDL_PIXELFORMAT_RGBA8888); 287 CASE(AIMAGE_FORMAT_RGBX_8888, SDL_PIXELFORMAT_RGBX8888); 288 #undef CASE 289 default: 290 return 0; 291 } 292} 293 294static bool ANDROIDCAMERA_WaitDevice(SDL_Camera *device) 295{ 296 return true; // this isn't used atm, since we run our own thread via onImageAvailable callbacks. 297} 298 299static SDL_CameraFrameResult ANDROIDCAMERA_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, float *rotation) 300{ 301 SDL_CameraFrameResult result = SDL_CAMERA_FRAME_READY; 302 media_status_t res; 303 AImage *image = NULL; 304 305 res = pAImageReader_acquireNextImage(device->hidden->reader, &image); 306 // We could also use this one: 307 //res = AImageReader_acquireLatestImage(device->hidden->reader, &image); 308 309 SDL_assert(res != AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE); // we should only be here if onImageAvailable was called. 310 311 if (res != AMEDIA_OK) { 312 SetMediaError("Error AImageReader_acquireNextImage", res); 313 return SDL_CAMERA_FRAME_ERROR; 314 } 315 316 int64_t atimestamp = 0; 317 if (pAImage_getTimestamp(image, &atimestamp) == AMEDIA_OK) { 318 *timestampNS = (Uint64) atimestamp; 319 } else { 320 *timestampNS = 0; 321 } 322 323 // !!! FIXME: this currently copies the data to the surface (see FIXME about non-contiguous planar surfaces, but in theory we could just keep this locked until ReleaseFrame... 324 int32_t num_planes = 0; 325 pAImage_getNumberOfPlanes(image, &num_planes); 326 327 if ((num_planes == 3) && (device->spec.format == SDL_PIXELFORMAT_NV12)) { 328 num_planes--; // treat the interleaved planes as one. 329 } 330 331 size_t buflen = 0; 332 pAImage_getPlaneRowStride(image, 0, &frame->pitch); 333 for (int i = 0; (i < num_planes) && (i < 3); i++) { 334 int32_t expected; 335 if (i == 0) { 336 expected = frame->pitch * frame->h; 337 } else { 338 expected = frame->pitch * (frame->h + 1) / 2; 339 } 340 buflen += expected; 341 } 342 343 frame->pixels = SDL_aligned_alloc(SDL_GetSIMDAlignment(), buflen); 344 if (frame->pixels == NULL) { 345 result = SDL_CAMERA_FRAME_ERROR; 346 } else { 347 Uint8 *dst = frame->pixels; 348 349 for (int i = 0; (i < num_planes) && (i < 3); i++) { 350 uint8_t *data = NULL; 351 int32_t datalen = 0; 352 int32_t expected; 353 if (i == 0) { 354 expected = frame->pitch * frame->h; 355 } else { 356 expected = frame->pitch * (frame->h + 1) / 2; 357 } 358 pAImage_getPlaneData(image, i, &data, &datalen); 359 360 int32_t row_stride = 0; 361 pAImage_getPlaneRowStride(image, i, &row_stride); 362 SDL_assert(row_stride == frame->pitch); 363 SDL_memcpy(dst, data, SDL_min(expected, datalen)); 364 dst += expected; 365 } 366 } 367 368 pAImage_delete(image); 369 370 int dev_rotation = 0; 371 switch (Android_JNI_GetDisplayCurrentOrientation()) { 372 case SDL_ORIENTATION_PORTRAIT: dev_rotation = 0; break; 373 case SDL_ORIENTATION_LANDSCAPE: dev_rotation = 90; break; 374 case SDL_ORIENTATION_PORTRAIT_FLIPPED: dev_rotation = 180; break; 375 case SDL_ORIENTATION_LANDSCAPE_FLIPPED: dev_rotation = 270; break; 376 default: SDL_assert(!"Unexpected device rotation!"); dev_rotation = 0; break; 377 } 378 379 if (device->position == SDL_CAMERA_POSITION_BACK_FACING) { 380 dev_rotation = -dev_rotation; // we want to subtract this value, instead of add, if back-facing. 381 } 382 383 *rotation = (float) (dev_rotation + device->hidden->rotation); // current phone orientation, static camera orientation in relation to phone. 384 385 return result; 386} 387 388static void ANDROIDCAMERA_ReleaseFrame(SDL_Camera *device, SDL_Surface *frame) 389{ 390 // !!! FIXME: this currently copies the data to the surface, but in theory we could just keep the AImage until ReleaseFrame... 391 SDL_aligned_free(frame->pixels); 392} 393 394static void onImageAvailable(void *context, AImageReader *reader) 395{ 396 #if DEBUG_CAMERA 397 SDL_Log("CAMERA: CB onImageAvailable"); 398 #endif 399 SDL_Camera *device = (SDL_Camera *) context; 400 SDL_CameraThreadIterate(device); 401} 402 403static void onDisconnected(void *context, ACameraDevice *device) 404{ 405 #if DEBUG_CAMERA 406 SDL_Log("CAMERA: CB onDisconnected"); 407 #endif 408 SDL_CameraDisconnected((SDL_Camera *) context); 409} 410 411static void onError(void *context, ACameraDevice *device, int error) 412{ 413 #if DEBUG_CAMERA 414 SDL_Log("CAMERA: CB onError"); 415 #endif 416 SDL_CameraDisconnected((SDL_Camera *) context); 417} 418 419static void onClosed(void* context, ACameraCaptureSession *session) 420{ 421 // SDL_Camera *_this = (SDL_Camera *) context; 422 #if DEBUG_CAMERA 423 SDL_Log("CAMERA: CB onClosed"); 424 #endif 425} 426 427static void onReady(void* context, ACameraCaptureSession *session) 428{ 429 // SDL_Camera *_this = (SDL_Camera *) context; 430 #if DEBUG_CAMERA 431 SDL_Log("CAMERA: CB onReady"); 432 #endif 433} 434 435static void onActive(void* context, ACameraCaptureSession *session) 436{ 437 // SDL_Camera *_this = (SDL_Camera *) context; 438 #if DEBUG_CAMERA 439 SDL_Log("CAMERA: CB onActive"); 440 #endif 441} 442 443static void ANDROIDCAMERA_CloseDevice(SDL_Camera *device) 444{ 445 if (device && device->hidden) { 446 struct SDL_PrivateCameraData *hidden = device->hidden; 447 device->hidden = NULL; 448 449 if (hidden->reader) { 450 pAImageReader_setImageListener(hidden->reader, NULL); 451 } 452 453 if (hidden->session) { 454 pACameraCaptureSession_close(hidden->session); 455 } 456 457 if (hidden->request) { 458 pACaptureRequest_free(hidden->request); 459 } 460 461 if (hidden->outputTarget) { 462 pACameraOutputTarget_free(hidden->outputTarget); 463 } 464 465 if (hidden->sessionOutputContainer) { 466 pACaptureSessionOutputContainer_free(hidden->sessionOutputContainer); 467 } 468 469 if (hidden->sessionOutput) { 470 pACaptureSessionOutput_free(hidden->sessionOutput); 471 } 472 473 // we don't free hidden->window here, it'll be cleaned up by AImageReader_delete. 474 475 if (hidden->reader) { 476 pAImageReader_delete(hidden->reader); 477 } 478 479 if (hidden->device) { 480 pACameraDevice_close(hidden->device); 481 } 482 483 SDL_free(hidden); 484 } 485} 486 487// this is where the "opening" of the camera happens, after permission is granted. 488static bool PrepareCamera(SDL_Camera *device) 489{ 490 SDL_assert(device->hidden != NULL); 491 492 camera_status_t res; 493 media_status_t res2; 494 495 ACameraDevice_StateCallbacks dev_callbacks; 496 SDL_zero(dev_callbacks); 497 dev_callbacks.context = device; 498 dev_callbacks.onDisconnected = onDisconnected; 499 dev_callbacks.onError = onError; 500 501 ACameraCaptureSession_stateCallbacks capture_callbacks; 502 SDL_zero(capture_callbacks); 503 capture_callbacks.context = device; 504 capture_callbacks.onClosed = onClosed; 505 capture_callbacks.onReady = onReady; 506 capture_callbacks.onActive = onActive; 507 508 AImageReader_ImageListener imglistener; 509 SDL_zero(imglistener); 510 imglistener.context = device; 511 imglistener.onImageAvailable = onImageAvailable; 512 513 514 const char *devid = (const char *) device->handle; 515 516 device->hidden->rotation = 0; 517 ACameraMetadata *metadata = NULL; 518 ACameraMetadata_const_entry orientationentry; 519 if (pACameraManager_getCameraCharacteristics(cameraMgr, devid, &metadata) == ACAMERA_OK) { 520 if (pACameraMetadata_getConstEntry(metadata, ACAMERA_SENSOR_ORIENTATION, &orientationentry) == ACAMERA_OK) { 521 device->hidden->rotation = (int) (*orientationentry.data.i32 % 360); 522 } 523 pACameraMetadata_free(metadata); 524 } 525 526 // just in case SDL_OpenCamera is overwriting device->spec as CameraPermissionCallback runs, we work from a different copy. 527 const SDL_CameraSpec *spec = &device->hidden->requested_spec; 528 529 if ((res = pACameraManager_openCamera(cameraMgr, devid, &dev_callbacks, &device->hidden->device)) != ACAMERA_OK) { 530 return SetCameraError("Failed to open camera", res); 531 } else if ((res2 = pAImageReader_new(spec->width, spec->height, format_sdl_to_android(spec->format), 10 /* nb buffers */, &device->hidden->reader)) != AMEDIA_OK) { 532 return SetMediaError("Error AImageReader_new", res2); 533 } else if ((res2 = pAImageReader_getWindow(device->hidden->reader, &device->hidden->window)) != AMEDIA_OK) { 534 return SetMediaError("Error AImageReader_getWindow", res2); 535 } else if ((res = pACaptureSessionOutput_create(device->hidden->window, &device->hidden->sessionOutput)) != ACAMERA_OK) { 536 return SetCameraError("Error ACaptureSessionOutput_create", res); 537 } else if ((res = pACaptureSessionOutputContainer_create(&device->hidden->sessionOutputContainer)) != ACAMERA_OK) { 538 return SetCameraError("Error ACaptureSessionOutputContainer_create", res); 539 } else if ((res = pACaptureSessionOutputContainer_add(device->hidden->sessionOutputContainer, device->hidden->sessionOutput)) != ACAMERA_OK) { 540 return SetCameraError("Error ACaptureSessionOutputContainer_add", res); 541 } else if ((res = pACameraOutputTarget_create(device->hidden->window, &device->hidden->outputTarget)) != ACAMERA_OK) { 542 return SetCameraError("Error ACameraOutputTarget_create", res); 543 } else if ((res = pACameraDevice_createCaptureRequest(device->hidden->device, TEMPLATE_RECORD, &device->hidden->request)) != ACAMERA_OK) { 544 return SetCameraError("Error ACameraDevice_createCaptureRequest", res); 545 } else if ((res = pACaptureRequest_addTarget(device->hidden->request, device->hidden->outputTarget)) != ACAMERA_OK) { 546 return SetCameraError("Error ACaptureRequest_addTarget", res); 547 } else if ((res = pACameraDevice_createCaptureSession(device->hidden->device, device->hidden->sessionOutputContainer, &capture_callbacks, &device->hidden->session)) != ACAMERA_OK) { 548 return SetCameraError("Error ACameraDevice_createCaptureSession", res); 549 } else if ((res = pACameraCaptureSession_setRepeatingRequest(device->hidden->session, NULL, 1, &device->hidden->request, NULL)) != ACAMERA_OK) { 550 return SetCameraError("Error ACameraCaptureSession_setRepeatingRequest", res); 551 } else if ((res2 = pAImageReader_setImageListener(device->hidden->reader, &imglistener)) != AMEDIA_OK) { 552 return SetMediaError("Error AImageReader_setImageListener", res2); 553 } 554 555 return true; 556} 557 558static void SDLCALL CameraPermissionCallback(void *userdata, const char *permission, bool granted) 559{ 560 SDL_Camera *device = (SDL_Camera *) userdata; 561 if (device->hidden != NULL) { // if device was already closed, don't send an event. 562 if (!granted) { 563 SDL_CameraPermissionOutcome(device, false); // sorry, permission denied. 564 } else if (!PrepareCamera(device)) { // permission given? Actually open the camera now. 565 // uhoh, setup failed; since the app thinks we already "opened" the device, mark it as disconnected and don't report the permission. 566 SDL_CameraDisconnected(device); 567 } else { 568 // okay! We have permission to use the camera _and_ opening the hardware worked out, report that the camera is usable! 569 SDL_CameraPermissionOutcome(device, true); // go go go! 570 } 571 } 572 573 UnrefPhysicalCamera(device); // we ref'd this in OpenDevice, release the extra reference. 574} 575 576 577static bool ANDROIDCAMERA_OpenDevice(SDL_Camera *device, const SDL_CameraSpec *spec) 578{ 579#if 0 // !!! FIXME: for now, we'll just let this fail if it is going to fail, without checking for this 580 /* Cannot open a second camera, while the first one is opened. 581 * If you want to play several camera, they must all be opened first, then played. 582 * 583 * https://developer.android.com/reference/android/hardware/camera2/CameraManager 584 * "All camera devices intended to be operated concurrently, must be opened using openCamera(String, CameraDevice.StateCallback, Handler), 585 * before configuring sessions on any of the camera devices. * " 586 * 587 */ 588 if (CheckDevicePlaying()) { 589 return SDL_SetError("A camera is already playing"); 590 } 591#endif 592 593 device->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData)); 594 if (device->hidden == NULL) { 595 return false; 596 } 597 598 RefPhysicalCamera(device); // ref'd until permission callback fires. 599 600 // just in case SDL_OpenCamera is overwriting device->spec as CameraPermissionCallback runs, we work from a different copy. 601 SDL_copyp(&device->hidden->requested_spec, spec); 602 if (!SDL_RequestAndroidPermission("android.permission.CAMERA", CameraPermissionCallback, device)) { 603 UnrefPhysicalCamera(device); 604 return false; 605 } 606 607 return true; // we don't open the camera until permission is granted, so always succeed for now. 608} 609 610static void ANDROIDCAMERA_FreeDeviceHandle(SDL_Camera *device) 611{ 612 if (device) { 613 SDL_free(device->handle); 614 } 615} 616 617static void GatherCameraSpecs(const char *devid, CameraFormatAddData *add_data, char **fullname, SDL_CameraPosition *position) 618{ 619 SDL_zerop(add_data); 620 621 ACameraMetadata *metadata = NULL; 622 ACameraMetadata_const_entry cfgentry; 623 ACameraMetadata_const_entry durentry; 624 ACameraMetadata_const_entry infoentry; 625 626 // This can fail with an "unknown error" (with `adb logcat` reporting "no such file or directory") 627 // for "LEGACY" level cameras. I saw this happen on a 30-dollar budget phone I have for testing 628 // (but a different brand budget phone worked, so it's not strictly the low-end of Android devices). 629 // LEGACY devices are seen by onCameraAvailable, but are not otherwise accessible through 630 // libcamera2ndk. The Java camera2 API apparently _can_ access these cameras, but we're going on 631 // without them here for now, in hopes that such hardware is a dying breed. 632 if (pACameraManager_getCameraCharacteristics(cameraMgr, devid, &metadata) != ACAMERA_OK) { 633 return; // oh well. 634 } else if (pACameraMetadata_getConstEntry(metadata, ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &cfgentry) != ACAMERA_OK) { 635 pACameraMetadata_free(metadata); 636 return; // oh well. 637 } else if (pACameraMetadata_getConstEntry(metadata, ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS, &durentry) != ACAMERA_OK) { 638 pACameraMetadata_free(metadata); 639 return; // oh well. 640 } 641 642 *fullname = NULL; 643 if (pACameraMetadata_getConstEntry(metadata, ACAMERA_INFO_VERSION, &infoentry) == ACAMERA_OK) { 644 *fullname = (char *) SDL_malloc(infoentry.count + 1); 645 if (*fullname) { 646 SDL_strlcpy(*fullname, (const char *) infoentry.data.u8, infoentry.count + 1); 647 } 648 } 649 650 ACameraMetadata_const_entry posentry; 651 if (pACameraMetadata_getConstEntry(metadata, ACAMERA_LENS_FACING, &posentry) == ACAMERA_OK) { // ignore this if it fails. 652 if (*posentry.data.u8 == ACAMERA_LENS_FACING_FRONT) { 653 *position = SDL_CAMERA_POSITION_FRONT_FACING; 654 if (!*fullname) { 655 *fullname = SDL_strdup("Front-facing camera"); 656 } 657 } else if (*posentry.data.u8 == ACAMERA_LENS_FACING_BACK) { 658 *position = SDL_CAMERA_POSITION_BACK_FACING; 659 if (!*fullname) { 660 *fullname = SDL_strdup("Back-facing camera"); 661 } 662 } 663 } 664 665 if (!*fullname) { 666 *fullname = SDL_strdup("Generic camera"); // we tried. 667 } 668 669 const int32_t *i32ptr = cfgentry.data.i32; 670 for (int i = 0; i < cfgentry.count; i++, i32ptr += 4) { 671 const int32_t fmt = i32ptr[0]; 672 const int w = i32ptr[1]; 673 const int h = i32ptr[2]; 674 const int32_t type = i32ptr[3]; 675 SDL_PixelFormat sdlfmt = SDL_PIXELFORMAT_UNKNOWN; 676 SDL_Colorspace colorspace = SDL_COLORSPACE_UNKNOWN; 677 678 if (type == ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT) { 679 continue; 680 } else if ((w <= 0) || (h <= 0)) { 681 continue; 682 } else { 683 format_android_to_sdl(fmt, &sdlfmt, &colorspace); 684 if (sdlfmt == SDL_PIXELFORMAT_UNKNOWN) { 685 continue; 686 } 687 } 688 689#if 0 // !!! FIXME: these all come out with 0 durations on my test phone. :( 690 const int64_t *i64ptr = durentry.data.i64; 691 for (int j = 0; j < durentry.count; j++, i64ptr += 4) { 692 const int32_t fpsfmt = (int32_t) i64ptr[0]; 693 const int fpsw = (int) i64ptr[1]; 694 const int fpsh = (int) i64ptr[2]; 695 const long long duration = (long long) i64ptr[3]; 696 SDL_Log("CAMERA: possible fps %s %dx%d duration=%lld", SDL_GetPixelFormatName(sdlfmt), fpsw, fpsh, duration); 697 if ((duration > 0) && (fpsfmt == fmt) && (fpsw == w) && (fpsh == h)) { 698 SDL_AddCameraFormat(add_data, sdlfmt, colorspace, w, h, 1000000000, duration); 699 } 700 } 701#else 702 SDL_AddCameraFormat(add_data, sdlfmt, colorspace, w, h, 30, 1); 703#endif 704 } 705 706 pACameraMetadata_free(metadata); 707} 708 709static bool FindAndroidCameraByID(SDL_Camera *device, void *userdata) 710{ 711 const char *devid = (const char *) userdata; 712 return (SDL_strcmp(devid, (const char *) device->handle) == 0); 713} 714 715static void MaybeAddDevice(const char *devid) 716{ 717 #if DEBUG_CAMERA 718 SDL_Log("CAMERA: MaybeAddDevice('%s')", devid); 719 #endif 720 721 if (SDL_FindPhysicalCameraByCallback(FindAndroidCameraByID, (void *) devid)) { 722 return; // already have this one. 723 } 724 725 SDL_CameraPosition position = SDL_CAMERA_POSITION_UNKNOWN; 726 char *fullname = NULL; 727 CameraFormatAddData add_data; 728 GatherCameraSpecs(devid, &add_data, &fullname, &position); 729 if (add_data.num_specs > 0) { 730 char *namecpy = SDL_strdup(devid); 731 if (namecpy) { 732 SDL_Camera *device = SDL_AddCamera(fullname, position, add_data.num_specs, add_data.specs, namecpy); 733 if (!device) { 734 SDL_free(namecpy); 735 } 736 } 737 } 738 739 SDL_free(fullname); 740 SDL_free(add_data.specs); 741} 742 743// note that camera "availability" covers both hotplugging and whether another 744// has the device opened, but for something like Android, it's probably fine 745// to treat both unplugging and loss of access as disconnection events. When 746// the other app closes the camera, we get an available event as if it was 747// just plugged back in. 748 749static void onCameraAvailable(void *context, const char *cameraId) 750{ 751 #if DEBUG_CAMERA 752 SDL_Log("CAMERA: CB onCameraAvailable('%s')", cameraId); 753 #endif 754 SDL_assert(cameraId != NULL); 755 MaybeAddDevice(cameraId); 756} 757 758static void onCameraUnavailable(void *context, const char *cameraId) 759{ 760 #if DEBUG_CAMERA 761 SDL_Log("CAMERA: CB onCameraUnavailable('%s')", cameraId); 762 #endif 763 764 SDL_assert(cameraId != NULL); 765 766 // THIS CALLBACK FIRES WHEN YOU OPEN THE DEVICE YOURSELF. :( 767 // Make sure we don't have the device opened, in which case onDisconnected will fire instead if actually lost. 768 SDL_Camera *device = SDL_FindPhysicalCameraByCallback(FindAndroidCameraByID, (void *) cameraId); 769 if (device && !device->hidden) { 770 SDL_CameraDisconnected(device); 771 } 772} 773 774static const ACameraManager_AvailabilityCallbacks camera_availability_listener = { 775 NULL, 776 onCameraAvailable, 777 onCameraUnavailable 778}; 779 780static void ANDROIDCAMERA_DetectDevices(void) 781{ 782 ACameraIdList *list = NULL; 783 camera_status_t res = pACameraManager_getCameraIdList(cameraMgr, &list); 784 785 if ((res == ACAMERA_OK) && list) { 786 const int total = list->numCameras; 787 for (int i = 0; i < total; i++) { 788 MaybeAddDevice(list->cameraIds[i]); 789 } 790 791 pACameraManager_deleteCameraIdList(list); 792 } 793 794 pACameraManager_registerAvailabilityCallback(cameraMgr, &camera_availability_listener); 795} 796 797static void ANDROIDCAMERA_Deinitialize(void) 798{ 799 pACameraManager_unregisterAvailabilityCallback(cameraMgr, &camera_availability_listener); 800 DestroyCameraManager(); 801 802 dlclose(libcamera2ndk); 803 libcamera2ndk = NULL; 804 pACameraManager_create = NULL; 805 pACameraManager_registerAvailabilityCallback = NULL; 806 pACameraManager_unregisterAvailabilityCallback = NULL; 807 pACameraManager_getCameraIdList = NULL; 808 pACameraManager_deleteCameraIdList = NULL; 809 pACameraCaptureSession_close = NULL; 810 pACaptureRequest_free = NULL; 811 pACameraOutputTarget_free = NULL; 812 pACameraDevice_close = NULL; 813 pACameraManager_delete = NULL; 814 pACaptureSessionOutputContainer_free = NULL; 815 pACaptureSessionOutput_free = NULL; 816 pACameraManager_openCamera = NULL; 817 pACameraDevice_createCaptureRequest = NULL; 818 pACameraDevice_createCaptureSession = NULL; 819 pACameraManager_getCameraCharacteristics = NULL; 820 pACameraMetadata_free = NULL; 821 pACameraMetadata_getConstEntry = NULL; 822 pACameraCaptureSession_setRepeatingRequest = NULL; 823 pACameraOutputTarget_create = NULL; 824 pACaptureRequest_addTarget = NULL; 825 pACaptureSessionOutputContainer_add = NULL; 826 pACaptureSessionOutputContainer_create = NULL; 827 pACaptureSessionOutput_create = NULL; 828 829 dlclose(libmediandk); 830 libmediandk = NULL; 831 pAImage_delete = NULL; 832 pAImage_getTimestamp = NULL; 833 pAImage_getNumberOfPlanes = NULL; 834 pAImage_getPlaneRowStride = NULL; 835 pAImage_getPlaneData = NULL; 836 pAImageReader_acquireNextImage = NULL; 837 pAImageReader_delete = NULL; 838 pAImageReader_setImageListener = NULL; 839 pAImageReader_getWindow = NULL; 840 pAImageReader_new = NULL; 841} 842 843static bool ANDROIDCAMERA_Init(SDL_CameraDriverImpl *impl) 844{ 845 // !!! FIXME: slide this off into a subroutine 846 // system libraries are in android-24 and later; we currently target android-21 and later, so check if they exist at runtime. 847 void *libcamera2 = dlopen("libcamera2ndk.so", RTLD_NOW | RTLD_LOCAL); 848 if (!libcamera2) { 849 SDL_Log("CAMERA: libcamera2ndk.so can't be loaded: %s", dlerror()); 850 return false; 851 } 852 853 void *libmedia = dlopen("libmediandk.so", RTLD_NOW | RTLD_LOCAL); 854 if (!libmedia) { 855 SDL_Log("CAMERA: libmediandk.so can't be loaded: %s", dlerror()); 856 dlclose(libcamera2); 857 return false; 858 } 859 860 bool okay = true; 861 #define LOADSYM(lib, fn) if (okay) { p##fn = (pfn##fn) dlsym(lib, #fn); if (!p##fn) { SDL_Log("CAMERA: symbol '%s' can't be found in %s: %s", #fn, #lib "ndk.so", dlerror()); okay = false; } } 862 //#define LOADSYM(lib, fn) p##fn = (pfn##fn) fn 863 LOADSYM(libcamera2, ACameraManager_create); 864 LOADSYM(libcamera2, ACameraManager_registerAvailabilityCallback); 865 LOADSYM(libcamera2, ACameraManager_unregisterAvailabilityCallback); 866 LOADSYM(libcamera2, ACameraManager_getCameraIdList); 867 LOADSYM(libcamera2, ACameraManager_deleteCameraIdList); 868 LOADSYM(libcamera2, ACameraCaptureSession_close); 869 LOADSYM(libcamera2, ACaptureRequest_free); 870 LOADSYM(libcamera2, ACameraOutputTarget_free); 871 LOADSYM(libcamera2, ACameraDevice_close); 872 LOADSYM(libcamera2, ACameraManager_delete); 873 LOADSYM(libcamera2, ACaptureSessionOutputContainer_free); 874 LOADSYM(libcamera2, ACaptureSessionOutput_free); 875 LOADSYM(libcamera2, ACameraManager_openCamera); 876 LOADSYM(libcamera2, ACameraDevice_createCaptureRequest); 877 LOADSYM(libcamera2, ACameraDevice_createCaptureSession); 878 LOADSYM(libcamera2, ACameraManager_getCameraCharacteristics); 879 LOADSYM(libcamera2, ACameraMetadata_free); 880 LOADSYM(libcamera2, ACameraMetadata_getConstEntry); 881 LOADSYM(libcamera2, ACameraCaptureSession_setRepeatingRequest); 882 LOADSYM(libcamera2, ACameraOutputTarget_create); 883 LOADSYM(libcamera2, ACaptureRequest_addTarget); 884 LOADSYM(libcamera2, ACaptureSessionOutputContainer_add); 885 LOADSYM(libcamera2, ACaptureSessionOutputContainer_create); 886 LOADSYM(libcamera2, ACaptureSessionOutput_create); 887 LOADSYM(libmedia, AImage_delete); 888 LOADSYM(libmedia, AImage_getTimestamp); 889 LOADSYM(libmedia, AImage_getNumberOfPlanes); 890 LOADSYM(libmedia, AImage_getPlaneRowStride); 891 LOADSYM(libmedia, AImage_getPlaneData); 892 LOADSYM(libmedia, AImageReader_acquireNextImage); 893 LOADSYM(libmedia, AImageReader_delete); 894 LOADSYM(libmedia, AImageReader_setImageListener); 895 LOADSYM(libmedia, AImageReader_getWindow); 896 LOADSYM(libmedia, AImageReader_new); 897 LOADSYM(libmedia, AImage_getWidth); 898 LOADSYM(libmedia, AImage_getHeight); 899 900 #undef LOADSYM 901 902 if (!okay) { 903 dlclose(libmedia); 904 dlclose(libcamera2); 905 } 906 907 if (!CreateCameraManager()) { 908 dlclose(libmedia); 909 dlclose(libcamera2); 910 return false; 911 } 912 913 libcamera2ndk = libcamera2; 914 libmediandk = libmedia; 915 916 impl->DetectDevices = ANDROIDCAMERA_DetectDevices; 917 impl->OpenDevice = ANDROIDCAMERA_OpenDevice; 918 impl->CloseDevice = ANDROIDCAMERA_CloseDevice; 919 impl->WaitDevice = ANDROIDCAMERA_WaitDevice; 920 impl->AcquireFrame = ANDROIDCAMERA_AcquireFrame; 921 impl->ReleaseFrame = ANDROIDCAMERA_ReleaseFrame; 922 impl->FreeDeviceHandle = ANDROIDCAMERA_FreeDeviceHandle; 923 impl->Deinitialize = ANDROIDCAMERA_Deinitialize; 924 925 impl->ProvidesOwnCallbackThread = true; 926 927 return true; 928} 929 930CameraBootStrap ANDROIDCAMERA_bootstrap = { 931 "android", "SDL Android camera driver", ANDROIDCAMERA_Init, false 932}; 933 934#endif 935[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.