1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
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};
153
154static bool SetErrorStr(const char *what, const char *errstr, const int rc)
155{
156 char errbuf[128];
157 if (!errstr) {
158 SDL_snprintf(errbuf, sizeof (errbuf), "Unknown error #%d", rc);
159 errstr = errbuf;
160 }
161 return SDL_SetError("%s: %s", what, errstr);
162}
163
164static const char *CameraStatusStr(const camera_status_t rc)
165{
166 switch (rc) {
167 case ACAMERA_OK: return "no error";
168 case ACAMERA_ERROR_UNKNOWN: return "unknown error";
169 case ACAMERA_ERROR_INVALID_PARAMETER: return "invalid parameter";
170 case ACAMERA_ERROR_CAMERA_DISCONNECTED: return "camera disconnected";
171 case ACAMERA_ERROR_NOT_ENOUGH_MEMORY: return "not enough memory";
172 case ACAMERA_ERROR_METADATA_NOT_FOUND: return "metadata not found";
173 case ACAMERA_ERROR_CAMERA_DEVICE: return "camera device error";
174 case ACAMERA_ERROR_CAMERA_SERVICE: return "camera service error";
175 case ACAMERA_ERROR_SESSION_CLOSED: return "session closed";
176 case ACAMERA_ERROR_INVALID_OPERATION: return "invalid operation";
177 case ACAMERA_ERROR_STREAM_CONFIGURE_FAIL: return "configure failure";
178 case ACAMERA_ERROR_CAMERA_IN_USE: return "camera in use";
179 case ACAMERA_ERROR_MAX_CAMERA_IN_USE: return "max cameras in use";
180 case ACAMERA_ERROR_CAMERA_DISABLED: return "camera disabled";
181 case ACAMERA_ERROR_PERMISSION_DENIED: return "permission denied";
182 case ACAMERA_ERROR_UNSUPPORTED_OPERATION: return "unsupported operation";
183 default: break;
184 }
185
186 return NULL; // unknown error
187}
188
189static bool SetCameraError(const char *what, const camera_status_t rc)
190{
191 return SetErrorStr(what, CameraStatusStr(rc), (int) rc);
192}
193
194static const char *MediaStatusStr(const media_status_t rc)
195{
196 switch (rc) {
197 case AMEDIA_OK: return "no error";
198 case AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE: return "insufficient resources";
199 case AMEDIACODEC_ERROR_RECLAIMED: return "reclaimed";
200 case AMEDIA_ERROR_UNKNOWN: return "unknown error";
201 case AMEDIA_ERROR_MALFORMED: return "malformed";
202 case AMEDIA_ERROR_UNSUPPORTED: return "unsupported";
203 case AMEDIA_ERROR_INVALID_OBJECT: return "invalid object";
204 case AMEDIA_ERROR_INVALID_PARAMETER: return "invalid parameter";
205 case AMEDIA_ERROR_INVALID_OPERATION: return "invalid operation";
206 case AMEDIA_ERROR_END_OF_STREAM: return "end of stream";
207 case AMEDIA_ERROR_IO: return "i/o error";
208 case AMEDIA_ERROR_WOULD_BLOCK: return "operation would block";
209 case AMEDIA_DRM_NOT_PROVISIONED: return "DRM not provisioned";
210 case AMEDIA_DRM_RESOURCE_BUSY: return "DRM resource busy";
211 case AMEDIA_DRM_DEVICE_REVOKED: return "DRM device revoked";
212 case AMEDIA_DRM_SHORT_BUFFER: return "DRM short buffer";
213 case AMEDIA_DRM_SESSION_NOT_OPENED: return "DRM session not opened";
214 case AMEDIA_DRM_TAMPER_DETECTED: return "DRM tampering detected";
215 case AMEDIA_DRM_VERIFY_FAILED: return "DRM verify failed";
216 case AMEDIA_DRM_NEED_KEY: return "DRM need key";
217 case AMEDIA_DRM_LICENSE_EXPIRED: return "DRM license expired";
218 case AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE: return "no buffer available";
219 case AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED: return "maximum images acquired";
220 case AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE: return "cannot lock image";
221 case AMEDIA_IMGREADER_CANNOT_UNLOCK_IMAGE: return "cannot unlock image";
222 case AMEDIA_IMGREADER_IMAGE_NOT_LOCKED: return "image not locked";
223 default: break;
224 }
225
226 return NULL; // unknown error
227}
228
229static bool SetMediaError(const char *what, const media_status_t rc)
230{
231 return SetErrorStr(what, MediaStatusStr(rc), (int) rc);
232}
233
234
235static ACameraManager *cameraMgr = NULL;
236
237static bool CreateCameraManager(void)
238{
239 SDL_assert(cameraMgr == NULL);
240
241 cameraMgr = pACameraManager_create();
242 if (!cameraMgr) {
243 return SDL_SetError("Error creating ACameraManager");
244 }
245 return true;
246}
247
248static void DestroyCameraManager(void)
249{
250 if (cameraMgr) {
251 pACameraManager_delete(cameraMgr);
252 cameraMgr = NULL;
253 }
254}
255
256static void format_android_to_sdl(Uint32 fmt, SDL_PixelFormat *format, SDL_Colorspace *colorspace)
257{
258 switch (fmt) {
259 #define CASE(x, y, z) case x: *format = y; *colorspace = z; return
260 CASE(AIMAGE_FORMAT_YUV_420_888, SDL_PIXELFORMAT_NV12, SDL_COLORSPACE_BT709_LIMITED);
261 CASE(AIMAGE_FORMAT_RGB_565, SDL_PIXELFORMAT_RGB565, SDL_COLORSPACE_SRGB);
262 CASE(AIMAGE_FORMAT_RGB_888, SDL_PIXELFORMAT_XRGB8888, SDL_COLORSPACE_SRGB);
263 CASE(AIMAGE_FORMAT_RGBA_8888, SDL_PIXELFORMAT_RGBA8888, SDL_COLORSPACE_SRGB);
264 CASE(AIMAGE_FORMAT_RGBX_8888, SDL_PIXELFORMAT_RGBX8888, SDL_COLORSPACE_SRGB);
265 CASE(AIMAGE_FORMAT_RGBA_FP16, SDL_PIXELFORMAT_RGBA64_FLOAT, SDL_COLORSPACE_SRGB);
266 #undef CASE
267 default: break;
268 }
269
270 #if DEBUG_CAMERA
271 //SDL_Log("Unknown format AIMAGE_FORMAT '%d'", fmt);
272 #endif
273
274 *format = SDL_PIXELFORMAT_UNKNOWN;
275 *colorspace = SDL_COLORSPACE_UNKNOWN;
276}
277
278static Uint32 format_sdl_to_android(SDL_PixelFormat fmt)
279{
280 switch (fmt) {
281 #define CASE(x, y) case y: return x
282 CASE(AIMAGE_FORMAT_YUV_420_888, SDL_PIXELFORMAT_NV12);
283 CASE(AIMAGE_FORMAT_RGB_565, SDL_PIXELFORMAT_RGB565);
284 CASE(AIMAGE_FORMAT_RGB_888, SDL_PIXELFORMAT_XRGB8888);
285 CASE(AIMAGE_FORMAT_RGBA_8888, SDL_PIXELFORMAT_RGBA8888);
286 CASE(AIMAGE_FORMAT_RGBX_8888, SDL_PIXELFORMAT_RGBX8888);
287 #undef CASE
288 default:
289 return 0;
290 }
291}
292
293static bool ANDROIDCAMERA_WaitDevice(SDL_Camera *device)
294{
295 return true; // this isn't used atm, since we run our own thread via onImageAvailable callbacks.
296}
297
298static SDL_CameraFrameResult ANDROIDCAMERA_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS)
299{
300 SDL_CameraFrameResult result = SDL_CAMERA_FRAME_READY;
301 media_status_t res;
302 AImage *image = NULL;
303
304 res = pAImageReader_acquireNextImage(device->hidden->reader, &image);
305 // We could also use this one:
306 //res = AImageReader_acquireLatestImage(device->hidden->reader, &image);
307
308 SDL_assert(res != AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE); // we should only be here if onImageAvailable was called.
309
310 if (res != AMEDIA_OK) {
311 SetMediaError("Error AImageReader_acquireNextImage", res);
312 return SDL_CAMERA_FRAME_ERROR;
313 }
314
315 int64_t atimestamp = 0;
316 if (pAImage_getTimestamp(image, &atimestamp) == AMEDIA_OK) {
317 *timestampNS = (Uint64) atimestamp;
318 } else {
319 *timestampNS = 0;
320 }
321
322 // !!! 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...
323 int32_t num_planes = 0;
324 pAImage_getNumberOfPlanes(image, &num_planes);
325
326 if ((num_planes == 3) && (device->spec.format == SDL_PIXELFORMAT_NV12)) {
327 num_planes--; // treat the interleaved planes as one.
328 }
329
330 // !!! FIXME: we have an open issue in SDL3 to allow SDL_Surface to support non-contiguous planar data, but we don't have it yet.
331 size_t buflen = 0;
332 for (int i = 0; (i < num_planes) && (i < 3); i++) {
333 uint8_t *data = NULL;
334 int32_t datalen = 0;
335 pAImage_getPlaneData(image, i, &data, &datalen);
336 buflen += (int) datalen;
337 }
338
339 frame->pixels = SDL_aligned_alloc(SDL_GetSIMDAlignment(), buflen);
340 if (frame->pixels == NULL) {
341 result = SDL_CAMERA_FRAME_ERROR;
342 } else {
343 int32_t row_stride = 0;
344 Uint8 *dst = frame->pixels;
345 pAImage_getPlaneRowStride(image, 0, &row_stride);
346 frame->pitch = (int) row_stride; // this is what SDL3 currently expects, probably incorrectly.
347
348 for (int i = 0; (i < num_planes) && (i < 3); i++) {
349 uint8_t *data = NULL;
350 int32_t datalen = 0;
351 pAImage_getPlaneData(image, i, &data, &datalen);
352 const void *src = data;
353 SDL_memcpy(dst, src, datalen);
354 dst += datalen;
355 }
356 }
357
358 pAImage_delete(image);
359
360 return result;
361}
362
363static void ANDROIDCAMERA_ReleaseFrame(SDL_Camera *device, SDL_Surface *frame)
364{
365 // !!! FIXME: this currently copies the data to the surface, but in theory we could just keep the AImage until ReleaseFrame...
366 SDL_aligned_free(frame->pixels);
367}
368
369static void onImageAvailable(void *context, AImageReader *reader)
370{
371 #if DEBUG_CAMERA
372 SDL_Log("CAMERA: CB onImageAvailable");
373 #endif
374 SDL_Camera *device = (SDL_Camera *) context;
375 SDL_CameraThreadIterate(device);
376}
377
378static void onDisconnected(void *context, ACameraDevice *device)
379{
380 #if DEBUG_CAMERA
381 SDL_Log("CAMERA: CB onDisconnected");
382 #endif
383 SDL_CameraDisconnected((SDL_Camera *) context);
384}
385
386static void onError(void *context, ACameraDevice *device, int error)
387{
388 #if DEBUG_CAMERA
389 SDL_Log("CAMERA: CB onError");
390 #endif
391 SDL_CameraDisconnected((SDL_Camera *) context);
392}
393
394static void onClosed(void* context, ACameraCaptureSession *session)
395{
396 // SDL_Camera *_this = (SDL_Camera *) context;
397 #if DEBUG_CAMERA
398 SDL_Log("CAMERA: CB onClosed");
399 #endif
400}
401
402static void onReady(void* context, ACameraCaptureSession *session)
403{
404 // SDL_Camera *_this = (SDL_Camera *) context;
405 #if DEBUG_CAMERA
406 SDL_Log("CAMERA: CB onReady");
407 #endif
408}
409
410static void onActive(void* context, ACameraCaptureSession *session)
411{
412 // SDL_Camera *_this = (SDL_Camera *) context;
413 #if DEBUG_CAMERA
414 SDL_Log("CAMERA: CB onActive");
415 #endif
416}
417
418static void ANDROIDCAMERA_CloseDevice(SDL_Camera *device)
419{
420 if (device && device->hidden) {
421 struct SDL_PrivateCameraData *hidden = device->hidden;
422 device->hidden = NULL;
423
424 if (hidden->reader) {
425 pAImageReader_setImageListener(hidden->reader, NULL);
426 }
427
428 if (hidden->session) {
429 pACameraCaptureSession_close(hidden->session);
430 }
431
432 if (hidden->request) {
433 pACaptureRequest_free(hidden->request);
434 }
435
436 if (hidden->outputTarget) {
437 pACameraOutputTarget_free(hidden->outputTarget);
438 }
439
440 if (hidden->sessionOutputContainer) {
441 pACaptureSessionOutputContainer_free(hidden->sessionOutputContainer);
442 }
443
444 if (hidden->sessionOutput) {
445 pACaptureSessionOutput_free(hidden->sessionOutput);
446 }
447
448 // we don't free hidden->window here, it'll be cleaned up by AImageReader_delete.
449
450 if (hidden->reader) {
451 pAImageReader_delete(hidden->reader);
452 }
453
454 if (hidden->device) {
455 pACameraDevice_close(hidden->device);
456 }
457
458 SDL_free(hidden);
459 }
460}
461
462// this is where the "opening" of the camera happens, after permission is granted.
463static bool PrepareCamera(SDL_Camera *device)
464{
465 SDL_assert(device->hidden != NULL);
466
467 camera_status_t res;
468 media_status_t res2;
469
470 ACameraDevice_StateCallbacks dev_callbacks;
471 SDL_zero(dev_callbacks);
472 dev_callbacks.context = device;
473 dev_callbacks.onDisconnected = onDisconnected;
474 dev_callbacks.onError = onError;
475
476 ACameraCaptureSession_stateCallbacks capture_callbacks;
477 SDL_zero(capture_callbacks);
478 capture_callbacks.context = device;
479 capture_callbacks.onClosed = onClosed;
480 capture_callbacks.onReady = onReady;
481 capture_callbacks.onActive = onActive;
482
483 AImageReader_ImageListener imglistener;
484 SDL_zero(imglistener);
485 imglistener.context = device;
486 imglistener.onImageAvailable = onImageAvailable;
487
488 // just in case SDL_OpenCamera is overwriting device->spec as CameraPermissionCallback runs, we work from a different copy.
489 const SDL_CameraSpec *spec = &device->hidden->requested_spec;
490
491 if ((res = pACameraManager_openCamera(cameraMgr, (const char *) device->handle, &dev_callbacks, &device->hidden->device)) != ACAMERA_OK) {
492 return SetCameraError("Failed to open camera", res);
493 } else if ((res2 = pAImageReader_new(spec->width, spec->height, format_sdl_to_android(spec->format), 10 /* nb buffers */, &device->hidden->reader)) != AMEDIA_OK) {
494 return SetMediaError("Error AImageReader_new", res2);
495 } else if ((res2 = pAImageReader_getWindow(device->hidden->reader, &device->hidden->window)) != AMEDIA_OK) {
496 return SetMediaError("Error AImageReader_getWindow", res2);
497 } else if ((res = pACaptureSessionOutput_create(device->hidden->window, &device->hidden->sessionOutput)) != ACAMERA_OK) {
498 return SetCameraError("Error ACaptureSessionOutput_create", res);
499 } else if ((res = pACaptureSessionOutputContainer_create(&device->hidden->sessionOutputContainer)) != ACAMERA_OK) {
500 return SetCameraError("Error ACaptureSessionOutputContainer_create", res);
501 } else if ((res = pACaptureSessionOutputContainer_add(device->hidden->sessionOutputContainer, device->hidden->sessionOutput)) != ACAMERA_OK) {
502 return SetCameraError("Error ACaptureSessionOutputContainer_add", res);
503 } else if ((res = pACameraOutputTarget_create(device->hidden->window, &device->hidden->outputTarget)) != ACAMERA_OK) {
504 return SetCameraError("Error ACameraOutputTarget_create", res);
505 } else if ((res = pACameraDevice_createCaptureRequest(device->hidden->device, TEMPLATE_RECORD, &device->hidden->request)) != ACAMERA_OK) {
506 return SetCameraError("Error ACameraDevice_createCaptureRequest", res);
507 } else if ((res = pACaptureRequest_addTarget(device->hidden->request, device->hidden->outputTarget)) != ACAMERA_OK) {
508 return SetCameraError("Error ACaptureRequest_addTarget", res);
509 } else if ((res = pACameraDevice_createCaptureSession(device->hidden->device, device->hidden->sessionOutputContainer, &capture_callbacks, &device->hidden->session)) != ACAMERA_OK) {
510 return SetCameraError("Error ACameraDevice_createCaptureSession", res);
511 } else if ((res = pACameraCaptureSession_setRepeatingRequest(device->hidden->session, NULL, 1, &device->hidden->request, NULL)) != ACAMERA_OK) {
512 return SetCameraError("Error ACameraCaptureSession_setRepeatingRequest", res);
513 } else if ((res2 = pAImageReader_setImageListener(device->hidden->reader, &imglistener)) != AMEDIA_OK) {
514 return SetMediaError("Error AImageReader_setImageListener", res2);
515 }
516
517 return true;
518}
519
520static void SDLCALL CameraPermissionCallback(void *userdata, const char *permission, bool granted)
521{
522 SDL_Camera *device = (SDL_Camera *) userdata;
523 if (device->hidden != NULL) { // if device was already closed, don't send an event.
524 if (!granted) {
525 SDL_CameraPermissionOutcome(device, false); // sorry, permission denied.
526 } else if (!PrepareCamera(device)) { // permission given? Actually open the camera now.
527 // uhoh, setup failed; since the app thinks we already "opened" the device, mark it as disconnected and don't report the permission.
528 SDL_CameraDisconnected(device);
529 } else {
530 // okay! We have permission to use the camera _and_ opening the hardware worked out, report that the camera is usable!
531 SDL_CameraPermissionOutcome(device, true); // go go go!
532 }
533 }
534
535 UnrefPhysicalCamera(device); // we ref'd this in OpenDevice, release the extra reference.
536}
537
538
539static bool ANDROIDCAMERA_OpenDevice(SDL_Camera *device, const SDL_CameraSpec *spec)
540{
541#if 0 // !!! FIXME: for now, we'll just let this fail if it is going to fail, without checking for this
542 /* Cannot open a second camera, while the first one is opened.
543 * If you want to play several camera, they must all be opened first, then played.
544 *
545 * https://developer.android.com/reference/android/hardware/camera2/CameraManager
546 * "All camera devices intended to be operated concurrently, must be opened using openCamera(String, CameraDevice.StateCallback, Handler),
547 * before configuring sessions on any of the camera devices. * "
548 *
549 */
550 if (CheckDevicePlaying()) {
551 return SDL_SetError("A camera is already playing");
552 }
553#endif
554
555 device->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData));
556 if (device->hidden == NULL) {
557 return false;
558 }
559
560 RefPhysicalCamera(device); // ref'd until permission callback fires.
561
562 // just in case SDL_OpenCamera is overwriting device->spec as CameraPermissionCallback runs, we work from a different copy.
563 SDL_copyp(&device->hidden->requested_spec, spec);
564 if (!SDL_RequestAndroidPermission("android.permission.CAMERA", CameraPermissionCallback, device)) {
565 UnrefPhysicalCamera(device);
566 return false;
567 }
568
569 return true; // we don't open the camera until permission is granted, so always succeed for now.
570}
571
572static void ANDROIDCAMERA_FreeDeviceHandle(SDL_Camera *device)
573{
574 if (device) {
575 SDL_free(device->handle);
576 }
577}
578
579static void GatherCameraSpecs(const char *devid, CameraFormatAddData *add_data, char **fullname, SDL_CameraPosition *position)
580{
581 SDL_zerop(add_data);
582
583 ACameraMetadata *metadata = NULL;
584 ACameraMetadata_const_entry cfgentry;
585 ACameraMetadata_const_entry durentry;
586 ACameraMetadata_const_entry infoentry;
587
588 // This can fail with an "unknown error" (with `adb logcat` reporting "no such file or directory")
589 // for "LEGACY" level cameras. I saw this happen on a 30-dollar budget phone I have for testing
590 // (but a different brand budget phone worked, so it's not strictly the low-end of Android devices).
591 // LEGACY devices are seen by onCameraAvailable, but are not otherwise accessible through
592 // libcamera2ndk. The Java camera2 API apparently _can_ access these cameras, but we're going on
593 // without them here for now, in hopes that such hardware is a dying breed.
594 if (pACameraManager_getCameraCharacteristics(cameraMgr, devid, &metadata) != ACAMERA_OK) {
595 return; // oh well.
596 } else if (pACameraMetadata_getConstEntry(metadata, ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &cfgentry) != ACAMERA_OK) {
597 pACameraMetadata_free(metadata);
598 return; // oh well.
599 } else if (pACameraMetadata_getConstEntry(metadata, ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS, &durentry) != ACAMERA_OK) {
600 pACameraMetadata_free(metadata);
601 return; // oh well.
602 }
603
604 *fullname = NULL;
605 if (pACameraMetadata_getConstEntry(metadata, ACAMERA_INFO_VERSION, &infoentry) == ACAMERA_OK) {
606 *fullname = (char *) SDL_malloc(infoentry.count + 1);
607 if (*fullname) {
608 SDL_strlcpy(*fullname, (const char *) infoentry.data.u8, infoentry.count + 1);
609 }
610 }
611
612 ACameraMetadata_const_entry posentry;
613 if (pACameraMetadata_getConstEntry(metadata, ACAMERA_LENS_FACING, &posentry) == ACAMERA_OK) { // ignore this if it fails.
614 if (*posentry.data.u8 == ACAMERA_LENS_FACING_FRONT) {
615 *position = SDL_CAMERA_POSITION_FRONT_FACING;
616 if (!*fullname) {
617 *fullname = SDL_strdup("Front-facing camera");
618 }
619 } else if (*posentry.data.u8 == ACAMERA_LENS_FACING_BACK) {
620 *position = SDL_CAMERA_POSITION_BACK_FACING;
621 if (!*fullname) {
622 *fullname = SDL_strdup("Back-facing camera");
623 }
624 }
625 }
626
627 if (!*fullname) {
628 *fullname = SDL_strdup("Generic camera"); // we tried.
629 }
630
631 const int32_t *i32ptr = cfgentry.data.i32;
632 for (int i = 0; i < cfgentry.count; i++, i32ptr += 4) {
633 const int32_t fmt = i32ptr[0];
634 const int w = (int) i32ptr[1];
635 const int h = (int) i32ptr[2];
636 const int32_t type = i32ptr[3];
637 SDL_PixelFormat sdlfmt = SDL_PIXELFORMAT_UNKNOWN;
638 SDL_Colorspace colorspace = SDL_COLORSPACE_UNKNOWN;
639
640 if (type == ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT) {
641 continue;
642 } else if ((w <= 0) || (h <= 0)) {
643 continue;
644 } else {
645 format_android_to_sdl(fmt, &sdlfmt, &colorspace);
646 if (sdlfmt == SDL_PIXELFORMAT_UNKNOWN) {
647 continue;
648 }
649 }
650
651#if 0 // !!! FIXME: these all come out with 0 durations on my test phone. :(
652 const int64_t *i64ptr = durentry.data.i64;
653 for (int j = 0; j < durentry.count; j++, i64ptr += 4) {
654 const int32_t fpsfmt = (int32_t) i64ptr[0];
655 const int fpsw = (int) i64ptr[1];
656 const int fpsh = (int) i64ptr[2];
657 const long long duration = (long long) i64ptr[3];
658 SDL_Log("CAMERA: possible fps %s %dx%d duration=%lld", SDL_GetPixelFormatName(sdlfmt), fpsw, fpsh, duration);
659 if ((duration > 0) && (fpsfmt == fmt) && (fpsw == w) && (fpsh == h)) {
660 SDL_AddCameraFormat(add_data, sdlfmt, colorspace, w, h, 1000000000, duration);
661 }
662 }
663#else
664 SDL_AddCameraFormat(add_data, sdlfmt, colorspace, w, h, 30, 1);
665#endif
666 }
667
668 pACameraMetadata_free(metadata);
669}
670
671static bool FindAndroidCameraByID(SDL_Camera *device, void *userdata)
672{
673 const char *devid = (const char *) userdata;
674 return (SDL_strcmp(devid, (const char *) device->handle) == 0);
675}
676
677static void MaybeAddDevice(const char *devid)
678{
679 #if DEBUG_CAMERA
680 SDL_Log("CAMERA: MaybeAddDevice('%s')", devid);
681 #endif
682
683 if (SDL_FindPhysicalCameraByCallback(FindAndroidCameraByID, (void *) devid)) {
684 return; // already have this one.
685 }
686
687 SDL_CameraPosition position = SDL_CAMERA_POSITION_UNKNOWN;
688 char *fullname = NULL;
689 CameraFormatAddData add_data;
690 GatherCameraSpecs(devid, &add_data, &fullname, &position);
691 if (add_data.num_specs > 0) {
692 char *namecpy = SDL_strdup(devid);
693 if (namecpy) {
694 SDL_Camera *device = SDL_AddCamera(fullname, position, add_data.num_specs, add_data.specs, namecpy);
695 if (!device) {
696 SDL_free(namecpy);
697 }
698 }
699 }
700
701 SDL_free(fullname);
702 SDL_free(add_data.specs);
703}
704
705// note that camera "availability" covers both hotplugging and whether another
706// has the device opened, but for something like Android, it's probably fine
707// to treat both unplugging and loss of access as disconnection events. When
708// the other app closes the camera, we get an available event as if it was
709// just plugged back in.
710
711static void onCameraAvailable(void *context, const char *cameraId)
712{
713 #if DEBUG_CAMERA
714 SDL_Log("CAMERA: CB onCameraAvailable('%s')", cameraId);
715 #endif
716 SDL_assert(cameraId != NULL);
717 MaybeAddDevice(cameraId);
718}
719
720static void onCameraUnavailable(void *context, const char *cameraId)
721{
722 #if DEBUG_CAMERA
723 SDL_Log("CAMERA: CB onCameraUnvailable('%s')", cameraId);
724 #endif
725
726 SDL_assert(cameraId != NULL);
727
728 // THIS CALLBACK FIRES WHEN YOU OPEN THE DEVICE YOURSELF. :(
729 // Make sure we don't have the device opened, in which case onDisconnected will fire instead if actually lost.
730 SDL_Camera *device = SDL_FindPhysicalCameraByCallback(FindAndroidCameraByID, (void *) cameraId);
731 if (device && !device->hidden) {
732 SDL_CameraDisconnected(device);
733 }
734}
735
736static const ACameraManager_AvailabilityCallbacks camera_availability_listener = {
737 NULL,
738 onCameraAvailable,
739 onCameraUnavailable
740};
741
742static void ANDROIDCAMERA_DetectDevices(void)
743{
744 ACameraIdList *list = NULL;
745 camera_status_t res = pACameraManager_getCameraIdList(cameraMgr, &list);
746
747 if ((res == ACAMERA_OK) && list) {
748 const int total = list->numCameras;
749 for (int i = 0; i < total; i++) {
750 MaybeAddDevice(list->cameraIds[i]);
751 }
752
753 pACameraManager_deleteCameraIdList(list);
754 }
755
756 pACameraManager_registerAvailabilityCallback(cameraMgr, &camera_availability_listener);
757}
758
759static void ANDROIDCAMERA_Deinitialize(void)
760{
761 pACameraManager_unregisterAvailabilityCallback(cameraMgr, &camera_availability_listener);
762 DestroyCameraManager();
763
764 dlclose(libcamera2ndk);
765 libcamera2ndk = NULL;
766 pACameraManager_create = NULL;
767 pACameraManager_registerAvailabilityCallback = NULL;
768 pACameraManager_unregisterAvailabilityCallback = NULL;
769 pACameraManager_getCameraIdList = NULL;
770 pACameraManager_deleteCameraIdList = NULL;
771 pACameraCaptureSession_close = NULL;
772 pACaptureRequest_free = NULL;
773 pACameraOutputTarget_free = NULL;
774 pACameraDevice_close = NULL;
775 pACameraManager_delete = NULL;
776 pACaptureSessionOutputContainer_free = NULL;
777 pACaptureSessionOutput_free = NULL;
778 pACameraManager_openCamera = NULL;
779 pACameraDevice_createCaptureRequest = NULL;
780 pACameraDevice_createCaptureSession = NULL;
781 pACameraManager_getCameraCharacteristics = NULL;
782 pACameraMetadata_free = NULL;
783 pACameraMetadata_getConstEntry = NULL;
784 pACameraCaptureSession_setRepeatingRequest = NULL;
785 pACameraOutputTarget_create = NULL;
786 pACaptureRequest_addTarget = NULL;
787 pACaptureSessionOutputContainer_add = NULL;
788 pACaptureSessionOutputContainer_create = NULL;
789 pACaptureSessionOutput_create = NULL;
790
791 dlclose(libmediandk);
792 libmediandk = NULL;
793 pAImage_delete = NULL;
794 pAImage_getTimestamp = NULL;
795 pAImage_getNumberOfPlanes = NULL;
796 pAImage_getPlaneRowStride = NULL;
797 pAImage_getPlaneData = NULL;
798 pAImageReader_acquireNextImage = NULL;
799 pAImageReader_delete = NULL;
800 pAImageReader_setImageListener = NULL;
801 pAImageReader_getWindow = NULL;
802 pAImageReader_new = NULL;
803}
804
805static bool ANDROIDCAMERA_Init(SDL_CameraDriverImpl *impl)
806{
807 // !!! FIXME: slide this off into a subroutine
808 // system libraries are in android-24 and later; we currently target android-16 and later, so check if they exist at runtime.
809 void *libcamera2 = dlopen("libcamera2ndk.so", RTLD_NOW | RTLD_LOCAL);
810 if (!libcamera2) {
811 SDL_Log("CAMERA: libcamera2ndk.so can't be loaded: %s", dlerror());
812 return false;
813 }
814
815 void *libmedia = dlopen("libmediandk.so", RTLD_NOW | RTLD_LOCAL);
816 if (!libmedia) {
817 SDL_Log("CAMERA: libmediandk.so can't be loaded: %s", dlerror());
818 dlclose(libcamera2);
819 return false;
820 }
821
822 bool okay = true;
823 #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; } }
824 //#define LOADSYM(lib, fn) p##fn = (pfn##fn) fn
825 LOADSYM(libcamera2, ACameraManager_create);
826 LOADSYM(libcamera2, ACameraManager_registerAvailabilityCallback);
827 LOADSYM(libcamera2, ACameraManager_unregisterAvailabilityCallback);
828 LOADSYM(libcamera2, ACameraManager_getCameraIdList);
829 LOADSYM(libcamera2, ACameraManager_deleteCameraIdList);
830 LOADSYM(libcamera2, ACameraCaptureSession_close);
831 LOADSYM(libcamera2, ACaptureRequest_free);
832 LOADSYM(libcamera2, ACameraOutputTarget_free);
833 LOADSYM(libcamera2, ACameraDevice_close);
834 LOADSYM(libcamera2, ACameraManager_delete);
835 LOADSYM(libcamera2, ACaptureSessionOutputContainer_free);
836 LOADSYM(libcamera2, ACaptureSessionOutput_free);
837 LOADSYM(libcamera2, ACameraManager_openCamera);
838 LOADSYM(libcamera2, ACameraDevice_createCaptureRequest);
839 LOADSYM(libcamera2, ACameraDevice_createCaptureSession);
840 LOADSYM(libcamera2, ACameraManager_getCameraCharacteristics);
841 LOADSYM(libcamera2, ACameraMetadata_free);
842 LOADSYM(libcamera2, ACameraMetadata_getConstEntry);
843 LOADSYM(libcamera2, ACameraCaptureSession_setRepeatingRequest);
844 LOADSYM(libcamera2, ACameraOutputTarget_create);
845 LOADSYM(libcamera2, ACaptureRequest_addTarget);
846 LOADSYM(libcamera2, ACaptureSessionOutputContainer_add);
847 LOADSYM(libcamera2, ACaptureSessionOutputContainer_create);
848 LOADSYM(libcamera2, ACaptureSessionOutput_create);
849 LOADSYM(libmedia, AImage_delete);
850 LOADSYM(libmedia, AImage_getTimestamp);
851 LOADSYM(libmedia, AImage_getNumberOfPlanes);
852 LOADSYM(libmedia, AImage_getPlaneRowStride);
853 LOADSYM(libmedia, AImage_getPlaneData);
854 LOADSYM(libmedia, AImageReader_acquireNextImage);
855 LOADSYM(libmedia, AImageReader_delete);
856 LOADSYM(libmedia, AImageReader_setImageListener);
857 LOADSYM(libmedia, AImageReader_getWindow);
858 LOADSYM(libmedia, AImageReader_new);
859 LOADSYM(libmedia, AImage_getWidth);
860 LOADSYM(libmedia, AImage_getHeight);
861
862 #undef LOADSYM
863
864 if (!okay) {
865 dlclose(libmedia);
866 dlclose(libcamera2);
867 }
868
869 if (!CreateCameraManager()) {
870 dlclose(libmedia);
871 dlclose(libcamera2);
872 return false;
873 }
874
875 libcamera2ndk = libcamera2;
876 libmediandk = libmedia;
877
878 impl->DetectDevices = ANDROIDCAMERA_DetectDevices;
879 impl->OpenDevice = ANDROIDCAMERA_OpenDevice;
880 impl->CloseDevice = ANDROIDCAMERA_CloseDevice;
881 impl->WaitDevice = ANDROIDCAMERA_WaitDevice;
882 impl->AcquireFrame = ANDROIDCAMERA_AcquireFrame;
883 impl->ReleaseFrame = ANDROIDCAMERA_ReleaseFrame;
884 impl->FreeDeviceHandle = ANDROIDCAMERA_FreeDeviceHandle;
885 impl->Deinitialize = ANDROIDCAMERA_Deinitialize;
886
887 impl->ProvidesOwnCallbackThread = true;
888
889 return true;
890}
891
892CameraBootStrap ANDROIDCAMERA_bootstrap = {
893 "android", "SDL Android camera driver", ANDROIDCAMERA_Init, false
894};
895
896#endif