2023-11-09 11:11:07 +01:00
/*
Simple DirectMedia Layer
2024-01-01 13:15:26 -08:00
Copyright ( C ) 1997 - 2024 Sam Lantinga < slouken @ libsdl . org >
2023-11-09 11:11:07 +01:00
This software is provided ' as - is ' , without any express or implied
warranty . In no event will the authors be held liable for any damages
arising from the use of this software .
Permission is granted to anyone to use this software for any purpose ,
including commercial applications , and to alter it and redistribute it
freely , subject to the following restrictions :
1. The origin of this software must not be misrepresented ; you must not
claim that you wrote the original software . If you use this software
in a product , an acknowledgment in the product documentation would be
appreciated but is not required .
2. Altered source versions must be plainly marked as such , and must not be
misrepresented as being the original software .
3. This notice may not be removed or altered from any source distribution .
*/
# include "SDL_internal.h"
2023-11-27 23:05:54 -05:00
# include "../SDL_syscamera.h"
# include "../SDL_camera_c.h"
# include "../../video/SDL_pixels_c.h"
2023-11-09 11:11:07 +01:00
# include "../../thread/SDL_systhread.h"
2024-02-18 00:50:32 -05:00
# ifdef SDL_CAMERA_DRIVER_ANDROID
2023-11-09 11:11:07 +01:00
/*
* AndroidManifest . xml :
* < uses - permission android : name = " android.permission.CAMERA " > < / uses - permission >
* < uses - feature android : name = " android.hardware.camera " / >
*
* Very likely SDL must be build with YUV support ( done by default )
*
* https : //developer.android.com/reference/android/hardware/camera2/CameraManager
* " All camera devices intended to be operated concurrently, must be opened using openCamera(String, CameraDevice.StateCallback, Handler),
2024-02-18 00:50:32 -05:00
* before configuring sessions on any of the camera devices . "
2023-11-09 11:11:07 +01:00
*/
2024-02-18 00:50:32 -05:00
// this is kinda gross, but on older NDK headers all the camera stuff is
// gated behind __ANDROID_API__. We'll dlopen() it at runtime, so we'll do
// the right thing on pre-Android 7.0 devices, but we still
// need the struct declarations and such in those headers.
// The other option is to make a massive jump in minimum Android version we
// support--going from ancient to merely really old--but this seems less
// distasteful and using dlopen matches practices on other SDL platforms.
// We'll see if it works out.
# if __ANDROID_API__ < 24
# undef __ANDROID_API__
# define __ANDROID_API__ 24
# endif
# include <dlfcn.h>
2023-11-09 11:11:07 +01:00
# include <camera/NdkCameraDevice.h>
# include <camera/NdkCameraManager.h>
# include <media/NdkImage.h>
# include <media/NdkImageReader.h>
# include "../../core/android/SDL_android.h"
2024-02-18 00:50:32 -05:00
static void * libcamera2ndk = NULL ;
typedef ACameraManager * ( * pfnACameraManager_create ) ( void ) ;
typedef camera_status_t ( * pfnACameraManager_registerAvailabilityCallback ) ( ACameraManager * , const ACameraManager_AvailabilityCallbacks * ) ;
typedef camera_status_t ( * pfnACameraManager_unregisterAvailabilityCallback ) ( ACameraManager * , const ACameraManager_AvailabilityCallbacks * ) ;
typedef camera_status_t ( * pfnACameraManager_getCameraIdList ) ( ACameraManager * , ACameraIdList * * ) ;
typedef void ( * pfnACameraManager_deleteCameraIdList ) ( ACameraIdList * ) ;
typedef void ( * pfnACameraCaptureSession_close ) ( ACameraCaptureSession * ) ;
typedef void ( * pfnACaptureRequest_free ) ( ACaptureRequest * ) ;
typedef void ( * pfnACameraOutputTarget_free ) ( ACameraOutputTarget * ) ;
typedef camera_status_t ( * pfnACameraDevice_close ) ( ACameraDevice * ) ;
typedef void ( * pfnACameraManager_delete ) ( ACameraManager * ) ;
typedef void ( * pfnACaptureSessionOutputContainer_free ) ( ACaptureSessionOutputContainer * ) ;
typedef void ( * pfnACaptureSessionOutput_free ) ( ACaptureSessionOutput * ) ;
typedef camera_status_t ( * pfnACameraManager_openCamera ) ( ACameraManager * , const char * , ACameraDevice_StateCallbacks * , ACameraDevice * * ) ;
typedef camera_status_t ( * pfnACameraDevice_createCaptureRequest ) ( const ACameraDevice * , ACameraDevice_request_template , ACaptureRequest * * ) ;
typedef camera_status_t ( * pfnACameraDevice_createCaptureSession ) ( ACameraDevice * , const ACaptureSessionOutputContainer * , const ACameraCaptureSession_stateCallbacks * , ACameraCaptureSession * * ) ;
typedef camera_status_t ( * pfnACameraManager_getCameraCharacteristics ) ( ACameraManager * , const char * , ACameraMetadata * * ) ;
typedef void ( * pfnACameraMetadata_free ) ( ACameraMetadata * ) ;
typedef camera_status_t ( * pfnACameraMetadata_getConstEntry ) ( const ACameraMetadata * , uint32_t tag , ACameraMetadata_const_entry * ) ;
typedef camera_status_t ( * pfnACameraCaptureSession_setRepeatingRequest ) ( ACameraCaptureSession * , ACameraCaptureSession_captureCallbacks * , int numRequests , ACaptureRequest * * , int * ) ;
typedef camera_status_t ( * pfnACameraOutputTarget_create ) ( ACameraWindowType * , ACameraOutputTarget * * ) ;
typedef camera_status_t ( * pfnACaptureRequest_addTarget ) ( ACaptureRequest * , const ACameraOutputTarget * ) ;
typedef camera_status_t ( * pfnACaptureSessionOutputContainer_add ) ( ACaptureSessionOutputContainer * , const ACaptureSessionOutput * ) ;
typedef camera_status_t ( * pfnACaptureSessionOutputContainer_create ) ( ACaptureSessionOutputContainer * * ) ;
typedef camera_status_t ( * pfnACaptureSessionOutput_create ) ( ACameraWindowType * , ACaptureSessionOutput * * ) ;
static pfnACameraManager_create pACameraManager_create = NULL ;
static pfnACameraManager_registerAvailabilityCallback pACameraManager_registerAvailabilityCallback = NULL ;
static pfnACameraManager_unregisterAvailabilityCallback pACameraManager_unregisterAvailabilityCallback = NULL ;
static pfnACameraManager_getCameraIdList pACameraManager_getCameraIdList = NULL ;
static pfnACameraManager_deleteCameraIdList pACameraManager_deleteCameraIdList = NULL ;
static pfnACameraCaptureSession_close pACameraCaptureSession_close = NULL ;
static pfnACaptureRequest_free pACaptureRequest_free = NULL ;
static pfnACameraOutputTarget_free pACameraOutputTarget_free = NULL ;
static pfnACameraDevice_close pACameraDevice_close = NULL ;
static pfnACameraManager_delete pACameraManager_delete = NULL ;
static pfnACaptureSessionOutputContainer_free pACaptureSessionOutputContainer_free = NULL ;
static pfnACaptureSessionOutput_free pACaptureSessionOutput_free = NULL ;
static pfnACameraManager_openCamera pACameraManager_openCamera = NULL ;
static pfnACameraDevice_createCaptureRequest pACameraDevice_createCaptureRequest = NULL ;
static pfnACameraDevice_createCaptureSession pACameraDevice_createCaptureSession = NULL ;
static pfnACameraManager_getCameraCharacteristics pACameraManager_getCameraCharacteristics = NULL ;
static pfnACameraMetadata_free pACameraMetadata_free = NULL ;
static pfnACameraMetadata_getConstEntry pACameraMetadata_getConstEntry = NULL ;
static pfnACameraCaptureSession_setRepeatingRequest pACameraCaptureSession_setRepeatingRequest = NULL ;
static pfnACameraOutputTarget_create pACameraOutputTarget_create = NULL ;
static pfnACaptureRequest_addTarget pACaptureRequest_addTarget = NULL ;
static pfnACaptureSessionOutputContainer_add pACaptureSessionOutputContainer_add = NULL ;
static pfnACaptureSessionOutputContainer_create pACaptureSessionOutputContainer_create = NULL ;
static pfnACaptureSessionOutput_create pACaptureSessionOutput_create = NULL ;
static void * libmediandk = NULL ;
typedef void ( * pfnAImage_delete ) ( AImage * ) ;
typedef media_status_t ( * pfnAImage_getTimestamp ) ( const AImage * , int64_t * ) ;
typedef media_status_t ( * pfnAImage_getNumberOfPlanes ) ( const AImage * , int32_t * ) ;
typedef media_status_t ( * pfnAImage_getPlaneRowStride ) ( const AImage * , int , int32_t * ) ;
typedef media_status_t ( * pfnAImage_getPlaneData ) ( const AImage * , int , uint8_t * * , int * ) ;
typedef media_status_t ( * pfnAImageReader_acquireNextImage ) ( AImageReader * , AImage * * ) ;
typedef void ( * pfnAImageReader_delete ) ( AImageReader * ) ;
typedef media_status_t ( * pfnAImageReader_setImageListener ) ( AImageReader * , AImageReader_ImageListener * ) ;
typedef media_status_t ( * pfnAImageReader_getWindow ) ( AImageReader * , ANativeWindow * * ) ;
typedef media_status_t ( * pfnAImageReader_new ) ( int32_t , int32_t , int32_t , int32_t , AImageReader * * ) ;
static pfnAImage_delete pAImage_delete = NULL ;
static pfnAImage_getTimestamp pAImage_getTimestamp = NULL ;
static pfnAImage_getNumberOfPlanes pAImage_getNumberOfPlanes = NULL ;
static pfnAImage_getPlaneRowStride pAImage_getPlaneRowStride = NULL ;
static pfnAImage_getPlaneData pAImage_getPlaneData = NULL ;
static pfnAImageReader_acquireNextImage pAImageReader_acquireNextImage = NULL ;
static pfnAImageReader_delete pAImageReader_delete = NULL ;
static pfnAImageReader_setImageListener pAImageReader_setImageListener = NULL ;
static pfnAImageReader_getWindow pAImageReader_getWindow = NULL ;
static pfnAImageReader_new pAImageReader_new = NULL ;
typedef media_status_t ( * pfnAImage_getWidth ) ( const AImage * , int32_t * ) ;
typedef media_status_t ( * pfnAImage_getHeight ) ( const AImage * , int32_t * ) ;
static pfnAImage_getWidth pAImage_getWidth = NULL ;
static pfnAImage_getHeight pAImage_getHeight = NULL ;
2023-11-09 11:11:07 +01:00
2024-02-18 00:50:32 -05:00
struct SDL_PrivateCameraData
{
ACameraDevice * device ;
AImageReader * reader ;
ANativeWindow * window ;
ACaptureSessionOutput * sessionOutput ;
ACaptureSessionOutputContainer * sessionOutputContainer ;
ACameraOutputTarget * outputTarget ;
ACaptureRequest * request ;
ACameraCaptureSession * session ;
SDL_CameraSpec requested_spec ;
} ;
2023-11-09 11:11:07 +01:00
2024-02-18 00:50:32 -05:00
static int SetErrorStr ( const char * what , const char * errstr , const int rc )
2023-11-09 11:11:07 +01:00
{
2024-02-18 00:50:32 -05:00
char errbuf [ 128 ] ;
if ( ! errstr ) {
SDL_snprintf ( errbuf , sizeof ( errbuf ) , " Unknown error #%d " , rc ) ;
errstr = errbuf ;
2023-11-09 11:11:07 +01:00
}
2024-02-18 00:50:32 -05:00
return SDL_SetError ( " %s: %s " , what , errstr ) ;
}
2023-12-01 10:59:13 -05:00
2024-02-18 00:50:32 -05:00
static const char * CameraStatusStr ( const camera_status_t rc )
{
switch ( rc ) {
case ACAMERA_OK : return " no error " ;
case ACAMERA_ERROR_UNKNOWN : return " unknown error " ;
case ACAMERA_ERROR_INVALID_PARAMETER : return " invalid parameter " ;
case ACAMERA_ERROR_CAMERA_DISCONNECTED : return " camera disconnected " ;
case ACAMERA_ERROR_NOT_ENOUGH_MEMORY : return " not enough memory " ;
case ACAMERA_ERROR_METADATA_NOT_FOUND : return " metadata not found " ;
case ACAMERA_ERROR_CAMERA_DEVICE : return " camera device error " ;
case ACAMERA_ERROR_CAMERA_SERVICE : return " camera service error " ;
case ACAMERA_ERROR_SESSION_CLOSED : return " session closed " ;
case ACAMERA_ERROR_INVALID_OPERATION : return " invalid operation " ;
case ACAMERA_ERROR_STREAM_CONFIGURE_FAIL : return " configure failure " ;
case ACAMERA_ERROR_CAMERA_IN_USE : return " camera in use " ;
case ACAMERA_ERROR_MAX_CAMERA_IN_USE : return " max cameras in use " ;
case ACAMERA_ERROR_CAMERA_DISABLED : return " camera disabled " ;
case ACAMERA_ERROR_PERMISSION_DENIED : return " permission denied " ;
case ACAMERA_ERROR_UNSUPPORTED_OPERATION : return " unsupported operation " ;
default : break ;
}
return NULL ; // unknown error
2023-11-09 11:11:07 +01:00
}
2024-02-18 00:50:32 -05:00
static int SetCameraError ( const char * what , const camera_status_t rc )
2023-11-09 11:11:07 +01:00
{
2024-02-18 00:50:32 -05:00
return SetErrorStr ( what , CameraStatusStr ( rc ) , ( int ) rc ) ;
}
2023-11-09 11:11:07 +01:00
2024-02-18 00:50:32 -05:00
static const char * MediaStatusStr ( const media_status_t rc )
{
switch ( rc ) {
case AMEDIA_OK : return " no error " ;
case AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE : return " insuffient resources " ;
case AMEDIACODEC_ERROR_RECLAIMED : return " reclaimed " ;
case AMEDIA_ERROR_UNKNOWN : return " unknown error " ;
case AMEDIA_ERROR_MALFORMED : return " malformed " ;
case AMEDIA_ERROR_UNSUPPORTED : return " unsupported " ;
case AMEDIA_ERROR_INVALID_OBJECT : return " invalid object " ;
case AMEDIA_ERROR_INVALID_PARAMETER : return " invalid parameter " ;
case AMEDIA_ERROR_INVALID_OPERATION : return " invalid operation " ;
case AMEDIA_ERROR_END_OF_STREAM : return " end of stream " ;
case AMEDIA_ERROR_IO : return " i/o error " ;
case AMEDIA_ERROR_WOULD_BLOCK : return " operation would block " ;
case AMEDIA_DRM_NOT_PROVISIONED : return " DRM not provisioned " ;
case AMEDIA_DRM_RESOURCE_BUSY : return " DRM resource busy " ;
case AMEDIA_DRM_DEVICE_REVOKED : return " DRM device revoked " ;
case AMEDIA_DRM_SHORT_BUFFER : return " DRM short buffer " ;
case AMEDIA_DRM_SESSION_NOT_OPENED : return " DRM session not opened " ;
case AMEDIA_DRM_TAMPER_DETECTED : return " DRM tampering detected " ;
case AMEDIA_DRM_VERIFY_FAILED : return " DRM verify failed " ;
case AMEDIA_DRM_NEED_KEY : return " DRM need key " ;
case AMEDIA_DRM_LICENSE_EXPIRED : return " DRM license expired " ;
case AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE : return " no buffer available " ;
case AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED : return " maximum images acquired " ;
case AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE : return " cannot lock image " ;
case AMEDIA_IMGREADER_CANNOT_UNLOCK_IMAGE : return " cannot unlock image " ;
case AMEDIA_IMGREADER_IMAGE_NOT_LOCKED : return " image not locked " ;
default : break ;
}
return NULL ; // unknown error
2023-11-09 11:11:07 +01:00
}
2024-02-18 00:50:32 -05:00
static int SetMediaError ( const char * what , const media_status_t rc )
2023-11-09 11:11:07 +01:00
{
2024-02-18 00:50:32 -05:00
return SetErrorStr ( what , MediaStatusStr ( rc ) , ( int ) rc ) ;
}
2023-11-09 11:11:07 +01:00
2024-02-18 00:50:32 -05:00
static ACameraManager * cameraMgr = NULL ;
2023-11-09 11:11:07 +01:00
2024-02-18 00:50:32 -05:00
static int CreateCameraManager ( void )
{
SDL_assert ( cameraMgr = = NULL ) ;
cameraMgr = pACameraManager_create ( ) ;
if ( ! cameraMgr ) {
return SDL_SetError ( " Error creating ACameraManager " ) ;
2023-11-09 11:11:07 +01:00
}
2024-02-18 00:50:32 -05:00
return 0 ;
2023-11-09 11:11:07 +01:00
}
2024-02-18 00:50:32 -05:00
static void DestroyCameraManager ( void )
{
if ( cameraMgr ) {
pACameraManager_delete ( cameraMgr ) ;
cameraMgr = NULL ;
2023-11-09 11:11:07 +01:00
}
}
2023-11-28 23:03:19 -05:00
static Uint32 format_android_to_sdl ( Uint32 fmt )
2023-11-09 11:11:07 +01:00
{
switch ( fmt ) {
2023-11-28 23:03:19 -05:00
# define CASE(x, y) case x: return y
2024-02-18 00:50:32 -05:00
CASE ( AIMAGE_FORMAT_YUV_420_888 , SDL_PIXELFORMAT_NV12 ) ;
2023-11-09 11:11:07 +01:00
CASE ( AIMAGE_FORMAT_RGB_565 , SDL_PIXELFORMAT_RGB565 ) ;
2023-12-30 11:47:56 -08:00
CASE ( AIMAGE_FORMAT_RGB_888 , SDL_PIXELFORMAT_XRGB8888 ) ;
2023-11-09 11:11:07 +01:00
CASE ( AIMAGE_FORMAT_RGBA_8888 , SDL_PIXELFORMAT_RGBA8888 ) ;
CASE ( AIMAGE_FORMAT_RGBX_8888 , SDL_PIXELFORMAT_RGBX8888 ) ;
2024-02-18 00:50:32 -05:00
//CASE(AIMAGE_FORMAT_RGBA_FP16, SDL_PIXELFORMAT_UNKNOWN); // 64bits
//CASE(AIMAGE_FORMAT_RAW_PRIVATE, SDL_PIXELFORMAT_UNKNOWN);
//CASE(AIMAGE_FORMAT_JPEG, SDL_PIXELFORMAT_UNKNOWN);
2023-11-28 23:03:19 -05:00
# undef CASE
2024-02-18 00:50:32 -05:00
default : break ;
2023-11-09 11:11:07 +01:00
}
2024-02-18 00:50:32 -05:00
# if DEBUG_CAMERA
//SDL_Log("Unknown format AIMAGE_FORMAT '%d'", fmt);
# endif
return SDL_PIXELFORMAT_UNKNOWN ;
2023-11-09 11:11:07 +01:00
}
2023-11-28 23:03:19 -05:00
static Uint32 format_sdl_to_android ( Uint32 fmt )
2023-11-09 11:11:07 +01:00
{
switch ( fmt ) {
2023-11-28 23:03:19 -05:00
# define CASE(x, y) case y: return x
2024-02-18 00:50:32 -05:00
CASE ( AIMAGE_FORMAT_YUV_420_888 , SDL_PIXELFORMAT_NV12 ) ;
2023-11-09 11:11:07 +01:00
CASE ( AIMAGE_FORMAT_RGB_565 , SDL_PIXELFORMAT_RGB565 ) ;
2023-12-30 11:47:56 -08:00
CASE ( AIMAGE_FORMAT_RGB_888 , SDL_PIXELFORMAT_XRGB8888 ) ;
2023-11-09 11:11:07 +01:00
CASE ( AIMAGE_FORMAT_RGBA_8888 , SDL_PIXELFORMAT_RGBA8888 ) ;
CASE ( AIMAGE_FORMAT_RGBX_8888 , SDL_PIXELFORMAT_RGBX8888 ) ;
2023-11-28 23:03:19 -05:00
# undef CASE
2023-11-09 11:11:07 +01:00
default :
return 0 ;
}
}
2024-02-18 00:50:32 -05:00
static int ANDROIDCAMERA_WaitDevice ( SDL_CameraDevice * device )
{
return 0 ; // this isn't used atm, since we run our own thread via onImageAvailable callbacks.
}
static int ANDROIDCAMERA_AcquireFrame ( SDL_CameraDevice * device , SDL_Surface * frame , Uint64 * timestampNS )
{
int retval = 1 ;
media_status_t res ;
AImage * image = NULL ;
res = pAImageReader_acquireNextImage ( device - > hidden - > reader , & image ) ;
// We could also use this one:
//res = AImageReader_acquireLatestImage(device->hidden->reader, &image);
SDL_assert ( res ! = AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE ) ; // we should only be here if onImageAvailable was called.
if ( res ! = AMEDIA_OK ) {
return SetMediaError ( " Error AImageReader_acquireNextImage " , res ) ;
}
int64_t atimestamp = 0 ;
if ( pAImage_getTimestamp ( image , & atimestamp ) = = AMEDIA_OK ) {
* timestampNS = ( Uint64 ) atimestamp ;
} else {
* timestampNS = 0 ;
}
// !!! 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...
int32_t num_planes = 0 ;
pAImage_getNumberOfPlanes ( image , & num_planes ) ;
if ( ( num_planes = = 3 ) & & ( device - > spec . format = = SDL_PIXELFORMAT_NV12 ) ) {
num_planes - - ; // treat the interleaved planes as one.
}
// !!! 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.
size_t buflen = 0 ;
for ( int i = 0 ; ( i < num_planes ) & & ( i < 3 ) ; i + + ) {
uint8_t * data = NULL ;
int32_t datalen = 0 ;
pAImage_getPlaneData ( image , i , & data , & datalen ) ;
buflen + = ( int ) datalen ;
}
frame - > pixels = SDL_aligned_alloc ( SDL_SIMDGetAlignment ( ) , buflen ) ;
if ( frame - > pixels = = NULL ) {
retval = - 1 ;
} else {
int32_t row_stride = 0 ;
Uint8 * dst = frame - > pixels ;
pAImage_getPlaneRowStride ( image , 0 , & row_stride ) ;
frame - > pitch = ( int ) row_stride ; // this is what SDL3 currently expects, probably incorrectly.
for ( int i = 0 ; ( i < num_planes ) & & ( i < 3 ) ; i + + ) {
uint8_t * data = NULL ;
int32_t datalen = 0 ;
pAImage_getPlaneData ( image , i , & data , & datalen ) ;
const void * src = data ;
SDL_memcpy ( dst , src , datalen ) ;
dst + = datalen ;
}
}
pAImage_delete ( image ) ;
return retval ;
}
static void ANDROIDCAMERA_ReleaseFrame ( SDL_CameraDevice * device , SDL_Surface * frame )
{
// !!! FIXME: this currently copies the data to the surface, but in theory we could just keep the AImage until ReleaseFrame...
SDL_aligned_free ( frame - > pixels ) ;
}
static void onImageAvailable ( void * context , AImageReader * reader )
{
# if DEBUG_CAMERA
SDL_Log ( " CAMERA: CB onImageAvailable " ) ;
# endif
SDL_CameraDevice * device = ( SDL_CameraDevice * ) context ;
SDL_CameraThreadIterate ( device ) ;
}
2023-11-09 11:11:07 +01:00
2023-11-28 23:03:19 -05:00
static void onDisconnected ( void * context , ACameraDevice * device )
2023-11-09 11:11:07 +01:00
{
2023-11-27 23:05:54 -05:00
// SDL_CameraDevice *_this = (SDL_CameraDevice *) context;
2023-11-28 23:03:19 -05:00
# if DEBUG_CAMERA
2024-02-18 00:50:32 -05:00
SDL_Log ( " CAMERA: CB onDisconnected " ) ;
2023-11-28 23:03:19 -05:00
# endif
2023-11-09 11:11:07 +01:00
}
2023-11-28 23:03:19 -05:00
static void onError ( void * context , ACameraDevice * device , int error )
2023-11-09 11:11:07 +01:00
{
2023-11-27 23:05:54 -05:00
// SDL_CameraDevice *_this = (SDL_CameraDevice *) context;
2023-11-28 23:03:19 -05:00
# if DEBUG_CAMERA
2024-02-18 00:50:32 -05:00
SDL_Log ( " CAMERA: CB onError " ) ;
2023-11-28 23:03:19 -05:00
# endif
2023-11-09 11:11:07 +01:00
}
2023-11-28 23:03:19 -05:00
static void onClosed ( void * context , ACameraCaptureSession * session )
2023-11-09 11:11:07 +01:00
{
2023-11-27 23:05:54 -05:00
// SDL_CameraDevice *_this = (SDL_CameraDevice *) context;
2023-11-28 23:03:19 -05:00
# if DEBUG_CAMERA
2024-02-18 00:50:32 -05:00
SDL_Log ( " CAMERA: CB onClosed " ) ;
2023-11-28 23:03:19 -05:00
# endif
2023-11-09 11:11:07 +01:00
}
2023-11-28 23:03:19 -05:00
static void onReady ( void * context , ACameraCaptureSession * session )
2023-11-09 11:11:07 +01:00
{
2023-11-27 23:05:54 -05:00
// SDL_CameraDevice *_this = (SDL_CameraDevice *) context;
2023-11-28 23:03:19 -05:00
# if DEBUG_CAMERA
2024-02-18 00:50:32 -05:00
SDL_Log ( " CAMERA: CB onReady " ) ;
2023-11-28 23:03:19 -05:00
# endif
2023-11-09 11:11:07 +01:00
}
2023-11-28 23:03:19 -05:00
static void onActive ( void * context , ACameraCaptureSession * session )
2023-11-09 11:11:07 +01:00
{
2023-11-27 23:05:54 -05:00
// SDL_CameraDevice *_this = (SDL_CameraDevice *) context;
2023-11-28 23:03:19 -05:00
# if DEBUG_CAMERA
2024-02-18 00:50:32 -05:00
SDL_Log ( " CAMERA: CB onActive " ) ;
2023-11-28 23:03:19 -05:00
# endif
2023-11-09 11:11:07 +01:00
}
2024-02-18 00:50:32 -05:00
static void ANDROIDCAMERA_CloseDevice ( SDL_CameraDevice * device )
2023-11-09 11:11:07 +01:00
{
2024-02-18 00:50:32 -05:00
if ( device & & device - > hidden ) {
struct SDL_PrivateCameraData * hidden = device - > hidden ;
device - > hidden = NULL ;
2023-11-09 11:11:07 +01:00
2024-02-18 00:50:32 -05:00
if ( hidden - > reader ) {
pAImageReader_setImageListener ( hidden - > reader , NULL ) ;
}
2023-11-09 11:11:07 +01:00
2024-02-18 00:50:32 -05:00
if ( hidden - > session ) {
pACameraCaptureSession_close ( hidden - > session ) ;
}
2023-11-09 11:11:07 +01:00
2024-02-18 00:50:32 -05:00
if ( hidden - > request ) {
pACaptureRequest_free ( hidden - > request ) ;
2023-11-09 11:11:07 +01:00
}
2024-02-18 00:50:32 -05:00
if ( hidden - > outputTarget ) {
pACameraOutputTarget_free ( hidden - > outputTarget ) ;
2023-11-09 11:11:07 +01:00
}
2024-02-18 00:50:32 -05:00
if ( hidden - > sessionOutputContainer ) {
pACaptureSessionOutputContainer_free ( hidden - > sessionOutputContainer ) ;
2023-11-09 11:11:07 +01:00
}
2024-02-18 00:50:32 -05:00
if ( hidden - > sessionOutput ) {
pACaptureSessionOutput_free ( hidden - > sessionOutput ) ;
2023-11-09 11:11:07 +01:00
}
2024-02-18 00:50:32 -05:00
// we don't free hidden->window here, it'll be cleaned up by AImageReader_delete.
2023-11-09 11:11:07 +01:00
2024-02-18 00:50:32 -05:00
if ( hidden - > reader ) {
pAImageReader_delete ( hidden - > reader ) ;
}
2023-11-09 11:11:07 +01:00
2024-02-18 00:50:32 -05:00
if ( hidden - > device ) {
pACameraDevice_close ( hidden - > device ) ;
}
2023-11-09 11:11:07 +01:00
2024-02-18 00:50:32 -05:00
SDL_free ( hidden ) ;
2023-11-09 11:11:07 +01:00
}
}
2024-02-18 00:50:32 -05:00
// this is where the "opening" of the camera happens, after permission is granted.
static int PrepareCamera ( SDL_CameraDevice * device )
2023-11-09 11:11:07 +01:00
{
2024-02-18 00:50:32 -05:00
SDL_assert ( device - > hidden ! = NULL ) ;
2023-11-09 11:11:07 +01:00
camera_status_t res ;
media_status_t res2 ;
2024-02-18 00:50:32 -05:00
ACameraDevice_StateCallbacks dev_callbacks ;
SDL_zero ( dev_callbacks ) ;
dev_callbacks . context = device ;
dev_callbacks . onDisconnected = onDisconnected ;
dev_callbacks . onError = onError ;
2023-11-09 11:11:07 +01:00
2024-02-18 00:50:32 -05:00
ACameraCaptureSession_stateCallbacks capture_callbacks ;
SDL_zero ( capture_callbacks ) ;
capture_callbacks . context = device ;
capture_callbacks . onClosed = onClosed ;
capture_callbacks . onReady = onReady ;
capture_callbacks . onActive = onActive ;
AImageReader_ImageListener imglistener ;
SDL_zero ( imglistener ) ;
imglistener . context = device ;
imglistener . onImageAvailable = onImageAvailable ;
// just in case SDL_OpenCameraDevice is overwriting device->spec as CameraPermissionCallback runs, we work from a different copy.
const SDL_CameraSpec * spec = & device - > hidden - > requested_spec ;
if ( ( res = pACameraManager_openCamera ( cameraMgr , ( const char * ) device - > handle , & dev_callbacks , & device - > hidden - > device ) ) ! = ACAMERA_OK ) {
return SetCameraError ( " Failed to open camera " , res ) ;
} else if ( ( res2 = pAImageReader_new ( spec - > width , spec - > height , format_sdl_to_android ( spec - > format ) , 10 /* nb buffers */ , & device - > hidden - > reader ) ) ! = AMEDIA_OK ) {
return SetMediaError ( " Error AImageReader_new " , res2 ) ;
} else if ( ( res2 = pAImageReader_getWindow ( device - > hidden - > reader , & device - > hidden - > window ) ) ! = AMEDIA_OK ) {
return SetMediaError ( " Error AImageReader_getWindow " , res2 ) ;
} else if ( ( res = pACaptureSessionOutput_create ( device - > hidden - > window , & device - > hidden - > sessionOutput ) ) ! = ACAMERA_OK ) {
return SetCameraError ( " Error ACaptureSessionOutput_create " , res ) ;
} else if ( ( res = pACaptureSessionOutputContainer_create ( & device - > hidden - > sessionOutputContainer ) ) ! = ACAMERA_OK ) {
return SetCameraError ( " Error ACaptureSessionOutputContainer_create " , res ) ;
} else if ( ( res = pACaptureSessionOutputContainer_add ( device - > hidden - > sessionOutputContainer , device - > hidden - > sessionOutput ) ) ! = ACAMERA_OK ) {
return SetCameraError ( " Error ACaptureSessionOutputContainer_add " , res ) ;
} else if ( ( res = pACameraOutputTarget_create ( device - > hidden - > window , & device - > hidden - > outputTarget ) ) ! = ACAMERA_OK ) {
return SetCameraError ( " Error ACameraOutputTarget_create " , res ) ;
} else if ( ( res = pACameraDevice_createCaptureRequest ( device - > hidden - > device , TEMPLATE_RECORD , & device - > hidden - > request ) ) ! = ACAMERA_OK ) {
return SetCameraError ( " Error ACameraDevice_createCaptureRequest " , res ) ;
} else if ( ( res = pACaptureRequest_addTarget ( device - > hidden - > request , device - > hidden - > outputTarget ) ) ! = ACAMERA_OK ) {
return SetCameraError ( " Error ACaptureRequest_addTarget " , res ) ;
} else if ( ( res = pACameraDevice_createCaptureSession ( device - > hidden - > device , device - > hidden - > sessionOutputContainer , & capture_callbacks , & device - > hidden - > session ) ) ! = ACAMERA_OK ) {
return SetCameraError ( " Error ACameraDevice_createCaptureSession " , res ) ;
} else if ( ( res = pACameraCaptureSession_setRepeatingRequest ( device - > hidden - > session , NULL , 1 , & device - > hidden - > request , NULL ) ) ! = ACAMERA_OK ) {
return SetCameraError ( " Error ACameraCaptureSession_setRepeatingRequest " , res ) ;
} else if ( ( res2 = pAImageReader_setImageListener ( device - > hidden - > reader , & imglistener ) ) ! = AMEDIA_OK ) {
return SetMediaError ( " Error AImageReader_setImageListener " , res2 ) ;
2023-11-09 11:11:07 +01:00
}
return 0 ;
}
2024-02-18 00:50:32 -05:00
static void SDLCALL CameraPermissionCallback ( void * userdata , const char * permission , SDL_bool granted )
2023-11-09 11:11:07 +01:00
{
2024-02-18 00:50:32 -05:00
SDL_CameraDevice * device = ( SDL_CameraDevice * ) userdata ;
if ( device - > hidden ! = NULL ) { // if device was already closed, don't send an event.
if ( ! granted ) {
SDL_CameraDevicePermissionOutcome ( device , SDL_FALSE ) ; // sorry, permission denied.
} else if ( PrepareCamera ( device ) < 0 ) { // permission given? Actually open the camera now.
// uhoh, setup failed; since the app thinks we already "opened" the device, mark it as disconnected and don't report the permission.
SDL_CameraDeviceDisconnected ( device ) ;
} else {
// okay! We have permission to use the camera _and_ opening the hardware worked out, report that the camera is usable!
SDL_CameraDevicePermissionOutcome ( device , SDL_TRUE ) ; // go go go!
2023-11-09 11:11:07 +01:00
}
}
2024-02-18 00:50:32 -05:00
UnrefPhysicalCameraDevice ( device ) ; // we ref'd this in OpenDevice, release the extra reference.
2023-11-09 11:11:07 +01:00
}
2024-02-18 00:50:32 -05:00
static int ANDROIDCAMERA_OpenDevice ( SDL_CameraDevice * device , const SDL_CameraSpec * spec )
2023-11-09 11:11:07 +01:00
{
2024-02-18 00:50:32 -05:00
#if 0 // !!! FIXME: for now, we'll just let this fail if it is going to fail, without checking for this
/* Cannot open a second camera, while the first one is opened.
* If you want to play several camera , they must all be opened first , then played .
*
* https : //developer.android.com/reference/android/hardware/camera2/CameraManager
* " All camera devices intended to be operated concurrently, must be opened using openCamera(String, CameraDevice.StateCallback, Handler),
* before configuring sessions on any of the camera devices . * "
*
*/
if ( CheckDevicePlaying ( ) ) {
return SDL_SetError ( " A camera is already playing " ) ;
2023-11-09 11:11:07 +01:00
}
2024-02-18 00:50:32 -05:00
# endif
2023-11-09 11:11:07 +01:00
2024-02-18 00:50:32 -05:00
device - > hidden = ( struct SDL_PrivateCameraData * ) SDL_calloc ( 1 , sizeof ( struct SDL_PrivateCameraData ) ) ;
if ( device - > hidden = = NULL ) {
2023-11-09 11:11:07 +01:00
return - 1 ;
}
2024-02-18 00:50:32 -05:00
RefPhysicalCameraDevice ( device ) ; // ref'd until permission callback fires.
2023-11-09 11:11:07 +01:00
2024-02-18 00:50:32 -05:00
// just in case SDL_OpenCameraDevice is overwriting device->spec as CameraPermissionCallback runs, we work from a different copy.
SDL_copyp ( & device - > hidden - > requested_spec , spec ) ;
if ( SDL_AndroidRequestPermission ( " android.permission.CAMERA " , CameraPermissionCallback , device ) < 0 ) {
UnrefPhysicalCameraDevice ( device ) ;
2023-11-09 11:11:07 +01:00
return - 1 ;
}
2024-02-18 00:50:32 -05:00
return 0 ; // we don't open the camera until permission is granted, so always succeed for now.
2023-11-09 11:11:07 +01:00
}
2024-02-18 00:50:32 -05:00
static void ANDROIDCAMERA_FreeDeviceHandle ( SDL_CameraDevice * device )
2023-11-09 11:11:07 +01:00
{
2024-02-18 00:50:32 -05:00
if ( device ) {
SDL_free ( device - > handle ) ;
2023-11-09 11:11:07 +01:00
}
}
2024-02-18 00:50:32 -05:00
static void GatherCameraSpecs ( const char * devid , CameraFormatAddData * add_data , char * * fullname , const char * * posstr )
2023-11-09 11:11:07 +01:00
{
2024-02-18 00:50:32 -05:00
SDL_zerop ( add_data ) ;
ACameraMetadata * metadata = NULL ;
ACameraMetadata_const_entry cfgentry ;
ACameraMetadata_const_entry durentry ;
ACameraMetadata_const_entry infoentry ;
// This can fail with an "unknown error" (with `adb logcat` reporting "no such file or directory")
// for "LEGACY" level cameras. I saw this happen on a 30-dollar budget phone I have for testing
// (but a different brand budget phone worked, so it's not strictly the low-end of Android devices).
// LEGACY devices are seen by onCameraAvailable, but are not otherwise accessible through
// libcamera2ndk. The Java camera2 API apparently _can_ access these cameras, but we're going on
// without them here for now, in hopes that such hardware is a dying breed.
if ( pACameraManager_getCameraCharacteristics ( cameraMgr , devid , & metadata ) ! = ACAMERA_OK ) {
return ; // oh well.
} else if ( pACameraMetadata_getConstEntry ( metadata , ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS , & cfgentry ) ! = ACAMERA_OK ) {
pACameraMetadata_free ( metadata ) ;
return ; // oh well.
} else if ( pACameraMetadata_getConstEntry ( metadata , ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS , & durentry ) ! = ACAMERA_OK ) {
pACameraMetadata_free ( metadata ) ;
return ; // oh well.
}
* fullname = NULL ;
if ( pACameraMetadata_getConstEntry ( metadata , ACAMERA_INFO_VERSION , & infoentry ) = = ACAMERA_OK ) {
* fullname = ( char * ) SDL_malloc ( infoentry . count + 1 ) ;
if ( * fullname ) {
SDL_strlcpy ( * fullname , ( const char * ) infoentry . data . u8 , infoentry . count + 1 ) ;
}
2023-11-09 11:11:07 +01:00
}
2024-02-18 00:50:32 -05:00
* posstr = NULL ;
ACameraMetadata_const_entry posentry ;
if ( pACameraMetadata_getConstEntry ( metadata , ACAMERA_LENS_FACING , & posentry ) = = ACAMERA_OK ) { // ignore this if it fails.
if ( * posentry . data . u8 = = ACAMERA_LENS_FACING_FRONT ) {
* posstr = " front " ;
if ( ! * fullname ) {
* fullname = SDL_strdup ( " Front-facing camera " ) ;
}
} else if ( * posentry . data . u8 = = ACAMERA_LENS_FACING_BACK ) {
* posstr = " back " ;
if ( ! * fullname ) {
* fullname = SDL_strdup ( " Back-facing camera " ) ;
}
}
2023-11-09 11:11:07 +01:00
}
2024-02-18 00:50:32 -05:00
if ( ! * fullname ) {
* fullname = SDL_strdup ( " Generic camera " ) ; // we tried.
2023-11-09 11:11:07 +01:00
}
2024-02-18 00:50:32 -05:00
const int32_t * i32ptr = cfgentry . data . i32 ;
for ( int i = 0 ; i < cfgentry . count ; i + + , i32ptr + = 4 ) {
const int32_t fmt = i32ptr [ 0 ] ;
const int w = ( int ) i32ptr [ 1 ] ;
const int h = ( int ) i32ptr [ 2 ] ;
const int32_t type = i32ptr [ 3 ] ;
Uint32 sdlfmt ;
2023-11-09 11:11:07 +01:00
if ( type = = ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT ) {
continue ;
2024-02-18 00:50:32 -05:00
} else if ( ( w < = 0 ) | | ( h < = 0 ) ) {
continue ;
} else if ( ( sdlfmt = format_android_to_sdl ( fmt ) ) = = SDL_PIXELFORMAT_UNKNOWN ) {
2023-11-09 11:11:07 +01:00
continue ;
}
2024-02-18 00:50:32 -05:00
#if 0 // !!! FIXME: these all come out with 0 durations on my test phone. :(
const int64_t * i64ptr = durentry . data . i64 ;
for ( int j = 0 ; j < durentry . count ; j + + , i64ptr + = 4 ) {
const int32_t fpsfmt = ( int32_t ) i64ptr [ 0 ] ;
const int fpsw = ( int ) i64ptr [ 1 ] ;
const int fpsh = ( int ) i64ptr [ 2 ] ;
const long long duration = ( long long ) i64ptr [ 3 ] ;
SDL_Log ( " CAMERA: possible fps %s %dx%d duration=%lld " , SDL_GetPixelFormatName ( format_android_to_sdl ( fpsfmt ) ) , fpsw , fpsh , duration ) ;
if ( ( duration > 0 ) & & ( fpsfmt = = fmt ) & & ( fpsw = = w ) & & ( fpsh = = h ) ) {
SDL_AddCameraFormat ( add_data , sdlfmt , w , h , duration , 1000000000 ) ;
}
2023-11-09 11:11:07 +01:00
}
2024-02-18 00:50:32 -05:00
# else
SDL_AddCameraFormat ( add_data , sdlfmt , w , h , 1 , 30 ) ;
# endif
2023-11-09 11:11:07 +01:00
}
2024-02-18 00:50:32 -05:00
pACameraMetadata_free ( metadata ) ;
2023-11-09 11:11:07 +01:00
}
2024-02-18 00:50:32 -05:00
static SDL_bool FindAndroidCameraDeviceByID ( SDL_CameraDevice * device , void * userdata )
2023-11-09 11:11:07 +01:00
{
2024-02-18 00:50:32 -05:00
const char * devid = ( const char * ) userdata ;
return ( SDL_strcmp ( devid , ( const char * ) device - > handle ) = = 0 ) ;
}
2023-11-09 11:11:07 +01:00
2024-02-18 00:50:32 -05:00
static void MaybeAddDevice ( const char * devid )
{
# if DEBUG_CAMERA
SDL_Log ( " CAMERA: MaybeAddDevice('%s') " , devid ) ;
# endif
2023-11-09 11:11:07 +01:00
2024-02-18 00:50:32 -05:00
if ( SDL_FindPhysicalCameraDeviceByCallback ( FindAndroidCameraDeviceByID , ( void * ) devid ) ) {
return ; // already have this one.
}
const char * posstr = NULL ;
char * fullname = NULL ;
CameraFormatAddData add_data ;
GatherCameraSpecs ( devid , & add_data , & fullname , & posstr ) ;
if ( add_data . num_specs > 0 ) {
char * namecpy = SDL_strdup ( devid ) ;
if ( namecpy ) {
SDL_CameraDevice * device = SDL_AddCameraDevice ( fullname , add_data . num_specs , add_data . specs , namecpy ) ;
if ( ! device ) {
SDL_free ( namecpy ) ;
} else if ( device & & posstr ) {
SDL_Camera * camera = ( SDL_Camera * ) device ; // currently there's no separation between physical and logical device.
SDL_PropertiesID props = SDL_GetCameraProperties ( camera ) ;
if ( props ) {
SDL_SetStringProperty ( props , SDL_PROP_CAMERA_POSITION_STRING , posstr ) ;
}
}
2023-11-09 11:11:07 +01:00
}
}
2024-02-18 00:50:32 -05:00
SDL_free ( fullname ) ;
SDL_free ( add_data . specs ) ;
}
2023-11-27 19:35:45 +01:00
2024-02-18 00:50:32 -05:00
// note that camera "availability" covers both hotplugging and whether another
// has the device opened, but for something like Android, it's probably fine
// to treat both unplugging and loss of access as disconnection events. When
// the other app closes the camera, we get an available event as if it was
// just plugged back in.
2023-11-27 19:35:45 +01:00
2024-02-18 00:50:32 -05:00
static void onCameraAvailable ( void * context , const char * cameraId )
{
# if DEBUG_CAMERA
SDL_Log ( " CAMERA: CB onCameraAvailable('%s') " , cameraId ) ;
# endif
SDL_assert ( cameraId ! = NULL ) ;
MaybeAddDevice ( cameraId ) ;
}
2023-11-28 23:03:19 -05:00
2024-02-18 00:50:32 -05:00
static void onCameraUnavailable ( void * context , const char * cameraId )
{
# if DEBUG_CAMERA
SDL_Log ( " CAMERA: CB onCameraUnvailable('%s') " , cameraId ) ;
# endif
SDL_assert ( cameraId ! = NULL ) ;
SDL_CameraDeviceDisconnected ( SDL_FindPhysicalCameraDeviceByCallback ( FindAndroidCameraDeviceByID , ( void * ) cameraId ) ) ;
2023-11-28 23:03:19 -05:00
}
2024-02-18 00:50:32 -05:00
static const ACameraManager_AvailabilityCallbacks camera_availability_listener = {
NULL ,
onCameraAvailable ,
onCameraUnavailable
} ;
static void ANDROIDCAMERA_DetectDevices ( void )
2023-11-28 23:03:19 -05:00
{
2024-02-18 00:50:32 -05:00
ACameraIdList * list = NULL ;
camera_status_t res = pACameraManager_getCameraIdList ( cameraMgr , & list ) ;
2023-11-28 23:03:19 -05:00
2024-02-18 00:50:32 -05:00
if ( ( res = = ACAMERA_OK ) & & list ) {
const int total = list - > numCameras ;
for ( int i = 0 ; i < total ; i + + ) {
MaybeAddDevice ( list - > cameraIds [ i ] ) ;
}
2023-11-27 19:35:45 +01:00
2024-02-18 00:50:32 -05:00
pACameraManager_deleteCameraIdList ( list ) ;
2023-11-27 19:35:45 +01:00
}
2024-02-18 00:50:32 -05:00
pACameraManager_registerAvailabilityCallback ( cameraMgr , & camera_availability_listener ) ;
2023-11-27 19:35:45 +01:00
}
2023-12-01 10:59:13 -05:00
static void ANDROIDCAMERA_Deinitialize ( void )
2023-11-28 23:03:19 -05:00
{
2024-02-18 00:50:32 -05:00
pACameraManager_unregisterAvailabilityCallback ( cameraMgr , & camera_availability_listener ) ;
2023-12-01 10:59:13 -05:00
DestroyCameraManager ( ) ;
2024-02-18 00:50:32 -05:00
dlclose ( libcamera2ndk ) ;
libcamera2ndk = NULL ;
pACameraManager_create = NULL ;
pACameraManager_registerAvailabilityCallback = NULL ;
pACameraManager_unregisterAvailabilityCallback = NULL ;
pACameraManager_getCameraIdList = NULL ;
pACameraManager_deleteCameraIdList = NULL ;
pACameraCaptureSession_close = NULL ;
pACaptureRequest_free = NULL ;
pACameraOutputTarget_free = NULL ;
pACameraDevice_close = NULL ;
pACameraManager_delete = NULL ;
pACaptureSessionOutputContainer_free = NULL ;
pACaptureSessionOutput_free = NULL ;
pACameraManager_openCamera = NULL ;
pACameraDevice_createCaptureRequest = NULL ;
pACameraDevice_createCaptureSession = NULL ;
pACameraManager_getCameraCharacteristics = NULL ;
pACameraMetadata_free = NULL ;
pACameraMetadata_getConstEntry = NULL ;
pACameraCaptureSession_setRepeatingRequest = NULL ;
pACameraOutputTarget_create = NULL ;
pACaptureRequest_addTarget = NULL ;
pACaptureSessionOutputContainer_add = NULL ;
pACaptureSessionOutputContainer_create = NULL ;
pACaptureSessionOutput_create = NULL ;
dlclose ( libmediandk ) ;
libmediandk = NULL ;
pAImage_delete = NULL ;
pAImage_getTimestamp = NULL ;
pAImage_getNumberOfPlanes = NULL ;
pAImage_getPlaneRowStride = NULL ;
pAImage_getPlaneData = NULL ;
pAImageReader_acquireNextImage = NULL ;
pAImageReader_delete = NULL ;
pAImageReader_setImageListener = NULL ;
pAImageReader_getWindow = NULL ;
pAImageReader_new = NULL ;
}
2023-12-01 10:59:13 -05:00
static SDL_bool ANDROIDCAMERA_Init ( SDL_CameraDriverImpl * impl )
2023-11-28 23:03:19 -05:00
{
2024-02-18 00:50:32 -05:00
// !!! FIXME: slide this off into a subroutine
// system libraries are in android-24 and later; we currently target android-16 and later, so check if they exist at runtime.
void * libcamera2 = dlopen ( " libcamera2ndk.so " , RTLD_NOW | RTLD_LOCAL ) ;
if ( ! libcamera2 ) {
SDL_Log ( " CAMERA: libcamera2ndk.so can't be loaded: %s " , dlerror ( ) ) ;
return SDL_FALSE ;
}
void * libmedia = dlopen ( " libmediandk.so " , RTLD_NOW | RTLD_LOCAL ) ;
if ( ! libmedia ) {
SDL_Log ( " CAMERA: libmediandk.so can't be loaded: %s " , dlerror ( ) ) ;
dlclose ( libcamera2 ) ;
return SDL_FALSE ;
}
SDL_bool okay = SDL_TRUE ;
# 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 = SDL_FALSE; } }
//#define LOADSYM(lib, fn) p##fn = (pfn##fn) fn
LOADSYM ( libcamera2 , ACameraManager_create ) ;
LOADSYM ( libcamera2 , ACameraManager_registerAvailabilityCallback ) ;
LOADSYM ( libcamera2 , ACameraManager_unregisterAvailabilityCallback ) ;
LOADSYM ( libcamera2 , ACameraManager_getCameraIdList ) ;
LOADSYM ( libcamera2 , ACameraManager_deleteCameraIdList ) ;
LOADSYM ( libcamera2 , ACameraCaptureSession_close ) ;
LOADSYM ( libcamera2 , ACaptureRequest_free ) ;
LOADSYM ( libcamera2 , ACameraOutputTarget_free ) ;
LOADSYM ( libcamera2 , ACameraDevice_close ) ;
LOADSYM ( libcamera2 , ACameraManager_delete ) ;
LOADSYM ( libcamera2 , ACaptureSessionOutputContainer_free ) ;
LOADSYM ( libcamera2 , ACaptureSessionOutput_free ) ;
LOADSYM ( libcamera2 , ACameraManager_openCamera ) ;
LOADSYM ( libcamera2 , ACameraDevice_createCaptureRequest ) ;
LOADSYM ( libcamera2 , ACameraDevice_createCaptureSession ) ;
LOADSYM ( libcamera2 , ACameraManager_getCameraCharacteristics ) ;
LOADSYM ( libcamera2 , ACameraMetadata_free ) ;
LOADSYM ( libcamera2 , ACameraMetadata_getConstEntry ) ;
LOADSYM ( libcamera2 , ACameraCaptureSession_setRepeatingRequest ) ;
LOADSYM ( libcamera2 , ACameraOutputTarget_create ) ;
LOADSYM ( libcamera2 , ACaptureRequest_addTarget ) ;
LOADSYM ( libcamera2 , ACaptureSessionOutputContainer_add ) ;
LOADSYM ( libcamera2 , ACaptureSessionOutputContainer_create ) ;
LOADSYM ( libcamera2 , ACaptureSessionOutput_create ) ;
LOADSYM ( libmedia , AImage_delete ) ;
LOADSYM ( libmedia , AImage_getTimestamp ) ;
LOADSYM ( libmedia , AImage_getNumberOfPlanes ) ;
LOADSYM ( libmedia , AImage_getPlaneRowStride ) ;
LOADSYM ( libmedia , AImage_getPlaneData ) ;
LOADSYM ( libmedia , AImageReader_acquireNextImage ) ;
LOADSYM ( libmedia , AImageReader_delete ) ;
LOADSYM ( libmedia , AImageReader_setImageListener ) ;
LOADSYM ( libmedia , AImageReader_getWindow ) ;
LOADSYM ( libmedia , AImageReader_new ) ;
LOADSYM ( libmedia , AImage_getWidth ) ;
LOADSYM ( libmedia , AImage_getHeight ) ;
# undef LOADSYM
if ( ! okay ) {
dlclose ( libmedia ) ;
dlclose ( libcamera2 ) ;
}
2023-12-01 10:59:13 -05:00
if ( CreateCameraManager ( ) < 0 ) {
2024-02-18 00:50:32 -05:00
dlclose ( libmedia ) ;
dlclose ( libcamera2 ) ;
2023-12-01 10:59:13 -05:00
return SDL_FALSE ;
}
2024-02-18 00:50:32 -05:00
libcamera2ndk = libcamera2 ;
libmediandk = libmedia ;
2023-12-01 10:59:13 -05:00
impl - > DetectDevices = ANDROIDCAMERA_DetectDevices ;
impl - > OpenDevice = ANDROIDCAMERA_OpenDevice ;
impl - > CloseDevice = ANDROIDCAMERA_CloseDevice ;
2024-02-18 00:50:32 -05:00
impl - > WaitDevice = ANDROIDCAMERA_WaitDevice ;
2023-12-01 10:59:13 -05:00
impl - > AcquireFrame = ANDROIDCAMERA_AcquireFrame ;
impl - > ReleaseFrame = ANDROIDCAMERA_ReleaseFrame ;
2024-02-18 00:50:32 -05:00
impl - > FreeDeviceHandle = ANDROIDCAMERA_FreeDeviceHandle ;
2023-12-01 10:59:13 -05:00
impl - > Deinitialize = ANDROIDCAMERA_Deinitialize ;
2024-02-18 00:50:32 -05:00
impl - > ProvidesOwnCallbackThread = SDL_TRUE ;
2023-12-01 10:59:13 -05:00
return SDL_TRUE ;
2023-11-27 19:35:45 +01:00
}
2023-12-01 10:59:13 -05:00
CameraBootStrap ANDROIDCAMERA_bootstrap = {
" android " , " SDL Android camera driver " , ANDROIDCAMERA_Init , SDL_FALSE
} ;
2023-11-09 11:11:07 +01:00
# endif