summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/camera/android
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/camera/android
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/camera/android')
-rw-r--r--contrib/SDL-3.2.8/src/camera/android/SDL_camera_android.c905
1 files changed, 905 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/camera/android/SDL_camera_android.c b/contrib/SDL-3.2.8/src/camera/android/SDL_camera_android.c
new file mode 100644
index 0000000..54b539a
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/camera/android/SDL_camera_android.c
@@ -0,0 +1,905 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 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 size_t buflen = 0;
331 pAImage_getPlaneRowStride(image, 0, &frame->pitch);
332 for (int i = 0; (i < num_planes) && (i < 3); i++) {
333 int32_t expected;
334 if (i == 0) {
335 expected = frame->pitch * frame->h;
336 } else {
337 expected = frame->pitch * (frame->h + 1) / 2;
338 }
339 buflen += expected;
340 }
341
342 frame->pixels = SDL_aligned_alloc(SDL_GetSIMDAlignment(), buflen);
343 if (frame->pixels == NULL) {
344 result = SDL_CAMERA_FRAME_ERROR;
345 } else {
346 Uint8 *dst = frame->pixels;
347
348 for (int i = 0; (i < num_planes) && (i < 3); i++) {
349 uint8_t *data = NULL;
350 int32_t datalen = 0;
351 int32_t expected;
352 if (i == 0) {
353 expected = frame->pitch * frame->h;
354 } else {
355 expected = frame->pitch * (frame->h + 1) / 2;
356 }
357 pAImage_getPlaneData(image, i, &data, &datalen);
358
359 int32_t row_stride = 0;
360 pAImage_getPlaneRowStride(image, i, &row_stride);
361 SDL_assert(row_stride == frame->pitch);
362 SDL_memcpy(dst, data, SDL_min(expected, datalen));
363 dst += expected;
364 }
365 }
366
367 pAImage_delete(image);
368
369 return result;
370}
371
372static void ANDROIDCAMERA_ReleaseFrame(SDL_Camera *device, SDL_Surface *frame)
373{
374 // !!! FIXME: this currently copies the data to the surface, but in theory we could just keep the AImage until ReleaseFrame...
375 SDL_aligned_free(frame->pixels);
376}
377
378static void onImageAvailable(void *context, AImageReader *reader)
379{
380 #if DEBUG_CAMERA
381 SDL_Log("CAMERA: CB onImageAvailable");
382 #endif
383 SDL_Camera *device = (SDL_Camera *) context;
384 SDL_CameraThreadIterate(device);
385}
386
387static void onDisconnected(void *context, ACameraDevice *device)
388{
389 #if DEBUG_CAMERA
390 SDL_Log("CAMERA: CB onDisconnected");
391 #endif
392 SDL_CameraDisconnected((SDL_Camera *) context);
393}
394
395static void onError(void *context, ACameraDevice *device, int error)
396{
397 #if DEBUG_CAMERA
398 SDL_Log("CAMERA: CB onError");
399 #endif
400 SDL_CameraDisconnected((SDL_Camera *) context);
401}
402
403static void onClosed(void* context, ACameraCaptureSession *session)
404{
405 // SDL_Camera *_this = (SDL_Camera *) context;
406 #if DEBUG_CAMERA
407 SDL_Log("CAMERA: CB onClosed");
408 #endif
409}
410
411static void onReady(void* context, ACameraCaptureSession *session)
412{
413 // SDL_Camera *_this = (SDL_Camera *) context;
414 #if DEBUG_CAMERA
415 SDL_Log("CAMERA: CB onReady");
416 #endif
417}
418
419static void onActive(void* context, ACameraCaptureSession *session)
420{
421 // SDL_Camera *_this = (SDL_Camera *) context;
422 #if DEBUG_CAMERA
423 SDL_Log("CAMERA: CB onActive");
424 #endif
425}
426
427static void ANDROIDCAMERA_CloseDevice(SDL_Camera *device)
428{
429 if (device && device->hidden) {
430 struct SDL_PrivateCameraData *hidden = device->hidden;
431 device->hidden = NULL;
432
433 if (hidden->reader) {
434 pAImageReader_setImageListener(hidden->reader, NULL);
435 }
436
437 if (hidden->session) {
438 pACameraCaptureSession_close(hidden->session);
439 }
440
441 if (hidden->request) {
442 pACaptureRequest_free(hidden->request);
443 }
444
445 if (hidden->outputTarget) {
446 pACameraOutputTarget_free(hidden->outputTarget);
447 }
448
449 if (hidden->sessionOutputContainer) {
450 pACaptureSessionOutputContainer_free(hidden->sessionOutputContainer);
451 }
452
453 if (hidden->sessionOutput) {
454 pACaptureSessionOutput_free(hidden->sessionOutput);
455 }
456
457 // we don't free hidden->window here, it'll be cleaned up by AImageReader_delete.
458
459 if (hidden->reader) {
460 pAImageReader_delete(hidden->reader);
461 }
462
463 if (hidden->device) {
464 pACameraDevice_close(hidden->device);
465 }
466
467 SDL_free(hidden);
468 }
469}
470
471// this is where the "opening" of the camera happens, after permission is granted.
472static bool PrepareCamera(SDL_Camera *device)
473{
474 SDL_assert(device->hidden != NULL);
475
476 camera_status_t res;
477 media_status_t res2;
478
479 ACameraDevice_StateCallbacks dev_callbacks;
480 SDL_zero(dev_callbacks);
481 dev_callbacks.context = device;
482 dev_callbacks.onDisconnected = onDisconnected;
483 dev_callbacks.onError = onError;
484
485 ACameraCaptureSession_stateCallbacks capture_callbacks;
486 SDL_zero(capture_callbacks);
487 capture_callbacks.context = device;
488 capture_callbacks.onClosed = onClosed;
489 capture_callbacks.onReady = onReady;
490 capture_callbacks.onActive = onActive;
491
492 AImageReader_ImageListener imglistener;
493 SDL_zero(imglistener);
494 imglistener.context = device;
495 imglistener.onImageAvailable = onImageAvailable;
496
497 // just in case SDL_OpenCamera is overwriting device->spec as CameraPermissionCallback runs, we work from a different copy.
498 const SDL_CameraSpec *spec = &device->hidden->requested_spec;
499
500 if ((res = pACameraManager_openCamera(cameraMgr, (const char *) device->handle, &dev_callbacks, &device->hidden->device)) != ACAMERA_OK) {
501 return SetCameraError("Failed to open camera", res);
502 } else if ((res2 = pAImageReader_new(spec->width, spec->height, format_sdl_to_android(spec->format), 10 /* nb buffers */, &device->hidden->reader)) != AMEDIA_OK) {
503 return SetMediaError("Error AImageReader_new", res2);
504 } else if ((res2 = pAImageReader_getWindow(device->hidden->reader, &device->hidden->window)) != AMEDIA_OK) {
505 return SetMediaError("Error AImageReader_getWindow", res2);
506 } else if ((res = pACaptureSessionOutput_create(device->hidden->window, &device->hidden->sessionOutput)) != ACAMERA_OK) {
507 return SetCameraError("Error ACaptureSessionOutput_create", res);
508 } else if ((res = pACaptureSessionOutputContainer_create(&device->hidden->sessionOutputContainer)) != ACAMERA_OK) {
509 return SetCameraError("Error ACaptureSessionOutputContainer_create", res);
510 } else if ((res = pACaptureSessionOutputContainer_add(device->hidden->sessionOutputContainer, device->hidden->sessionOutput)) != ACAMERA_OK) {
511 return SetCameraError("Error ACaptureSessionOutputContainer_add", res);
512 } else if ((res = pACameraOutputTarget_create(device->hidden->window, &device->hidden->outputTarget)) != ACAMERA_OK) {
513 return SetCameraError("Error ACameraOutputTarget_create", res);
514 } else if ((res = pACameraDevice_createCaptureRequest(device->hidden->device, TEMPLATE_RECORD, &device->hidden->request)) != ACAMERA_OK) {
515 return SetCameraError("Error ACameraDevice_createCaptureRequest", res);
516 } else if ((res = pACaptureRequest_addTarget(device->hidden->request, device->hidden->outputTarget)) != ACAMERA_OK) {
517 return SetCameraError("Error ACaptureRequest_addTarget", res);
518 } else if ((res = pACameraDevice_createCaptureSession(device->hidden->device, device->hidden->sessionOutputContainer, &capture_callbacks, &device->hidden->session)) != ACAMERA_OK) {
519 return SetCameraError("Error ACameraDevice_createCaptureSession", res);
520 } else if ((res = pACameraCaptureSession_setRepeatingRequest(device->hidden->session, NULL, 1, &device->hidden->request, NULL)) != ACAMERA_OK) {
521 return SetCameraError("Error ACameraCaptureSession_setRepeatingRequest", res);
522 } else if ((res2 = pAImageReader_setImageListener(device->hidden->reader, &imglistener)) != AMEDIA_OK) {
523 return SetMediaError("Error AImageReader_setImageListener", res2);
524 }
525
526 return true;
527}
528
529static void SDLCALL CameraPermissionCallback(void *userdata, const char *permission, bool granted)
530{
531 SDL_Camera *device = (SDL_Camera *) userdata;
532 if (device->hidden != NULL) { // if device was already closed, don't send an event.
533 if (!granted) {
534 SDL_CameraPermissionOutcome(device, false); // sorry, permission denied.
535 } else if (!PrepareCamera(device)) { // permission given? Actually open the camera now.
536 // uhoh, setup failed; since the app thinks we already "opened" the device, mark it as disconnected and don't report the permission.
537 SDL_CameraDisconnected(device);
538 } else {
539 // okay! We have permission to use the camera _and_ opening the hardware worked out, report that the camera is usable!
540 SDL_CameraPermissionOutcome(device, true); // go go go!
541 }
542 }
543
544 UnrefPhysicalCamera(device); // we ref'd this in OpenDevice, release the extra reference.
545}
546
547
548static bool ANDROIDCAMERA_OpenDevice(SDL_Camera *device, const SDL_CameraSpec *spec)
549{
550#if 0 // !!! FIXME: for now, we'll just let this fail if it is going to fail, without checking for this
551 /* Cannot open a second camera, while the first one is opened.
552 * If you want to play several camera, they must all be opened first, then played.
553 *
554 * https://developer.android.com/reference/android/hardware/camera2/CameraManager
555 * "All camera devices intended to be operated concurrently, must be opened using openCamera(String, CameraDevice.StateCallback, Handler),
556 * before configuring sessions on any of the camera devices. * "
557 *
558 */
559 if (CheckDevicePlaying()) {
560 return SDL_SetError("A camera is already playing");
561 }
562#endif
563
564 device->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData));
565 if (device->hidden == NULL) {
566 return false;
567 }
568
569 RefPhysicalCamera(device); // ref'd until permission callback fires.
570
571 // just in case SDL_OpenCamera is overwriting device->spec as CameraPermissionCallback runs, we work from a different copy.
572 SDL_copyp(&device->hidden->requested_spec, spec);
573 if (!SDL_RequestAndroidPermission("android.permission.CAMERA", CameraPermissionCallback, device)) {
574 UnrefPhysicalCamera(device);
575 return false;
576 }
577
578 return true; // we don't open the camera until permission is granted, so always succeed for now.
579}
580
581static void ANDROIDCAMERA_FreeDeviceHandle(SDL_Camera *device)
582{
583 if (device) {
584 SDL_free(device->handle);
585 }
586}
587
588static void GatherCameraSpecs(const char *devid, CameraFormatAddData *add_data, char **fullname, SDL_CameraPosition *position)
589{
590 SDL_zerop(add_data);
591
592 ACameraMetadata *metadata = NULL;
593 ACameraMetadata_const_entry cfgentry;
594 ACameraMetadata_const_entry durentry;
595 ACameraMetadata_const_entry infoentry;
596
597 // This can fail with an "unknown error" (with `adb logcat` reporting "no such file or directory")
598 // for "LEGACY" level cameras. I saw this happen on a 30-dollar budget phone I have for testing
599 // (but a different brand budget phone worked, so it's not strictly the low-end of Android devices).
600 // LEGACY devices are seen by onCameraAvailable, but are not otherwise accessible through
601 // libcamera2ndk. The Java camera2 API apparently _can_ access these cameras, but we're going on
602 // without them here for now, in hopes that such hardware is a dying breed.
603 if (pACameraManager_getCameraCharacteristics(cameraMgr, devid, &metadata) != ACAMERA_OK) {
604 return; // oh well.
605 } else if (pACameraMetadata_getConstEntry(metadata, ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &cfgentry) != ACAMERA_OK) {
606 pACameraMetadata_free(metadata);
607 return; // oh well.
608 } else if (pACameraMetadata_getConstEntry(metadata, ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS, &durentry) != ACAMERA_OK) {
609 pACameraMetadata_free(metadata);
610 return; // oh well.
611 }
612
613 *fullname = NULL;
614 if (pACameraMetadata_getConstEntry(metadata, ACAMERA_INFO_VERSION, &infoentry) == ACAMERA_OK) {
615 *fullname = (char *) SDL_malloc(infoentry.count + 1);
616 if (*fullname) {
617 SDL_strlcpy(*fullname, (const char *) infoentry.data.u8, infoentry.count + 1);
618 }
619 }
620
621 ACameraMetadata_const_entry posentry;
622 if (pACameraMetadata_getConstEntry(metadata, ACAMERA_LENS_FACING, &posentry) == ACAMERA_OK) { // ignore this if it fails.
623 if (*posentry.data.u8 == ACAMERA_LENS_FACING_FRONT) {
624 *position = SDL_CAMERA_POSITION_FRONT_FACING;
625 if (!*fullname) {
626 *fullname = SDL_strdup("Front-facing camera");
627 }
628 } else if (*posentry.data.u8 == ACAMERA_LENS_FACING_BACK) {
629 *position = SDL_CAMERA_POSITION_BACK_FACING;
630 if (!*fullname) {
631 *fullname = SDL_strdup("Back-facing camera");
632 }
633 }
634 }
635
636 if (!*fullname) {
637 *fullname = SDL_strdup("Generic camera"); // we tried.
638 }
639
640 const int32_t *i32ptr = cfgentry.data.i32;
641 for (int i = 0; i < cfgentry.count; i++, i32ptr += 4) {
642 const int32_t fmt = i32ptr[0];
643 const int w = i32ptr[1];
644 const int h = i32ptr[2];
645 const int32_t type = i32ptr[3];
646 SDL_PixelFormat sdlfmt = SDL_PIXELFORMAT_UNKNOWN;
647 SDL_Colorspace colorspace = SDL_COLORSPACE_UNKNOWN;
648
649 if (type == ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT) {
650 continue;
651 } else if ((w <= 0) || (h <= 0)) {
652 continue;
653 } else {
654 format_android_to_sdl(fmt, &sdlfmt, &colorspace);
655 if (sdlfmt == SDL_PIXELFORMAT_UNKNOWN) {
656 continue;
657 }
658 }
659
660#if 0 // !!! FIXME: these all come out with 0 durations on my test phone. :(
661 const int64_t *i64ptr = durentry.data.i64;
662 for (int j = 0; j < durentry.count; j++, i64ptr += 4) {
663 const int32_t fpsfmt = (int32_t) i64ptr[0];
664 const int fpsw = (int) i64ptr[1];
665 const int fpsh = (int) i64ptr[2];
666 const long long duration = (long long) i64ptr[3];
667 SDL_Log("CAMERA: possible fps %s %dx%d duration=%lld", SDL_GetPixelFormatName(sdlfmt), fpsw, fpsh, duration);
668 if ((duration > 0) && (fpsfmt == fmt) && (fpsw == w) && (fpsh == h)) {
669 SDL_AddCameraFormat(add_data, sdlfmt, colorspace, w, h, 1000000000, duration);
670 }
671 }
672#else
673 SDL_AddCameraFormat(add_data, sdlfmt, colorspace, w, h, 30, 1);
674#endif
675 }
676
677 pACameraMetadata_free(metadata);
678}
679
680static bool FindAndroidCameraByID(SDL_Camera *device, void *userdata)
681{
682 const char *devid = (const char *) userdata;
683 return (SDL_strcmp(devid, (const char *) device->handle) == 0);
684}
685
686static void MaybeAddDevice(const char *devid)
687{
688 #if DEBUG_CAMERA
689 SDL_Log("CAMERA: MaybeAddDevice('%s')", devid);
690 #endif
691
692 if (SDL_FindPhysicalCameraByCallback(FindAndroidCameraByID, (void *) devid)) {
693 return; // already have this one.
694 }
695
696 SDL_CameraPosition position = SDL_CAMERA_POSITION_UNKNOWN;
697 char *fullname = NULL;
698 CameraFormatAddData add_data;
699 GatherCameraSpecs(devid, &add_data, &fullname, &position);
700 if (add_data.num_specs > 0) {
701 char *namecpy = SDL_strdup(devid);
702 if (namecpy) {
703 SDL_Camera *device = SDL_AddCamera(fullname, position, add_data.num_specs, add_data.specs, namecpy);
704 if (!device) {
705 SDL_free(namecpy);
706 }
707 }
708 }
709
710 SDL_free(fullname);
711 SDL_free(add_data.specs);
712}
713
714// note that camera "availability" covers both hotplugging and whether another
715// has the device opened, but for something like Android, it's probably fine
716// to treat both unplugging and loss of access as disconnection events. When
717// the other app closes the camera, we get an available event as if it was
718// just plugged back in.
719
720static void onCameraAvailable(void *context, const char *cameraId)
721{
722 #if DEBUG_CAMERA
723 SDL_Log("CAMERA: CB onCameraAvailable('%s')", cameraId);
724 #endif
725 SDL_assert(cameraId != NULL);
726 MaybeAddDevice(cameraId);
727}
728
729static void onCameraUnavailable(void *context, const char *cameraId)
730{
731 #if DEBUG_CAMERA
732 SDL_Log("CAMERA: CB onCameraUnvailable('%s')", cameraId);
733 #endif
734
735 SDL_assert(cameraId != NULL);
736
737 // THIS CALLBACK FIRES WHEN YOU OPEN THE DEVICE YOURSELF. :(
738 // Make sure we don't have the device opened, in which case onDisconnected will fire instead if actually lost.
739 SDL_Camera *device = SDL_FindPhysicalCameraByCallback(FindAndroidCameraByID, (void *) cameraId);
740 if (device && !device->hidden) {
741 SDL_CameraDisconnected(device);
742 }
743}
744
745static const ACameraManager_AvailabilityCallbacks camera_availability_listener = {
746 NULL,
747 onCameraAvailable,
748 onCameraUnavailable
749};
750
751static void ANDROIDCAMERA_DetectDevices(void)
752{
753 ACameraIdList *list = NULL;
754 camera_status_t res = pACameraManager_getCameraIdList(cameraMgr, &list);
755
756 if ((res == ACAMERA_OK) && list) {
757 const int total = list->numCameras;
758 for (int i = 0; i < total; i++) {
759 MaybeAddDevice(list->cameraIds[i]);
760 }
761
762 pACameraManager_deleteCameraIdList(list);
763 }
764
765 pACameraManager_registerAvailabilityCallback(cameraMgr, &camera_availability_listener);
766}
767
768static void ANDROIDCAMERA_Deinitialize(void)
769{
770 pACameraManager_unregisterAvailabilityCallback(cameraMgr, &camera_availability_listener);
771 DestroyCameraManager();
772
773 dlclose(libcamera2ndk);
774 libcamera2ndk = NULL;
775 pACameraManager_create = NULL;
776 pACameraManager_registerAvailabilityCallback = NULL;
777 pACameraManager_unregisterAvailabilityCallback = NULL;
778 pACameraManager_getCameraIdList = NULL;
779 pACameraManager_deleteCameraIdList = NULL;
780 pACameraCaptureSession_close = NULL;
781 pACaptureRequest_free = NULL;
782 pACameraOutputTarget_free = NULL;
783 pACameraDevice_close = NULL;
784 pACameraManager_delete = NULL;
785 pACaptureSessionOutputContainer_free = NULL;
786 pACaptureSessionOutput_free = NULL;
787 pACameraManager_openCamera = NULL;
788 pACameraDevice_createCaptureRequest = NULL;
789 pACameraDevice_createCaptureSession = NULL;
790 pACameraManager_getCameraCharacteristics = NULL;
791 pACameraMetadata_free = NULL;
792 pACameraMetadata_getConstEntry = NULL;
793 pACameraCaptureSession_setRepeatingRequest = NULL;
794 pACameraOutputTarget_create = NULL;
795 pACaptureRequest_addTarget = NULL;
796 pACaptureSessionOutputContainer_add = NULL;
797 pACaptureSessionOutputContainer_create = NULL;
798 pACaptureSessionOutput_create = NULL;
799
800 dlclose(libmediandk);
801 libmediandk = NULL;
802 pAImage_delete = NULL;
803 pAImage_getTimestamp = NULL;
804 pAImage_getNumberOfPlanes = NULL;
805 pAImage_getPlaneRowStride = NULL;
806 pAImage_getPlaneData = NULL;
807 pAImageReader_acquireNextImage = NULL;
808 pAImageReader_delete = NULL;
809 pAImageReader_setImageListener = NULL;
810 pAImageReader_getWindow = NULL;
811 pAImageReader_new = NULL;
812}
813
814static bool ANDROIDCAMERA_Init(SDL_CameraDriverImpl *impl)
815{
816 // !!! FIXME: slide this off into a subroutine
817 // system libraries are in android-24 and later; we currently target android-16 and later, so check if they exist at runtime.
818 void *libcamera2 = dlopen("libcamera2ndk.so", RTLD_NOW | RTLD_LOCAL);
819 if (!libcamera2) {
820 SDL_Log("CAMERA: libcamera2ndk.so can't be loaded: %s", dlerror());
821 return false;
822 }
823
824 void *libmedia = dlopen("libmediandk.so", RTLD_NOW | RTLD_LOCAL);
825 if (!libmedia) {
826 SDL_Log("CAMERA: libmediandk.so can't be loaded: %s", dlerror());
827 dlclose(libcamera2);
828 return false;
829 }
830
831 bool okay = true;
832 #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; } }
833 //#define LOADSYM(lib, fn) p##fn = (pfn##fn) fn
834 LOADSYM(libcamera2, ACameraManager_create);
835 LOADSYM(libcamera2, ACameraManager_registerAvailabilityCallback);
836 LOADSYM(libcamera2, ACameraManager_unregisterAvailabilityCallback);
837 LOADSYM(libcamera2, ACameraManager_getCameraIdList);
838 LOADSYM(libcamera2, ACameraManager_deleteCameraIdList);
839 LOADSYM(libcamera2, ACameraCaptureSession_close);
840 LOADSYM(libcamera2, ACaptureRequest_free);
841 LOADSYM(libcamera2, ACameraOutputTarget_free);
842 LOADSYM(libcamera2, ACameraDevice_close);
843 LOADSYM(libcamera2, ACameraManager_delete);
844 LOADSYM(libcamera2, ACaptureSessionOutputContainer_free);
845 LOADSYM(libcamera2, ACaptureSessionOutput_free);
846 LOADSYM(libcamera2, ACameraManager_openCamera);
847 LOADSYM(libcamera2, ACameraDevice_createCaptureRequest);
848 LOADSYM(libcamera2, ACameraDevice_createCaptureSession);
849 LOADSYM(libcamera2, ACameraManager_getCameraCharacteristics);
850 LOADSYM(libcamera2, ACameraMetadata_free);
851 LOADSYM(libcamera2, ACameraMetadata_getConstEntry);
852 LOADSYM(libcamera2, ACameraCaptureSession_setRepeatingRequest);
853 LOADSYM(libcamera2, ACameraOutputTarget_create);
854 LOADSYM(libcamera2, ACaptureRequest_addTarget);
855 LOADSYM(libcamera2, ACaptureSessionOutputContainer_add);
856 LOADSYM(libcamera2, ACaptureSessionOutputContainer_create);
857 LOADSYM(libcamera2, ACaptureSessionOutput_create);
858 LOADSYM(libmedia, AImage_delete);
859 LOADSYM(libmedia, AImage_getTimestamp);
860 LOADSYM(libmedia, AImage_getNumberOfPlanes);
861 LOADSYM(libmedia, AImage_getPlaneRowStride);
862 LOADSYM(libmedia, AImage_getPlaneData);
863 LOADSYM(libmedia, AImageReader_acquireNextImage);
864 LOADSYM(libmedia, AImageReader_delete);
865 LOADSYM(libmedia, AImageReader_setImageListener);
866 LOADSYM(libmedia, AImageReader_getWindow);
867 LOADSYM(libmedia, AImageReader_new);
868 LOADSYM(libmedia, AImage_getWidth);
869 LOADSYM(libmedia, AImage_getHeight);
870
871 #undef LOADSYM
872
873 if (!okay) {
874 dlclose(libmedia);
875 dlclose(libcamera2);
876 }
877
878 if (!CreateCameraManager()) {
879 dlclose(libmedia);
880 dlclose(libcamera2);
881 return false;
882 }
883
884 libcamera2ndk = libcamera2;
885 libmediandk = libmedia;
886
887 impl->DetectDevices = ANDROIDCAMERA_DetectDevices;
888 impl->OpenDevice = ANDROIDCAMERA_OpenDevice;
889 impl->CloseDevice = ANDROIDCAMERA_CloseDevice;
890 impl->WaitDevice = ANDROIDCAMERA_WaitDevice;
891 impl->AcquireFrame = ANDROIDCAMERA_AcquireFrame;
892 impl->ReleaseFrame = ANDROIDCAMERA_ReleaseFrame;
893 impl->FreeDeviceHandle = ANDROIDCAMERA_FreeDeviceHandle;
894 impl->Deinitialize = ANDROIDCAMERA_Deinitialize;
895
896 impl->ProvidesOwnCallbackThread = true;
897
898 return true;
899}
900
901CameraBootStrap ANDROIDCAMERA_bootstrap = {
902 "android", "SDL Android camera driver", ANDROIDCAMERA_Init, false
903};
904
905#endif