FreeRDP
Loading...
Searching...
No Matches
camera_android.c
1
20#include <stdlib.h>
21#include <string.h>
22
23#include <camera/NdkCameraDevice.h>
24#include <camera/NdkCameraManager.h>
25#include <camera/NdkCameraMetadata.h>
26#include <camera/NdkCameraCaptureSession.h>
27#include <media/NdkImage.h>
28#include <media/NdkImageReader.h>
29
30#include "../camera.h"
31
32#define TAG CHANNELS_TAG("rdpecam-android.client")
33
34#define ANDROID_CAMERA_PREFIX "android-camera-"
35#define ANDROID_CAMERA_PREFIX_LEN (sizeof(ANDROID_CAMERA_PREFIX) - 1)
36
37#define CAM_ANDROID_FPS 30
38
39typedef struct
40{
41 ICamHal iHal;
42 ACameraManager* manager;
43 wHashTable* streams; /* key: deviceId, value: CamAndroidStream */
44} CamAndroidHal;
45
46typedef struct
47{
48 size_t streamIndex;
49
50 ACameraDevice* device;
51 ACameraCaptureSession* session;
52 ACaptureSessionOutputContainer* outputContainer;
53 ACaptureSessionOutput* sessionOutput;
54 ACameraOutputTarget* outputTarget;
55 ACaptureRequest* captureRequest;
56 AImageReader* imageReader;
57 ANativeWindow* window;
58
59 ICamHalSampleCapturedCallback sampleCallback;
60 CameraDevice* dev;
61 BOOL streaming;
62 BOOL deviceError;
64
65 BYTE* nv12Buffer;
66 size_t nv12BufferSize;
67} CamAndroidStream;
68
69static const char* cam_android_extract_camera_id(const char* deviceId)
70{
71 WINPR_ASSERT(deviceId);
72
73 if (strncmp(deviceId, ANDROID_CAMERA_PREFIX, ANDROID_CAMERA_PREFIX_LEN) != 0)
74 return NULL;
75 return deviceId + ANDROID_CAMERA_PREFIX_LEN;
76}
77
78/* Tear down the capture session and its request objects. The ImageReader and device are kept. */
79static void cam_android_close_session(CamAndroidStream* stream)
80{
81 WINPR_ASSERT(stream);
82
83 if (stream->session)
84 {
85 ACameraCaptureSession_stopRepeating(stream->session);
86 ACameraCaptureSession_close(stream->session);
87 stream->session = NULL;
88 }
89 if (stream->captureRequest)
90 {
91 if (stream->outputTarget)
92 ACaptureRequest_removeTarget(stream->captureRequest, stream->outputTarget);
93 ACaptureRequest_free(stream->captureRequest);
94 stream->captureRequest = NULL;
95 }
96 if (stream->outputTarget)
97 {
98 ACameraOutputTarget_free(stream->outputTarget);
99 stream->outputTarget = NULL;
100 }
101 if (stream->outputContainer)
102 {
103 ACaptureSessionOutputContainer_free(stream->outputContainer);
104 stream->outputContainer = NULL;
105 }
106 if (stream->sessionOutput)
107 {
108 ACaptureSessionOutput_free(stream->sessionOutput);
109 stream->sessionOutput = NULL;
110 }
111}
112
113/* Pack a YUV_420_888 frame into NV12 (handles NV12, NV21 and planar I420 layouts). */
114static void cam_android_yuv420_888_to_nv12(BYTE* dst, int width, int height, const uint8_t* yData,
115 int yRowStride, const uint8_t* uData, int uRowStride,
116 int uPixelStride, const uint8_t* vData, int vRowStride,
117 int vPixelStride)
118{
119 WINPR_ASSERT(dst);
120 WINPR_ASSERT(yData);
121 WINPR_ASSERT(uData);
122 WINPR_ASSERT(vData);
123
124 const size_t ySize = (size_t)width * height;
125
126 /* Y plane */
127 if (yRowStride == width)
128 memcpy(dst, yData, ySize);
129 else
130 {
131 for (int row = 0; row < height; row++)
132 memcpy(dst + (size_t)row * width, yData + (size_t)row * yRowStride, (size_t)width);
133 }
134
135 /* UV planes -> interleaved NV12 */
136 BYTE* uv = dst + ySize;
137 const int uvHeight = height / 2;
138 const int uvWidth = width / 2;
139 const size_t uvRowBytes = (size_t)(uvWidth * 2);
140
141 if (uPixelStride == 2 && vPixelStride == 2)
142 {
143 /* Semi-planar: NV12 (U first) or NV21 (V first). */
144 if (uData < vData)
145 {
146 /* NV12: copy the UV block directly. */
147 if (uRowStride == (int)uvRowBytes)
148 memcpy(uv, uData, (size_t)uvHeight * uvRowBytes);
149 else
150 {
151 for (int row = 0; row < uvHeight; row++)
152 memcpy(uv + (size_t)row * uvRowBytes, uData + (size_t)row * uRowStride,
153 uvRowBytes);
154 }
155 }
156 else
157 {
158 /* NV21: swap U and V bytes. */
159 for (int row = 0; row < uvHeight; row++)
160 {
161 const uint8_t* u = uData + (size_t)row * uRowStride;
162 const uint8_t* v = vData + (size_t)row * vRowStride;
163 BYTE* d = uv + (size_t)row * uvRowBytes;
164 for (int col = 0; col < uvWidth; col++, u += 2, v += 2, d += 2)
165 {
166 d[0] = u[0];
167 d[1] = v[0];
168 }
169 }
170 }
171 }
172 else
173 {
174 /* Planar I420: interleave U and V. */
175 for (int row = 0; row < uvHeight; row++)
176 {
177 const uint8_t* uRow = uData + (size_t)row * uRowStride;
178 const uint8_t* vRow = vData + (size_t)row * vRowStride;
179 BYTE* dstRow = uv + (size_t)row * uvRowBytes;
180 for (int col = 0; col < uvWidth; col++)
181 {
182 dstRow[col * 2] = uRow[col * uPixelStride];
183 dstRow[col * 2 + 1] = vRow[col * vPixelStride];
184 }
185 }
186 }
187}
188
189static void cam_android_deliver_frame(CamAndroidStream* stream, AImage* image)
190{
191 WINPR_ASSERT(stream);
192 WINPR_ASSERT(image);
193
194 int32_t width = 0;
195 int32_t height = 0;
196 if (AImage_getWidth(image, &width) != AMEDIA_OK ||
197 AImage_getHeight(image, &height) != AMEDIA_OK)
198 return;
199
200 const size_t ySize = (size_t)(width * height);
201 const size_t nv12Size = ySize + ySize / 2;
202
203 if (stream->nv12BufferSize < nv12Size)
204 {
205 BYTE* tmp = (BYTE*)realloc(stream->nv12Buffer, nv12Size);
206 if (!tmp)
207 return;
208 stream->nv12Buffer = tmp;
209 stream->nv12BufferSize = nv12Size;
210 }
211
212 /* Fetch the planes; the length out-param is required but unused. */
213 int dataLength = 0;
214 uint8_t* yData = NULL;
215 uint8_t* uData = NULL;
216 uint8_t* vData = NULL;
217 if (AImage_getPlaneData(image, 0, &yData, &dataLength) != AMEDIA_OK ||
218 AImage_getPlaneData(image, 1, &uData, &dataLength) != AMEDIA_OK ||
219 AImage_getPlaneData(image, 2, &vData, &dataLength) != AMEDIA_OK)
220 return;
221
222 int yRowStride = 0;
223 int uRowStride = 0;
224 int uPixelStride = 0;
225 int vRowStride = 0;
226 int vPixelStride = 0;
227 AImage_getPlaneRowStride(image, 0, &yRowStride);
228 AImage_getPlaneRowStride(image, 1, &uRowStride);
229 AImage_getPlanePixelStride(image, 1, &uPixelStride);
230 AImage_getPlaneRowStride(image, 2, &vRowStride);
231 AImage_getPlanePixelStride(image, 2, &vPixelStride);
232
233 cam_android_yuv420_888_to_nv12(stream->nv12Buffer, width, height, yData, yRowStride, uData,
234 uRowStride, uPixelStride, vData, vRowStride, vPixelStride);
235
236 stream->sampleCallback(stream->dev, stream->streamIndex, stream->nv12Buffer, nv12Size);
237}
238
239static void cam_android_on_image_available(void* context, AImageReader* reader)
240{
241 CamAndroidStream* stream = (CamAndroidStream*)context;
242 WINPR_ASSERT(stream);
243 WINPR_ASSERT(reader);
244
245 EnterCriticalSection(&stream->lock);
246 const BOOL streaming = stream->streaming;
247 LeaveCriticalSection(&stream->lock);
248
249 if (!streaming)
250 return;
251
252 AImage* image = NULL;
253 if (AImageReader_acquireLatestImage(reader, &image) == AMEDIA_OK && image)
254 {
255 cam_android_deliver_frame(stream, image);
256 AImage_delete(image);
257 }
258}
259
260/* Mark the device broken so it is reopened on next use. */
261static void cam_android_mark_device_error(CamAndroidStream* stream)
262{
263 if (!stream)
264 return;
265 EnterCriticalSection(&stream->lock);
266 stream->deviceError = TRUE;
267 stream->streaming = FALSE;
268 LeaveCriticalSection(&stream->lock);
269}
270
271static void cam_android_device_disconnected(void* context, ACameraDevice* device)
272{
273 WINPR_UNUSED(device);
274 WLog_WARN(TAG, "Camera device disconnected");
275 cam_android_mark_device_error((CamAndroidStream*)context);
276}
277
278static void cam_android_device_error(void* context, ACameraDevice* device, int error)
279{
280 WINPR_UNUSED(device);
281 WLog_ERR(TAG, "Camera device error %d", error);
282 cam_android_mark_device_error((CamAndroidStream*)context);
283}
284
285static void cam_android_session_active(void* context, ACameraCaptureSession* session)
286{
287 WINPR_UNUSED(context);
288 WINPR_UNUSED(session);
289 WLog_DBG(TAG, "Capture session active");
290}
291
292static void cam_android_session_closed(void* context, ACameraCaptureSession* session)
293{
294 WINPR_UNUSED(context);
295 WINPR_UNUSED(session);
296 WLog_DBG(TAG, "Capture session closed");
297}
298
299static void cam_android_session_ready(void* context, ACameraCaptureSession* session)
300{
301 WINPR_UNUSED(context);
302 WINPR_UNUSED(session);
303 WLog_DBG(TAG, "Capture session ready");
304}
305
306/* TRUE if the camera is BACKWARD_COMPATIBLE. Depth/IR/sub-sensor cameras lack this and cannot
307 * configure a normal YUV session, so they must not be offered to the server. */
308static BOOL cam_android_is_backward_compatible(ACameraMetadata* characteristics)
309{
310 WINPR_ASSERT(characteristics);
311
312 ACameraMetadata_const_entry caps;
313 if (ACameraMetadata_getConstEntry(characteristics, ACAMERA_REQUEST_AVAILABLE_CAPABILITIES,
314 &caps) != ACAMERA_OK)
315 return FALSE;
316
317 for (uint32_t i = 0; i < caps.count; i++)
318 {
319 if (caps.data.u8[i] == ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE)
320 return TRUE;
321 }
322 return FALSE;
323}
324
325static UINT cam_android_enumerate(ICamHal* ihal, ICamHalEnumCallback callback, CameraPlugin* ecam,
326 GENERIC_CHANNEL_CALLBACK* hchannel)
327{
328 CamAndroidHal* hal = (CamAndroidHal*)ihal;
329 WINPR_ASSERT(hal);
330
331 UINT count = 0;
332
333 /* NV12 capture needs FFmpeg/swscale to be encoded; without it, offer no cameras. */
334 if (!freerdp_video_conversion_supported(FREERDP_VIDEO_FORMAT_NV12,
335 FREERDP_VIDEO_FORMAT_YUV420P))
336 {
337 WLog_WARN(TAG, "Camera redirection unavailable: video conversion (FFmpeg/swscale) missing");
338 return 0;
339 }
340
341 ACameraIdList* idList = NULL;
342 if (ACameraManager_getCameraIdList(hal->manager, &idList) != ACAMERA_OK || !idList)
343 return 0;
344
345 /* Offer only the primary camera per facing direction: auxiliary back cameras (ultrawide/tele/
346 * macro/depth) advertise resolutions but fail to configure a YUV session and freeze the
347 * channel. Indexed by ACAMERA_LENS_FACING_* (FRONT=0, BACK=1, EXTERNAL=2). */
348 BOOL seenFacing[ACAMERA_LENS_FACING_EXTERNAL + 1] = WINPR_C_ARRAY_INIT;
349
350 for (int i = 0; i < idList->numCameras; i++)
351 {
352 const char* cameraId = idList->cameraIds[i];
353
354 ACameraMetadata* characteristics = NULL;
355 if (ACameraManager_getCameraCharacteristics(hal->manager, cameraId, &characteristics) !=
356 ACAMERA_OK)
357 continue;
358
359 /* Skip depth/IR/sub-sensor cameras that can't configure a normal capture session. */
360 if (!cam_android_is_backward_compatible(characteristics))
361 {
362 WLog_DBG(TAG, "Skipping camera %s: not BACKWARD_COMPATIBLE", cameraId);
363 ACameraMetadata_free(characteristics);
364 continue;
365 }
366
367 ACameraMetadata_const_entry facingEntry;
368 const char* facingName = "Camera";
369 uint8_t facing = ACAMERA_LENS_FACING_EXTERNAL;
370 if (ACameraMetadata_getConstEntry(characteristics, ACAMERA_LENS_FACING, &facingEntry) ==
371 ACAMERA_OK)
372 {
373 facing = facingEntry.data.u8[0];
374 switch (facing)
375 {
376 case ACAMERA_LENS_FACING_FRONT:
377 facingName = "Front Camera";
378 break;
379 case ACAMERA_LENS_FACING_BACK:
380 facingName = "Back Camera";
381 break;
382 default:
383 facingName = "External Camera";
384 break;
385 }
386 }
387
388 /* Bounds-guard seenFacing[] against an out-of-range facing value. */
389 if (facing <= ACAMERA_LENS_FACING_EXTERNAL)
390 {
391 if (seenFacing[facing])
392 {
393 WLog_DBG(TAG, "Skipping camera %s: already have a %s", cameraId, facingName);
394 ACameraMetadata_free(characteristics);
395 continue;
396 }
397 seenFacing[facing] = TRUE;
398 }
399
400 char deviceId[64];
401 (void)_snprintf(deviceId, sizeof(deviceId), ANDROID_CAMERA_PREFIX "%s", cameraId);
402
403 IFCALL(callback, ecam, hchannel, deviceId, facingName);
404 count++;
405
406 ACameraMetadata_free(characteristics);
407 }
408
409 ACameraManager_deleteCameraIdList(idList);
410 return count;
411}
412
413/* Stop streaming and fully release the camera. Order matters: close the session and device before
414 * the ImageReader, or the camera service fails to unconfigure and leaves the device running. */
415static void cam_android_close_device(CamAndroidStream* stream)
416{
417 WINPR_ASSERT(stream);
418
419 EnterCriticalSection(&stream->lock);
420 stream->streaming = FALSE;
421 LeaveCriticalSection(&stream->lock);
422
423 if (stream->imageReader)
424 AImageReader_setImageListener(stream->imageReader, NULL);
425
426 cam_android_close_session(stream);
427
428 if (stream->device)
429 {
430 ACameraDevice_close(stream->device);
431 stream->device = NULL;
432 }
433
434 if (stream->imageReader)
435 {
436 AImageReader_delete(stream->imageReader);
437 stream->imageReader = NULL;
438 stream->window = NULL;
439 }
440 free(stream->nv12Buffer);
441 stream->nv12Buffer = NULL;
442 stream->nv12BufferSize = 0;
443 stream->deviceError = FALSE;
444}
445
446/* Close every open camera except keepDeviceId. Phones share camera hardware, so holding more than
447 * one open makes the next openCamera fail with CAMERA_DISCONNECTED and freeze the channel. */
448static void cam_android_close_other_devices(CamAndroidHal* hal, const char* keepDeviceId)
449{
450 WINPR_ASSERT(hal);
451 WINPR_ASSERT(keepDeviceId);
452
453 ULONG_PTR* keys = NULL;
454 const size_t count = HashTable_GetKeys(hal->streams, &keys);
455 for (size_t i = 0; i < count; i++)
456 {
457 const char* id = (const char*)keys[i];
458 if (strcmp(id, keepDeviceId) == 0)
459 continue;
460
461 CamAndroidStream* other = (CamAndroidStream*)HashTable_GetItemValue(hal->streams, id);
462 if (!other || !other->device)
463 continue;
464
465 WLog_DBG(TAG, "Closing camera %s to free resources for %s", id, keepDeviceId);
466 cam_android_close_device(other);
467 }
468 free(keys);
469}
470
471static CamAndroidStream* cam_android_get_or_create_stream(CamAndroidHal* hal, const char* deviceId,
472 CAM_ERROR_CODE* errorCode)
473{
474 WINPR_ASSERT(hal);
475 WINPR_ASSERT(deviceId);
476 WINPR_ASSERT(errorCode);
477
478 CamAndroidStream* stream = (CamAndroidStream*)HashTable_GetItemValue(hal->streams, deviceId);
479 if (stream)
480 return stream;
481
482 stream = (CamAndroidStream*)calloc(1, sizeof(CamAndroidStream));
483 if (!stream)
484 {
485 *errorCode = CAM_ERROR_CODE_OutOfMemory;
486 return NULL;
487 }
488 if (!InitializeCriticalSectionEx(&stream->lock, 0, 0))
489 {
490 free(stream);
491 *errorCode = CAM_ERROR_CODE_UnexpectedError;
492 return NULL;
493 }
494 if (!HashTable_Insert(hal->streams, deviceId, stream))
495 {
496 DeleteCriticalSection(&stream->lock);
497 free(stream);
498 *errorCode = CAM_ERROR_CODE_UnexpectedError;
499 return NULL;
500 }
501 return stream;
502}
503
504/* Open the camera, closing any other open one first (phones keep only one open). Deferred from
505 * Activate to StartStream so media-type probing doesn't thrash the hardware. */
506static BOOL cam_android_open_device(CamAndroidHal* hal, CamAndroidStream* stream,
507 const char* deviceId, const char* cameraId,
508 CAM_ERROR_CODE* errorCode)
509{
510 WINPR_ASSERT(hal);
511 WINPR_ASSERT(stream);
512 WINPR_ASSERT(deviceId);
513 WINPR_ASSERT(cameraId);
514 WINPR_ASSERT(errorCode);
515
516 /* Reopen a device left broken by a previous error. */
517 EnterCriticalSection(&stream->lock);
518 const BOOL hadError = stream->deviceError;
519 LeaveCriticalSection(&stream->lock);
520 if (stream->device && hadError)
521 cam_android_close_device(stream);
522
523 if (stream->device)
524 return TRUE; /* already open */
525
526 cam_android_close_other_devices(hal, deviceId);
527
528 ACameraDevice_StateCallbacks deviceCallbacks = {
529 .context = stream,
530 .onDisconnected = cam_android_device_disconnected,
531 .onError = cam_android_device_error,
532 };
533
534 camera_status_t status =
535 ACameraManager_openCamera(hal->manager, cameraId, &deviceCallbacks, &stream->device);
536 if (status != ACAMERA_OK)
537 {
538 WLog_ERR(TAG, "ACameraManager_openCamera failed: %d", status);
539 *errorCode = CAM_ERROR_CODE_InvalidRequest;
540 return FALSE;
541 }
542
543 WLog_DBG(TAG, "Opened camera %s", cameraId);
544 return TRUE;
545}
546
547static BOOL cam_android_activate(ICamHal* ihal, const char* deviceId, CAM_ERROR_CODE* errorCode)
548{
549 CamAndroidHal* hal = (CamAndroidHal*)ihal;
550 WINPR_ASSERT(hal);
551 WINPR_ASSERT(deviceId);
552 WINPR_ASSERT(errorCode);
553
554 *errorCode = CAM_ERROR_CODE_None;
555
556 const char* cameraId = cam_android_extract_camera_id(deviceId);
557 if (!cameraId)
558 {
559 *errorCode = CAM_ERROR_CODE_InvalidRequest;
560 return FALSE;
561 }
562
563 /* Hardware is opened lazily in StartStream, not here. */
564 return cam_android_get_or_create_stream(hal, deviceId, errorCode) != NULL;
565}
566
567static BOOL cam_android_deactivate(ICamHal* ihal, const char* deviceId, CAM_ERROR_CODE* errorCode)
568{
569 CamAndroidHal* hal = (CamAndroidHal*)ihal;
570 WINPR_ASSERT(hal);
571 WINPR_ASSERT(deviceId);
572 WINPR_ASSERT(errorCode);
573
574 *errorCode = CAM_ERROR_CODE_None;
575
576 CamAndroidStream* stream = (CamAndroidStream*)HashTable_GetItemValue(hal->streams, deviceId);
577 if (!stream || !stream->device)
578 return TRUE;
579
580 cam_android_close_device(stream);
581 return TRUE;
582}
583
584static INT16 cam_android_get_media_type_descriptions(ICamHal* ihal, const char* deviceId,
585 size_t streamIndex,
586 const CAM_MEDIA_FORMAT_INFO* supportedFormats,
587 size_t nSupportedFormats,
588 CAM_MEDIA_TYPE_DESCRIPTION* mediaTypes,
589 size_t* nMediaTypes)
590{
591 WINPR_UNUSED(streamIndex);
592 CamAndroidHal* hal = (CamAndroidHal*)ihal;
593 WINPR_ASSERT(hal);
594 WINPR_ASSERT(deviceId);
595 WINPR_ASSERT(supportedFormats || (nSupportedFormats == 0));
596 WINPR_ASSERT(mediaTypes);
597 WINPR_ASSERT(nMediaTypes);
598
599 size_t maxMediaTypes = *nMediaTypes;
600 *nMediaTypes = 0;
601
602 const char* cameraId = cam_android_extract_camera_id(deviceId);
603 if (!cameraId)
604 return -1;
605
606 /* Find the NV12 format index. */
607 INT16 formatIndex = -1;
608 for (size_t i = 0; i < nSupportedFormats; i++)
609 {
610 if (supportedFormats[i].inputFormat == CAM_MEDIA_FORMAT_NV12)
611 {
612 formatIndex = (INT16)i;
613 break;
614 }
615 }
616 if (formatIndex < 0)
617 {
618 WLog_WARN(TAG, "Server does not offer NV12 format");
619 return -1;
620 }
621
622 ACameraMetadata* characteristics = NULL;
623 if (ACameraManager_getCameraCharacteristics(hal->manager, cameraId, &characteristics) !=
624 ACAMERA_OK)
625 return -1;
626
627 /* Stream configs: [format, width, height, isInput] tuples. */
628 ACameraMetadata_const_entry entry;
629 if (ACameraMetadata_getConstEntry(
630 characteristics, ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &entry) != ACAMERA_OK)
631 {
632 ACameraMetadata_free(characteristics);
633 return -1;
634 }
635
636 size_t nTypes = 0;
637 for (uint32_t i = 0; i + 3 < entry.count; i += 4)
638 {
639 const int32_t fmt = entry.data.i32[i];
640 const int32_t w = entry.data.i32[i + 1];
641 const int32_t h = entry.data.i32[i + 2];
642 const int32_t isInput = entry.data.i32[i + 3];
643
644 if (isInput != 0)
645 continue;
646 if (fmt != AIMAGE_FORMAT_YUV_420_888)
647 continue;
648 /* Skip resolutions larger than 1080p to avoid saturating the RDP link */
649 if (w > 1920 || h > 1080)
650 continue;
651
652 mediaTypes[nTypes].Format = CAM_MEDIA_FORMAT_NV12;
653 mediaTypes[nTypes].Width = (UINT32)w;
654 mediaTypes[nTypes].Height = (UINT32)h;
655 mediaTypes[nTypes].FrameRateNumerator = CAM_ANDROID_FPS;
656 mediaTypes[nTypes].FrameRateDenominator = 1;
657 mediaTypes[nTypes].PixelAspectRatioNumerator = 1;
658 mediaTypes[nTypes].PixelAspectRatioDenominator = 1;
659
660 WLog_DBG(TAG, "Camera format NV12 %dx%d @ %dfps", w, h, CAM_ANDROID_FPS);
661
662 if (++nTypes >= maxMediaTypes)
663 break;
664 }
665
666 *nMediaTypes = nTypes;
667 ACameraMetadata_free(characteristics);
668
669 if (nTypes == 0)
670 {
671 WLog_ERR(TAG, "No supported YUV resolutions found for camera %s", cameraId);
672 return -1;
673 }
674
675 return formatIndex;
676}
677
678static CAM_ERROR_CODE cam_android_start_stream(ICamHal* ihal, CameraDevice* dev, size_t streamIndex,
679 const CAM_MEDIA_TYPE_DESCRIPTION* mediaType,
680 ICamHalSampleCapturedCallback callback)
681{
682 CamAndroidHal* hal = (CamAndroidHal*)ihal;
683 WINPR_ASSERT(hal);
684 WINPR_ASSERT(dev);
685 WINPR_ASSERT(mediaType);
686 WINPR_ASSERT(callback);
687
688 const char* deviceId = dev->deviceId;
689
690 const char* cameraId = cam_android_extract_camera_id(deviceId);
691 if (!cameraId)
692 return CAM_ERROR_CODE_InvalidRequest;
693
694 CAM_ERROR_CODE errorCode = CAM_ERROR_CODE_None;
695 CamAndroidStream* stream = cam_android_get_or_create_stream(hal, deviceId, &errorCode);
696 if (!stream)
697 return errorCode;
698
699 if (!cam_android_open_device(hal, stream, deviceId, cameraId, &errorCode))
700 return errorCode;
701
702 stream->dev = dev;
703 stream->streamIndex = streamIndex;
704 stream->sampleCallback = callback;
705
706 const int32_t width = (int32_t)mediaType->Width;
707 const int32_t height = (int32_t)mediaType->Height;
708
709 if (AImageReader_new(width, height, AIMAGE_FORMAT_YUV_420_888, 4, &stream->imageReader) !=
710 AMEDIA_OK)
711 {
712 WLog_ERR(TAG, "AImageReader_new failed");
713 return CAM_ERROR_CODE_UnexpectedError;
714 }
715
716 AImageReader_ImageListener listener = {
717 .context = stream,
718 .onImageAvailable = cam_android_on_image_available,
719 };
720 AImageReader_setImageListener(stream->imageReader, &listener);
721
722 if (AImageReader_getWindow(stream->imageReader, &stream->window) != AMEDIA_OK)
723 {
724 WLog_ERR(TAG, "AImageReader_getWindow failed");
725 goto error;
726 }
727
728 ACaptureSessionOutput_create(stream->window, &stream->sessionOutput);
729 ACaptureSessionOutputContainer_create(&stream->outputContainer);
730 ACaptureSessionOutputContainer_add(stream->outputContainer, stream->sessionOutput);
731 ACameraOutputTarget_create(stream->window, &stream->outputTarget);
732 ACameraDevice_createCaptureRequest(stream->device, TEMPLATE_RECORD, &stream->captureRequest);
733 ACaptureRequest_addTarget(stream->captureRequest, stream->outputTarget);
734
735 ACameraCaptureSession_stateCallbacks sessionCallbacks = {
736 .context = stream,
737 .onActive = cam_android_session_active,
738 .onClosed = cam_android_session_closed,
739 .onReady = cam_android_session_ready,
740 };
741
742 if (ACameraDevice_createCaptureSession(stream->device, stream->outputContainer,
743 &sessionCallbacks, &stream->session) != ACAMERA_OK)
744 {
745 WLog_ERR(TAG, "ACameraDevice_createCaptureSession failed");
746 goto error;
747 }
748
749 if (ACameraCaptureSession_setRepeatingRequest(stream->session, NULL, 1, &stream->captureRequest,
750 NULL) != ACAMERA_OK)
751 {
752 WLog_ERR(TAG, "ACameraCaptureSession_setRepeatingRequest failed");
753 goto error;
754 }
755
756 EnterCriticalSection(&stream->lock);
757 stream->streaming = TRUE;
758 LeaveCriticalSection(&stream->lock);
759
760 WLog_DBG(TAG, "Camera stream started: %dx%d", width, height);
761 return CAM_ERROR_CODE_None;
762
763error:
764 cam_android_close_device(stream);
765 return CAM_ERROR_CODE_UnexpectedError;
766}
767
768static CAM_ERROR_CODE cam_android_stop_stream(ICamHal* ihal, const char* deviceId,
769 size_t streamIndex)
770{
771 WINPR_UNUSED(streamIndex);
772 CamAndroidHal* hal = (CamAndroidHal*)ihal;
773 WINPR_ASSERT(hal);
774 WINPR_ASSERT(deviceId);
775
776 CamAndroidStream* stream = (CamAndroidStream*)HashTable_GetItemValue(hal->streams, deviceId);
777 if (!stream)
778 return CAM_ERROR_CODE_None;
779
780 cam_android_close_device(stream);
781
782 WLog_DBG(TAG, "Camera stream stopped for %s", deviceId);
783 return CAM_ERROR_CODE_None;
784}
785
786static void cam_android_stream_free(void* obj)
787{
788 CamAndroidStream* stream = (CamAndroidStream*)obj;
789 if (!stream)
790 return;
791
792 cam_android_close_device(stream);
793 DeleteCriticalSection(&stream->lock);
794 free(stream);
795}
796
797static CAM_ERROR_CODE cam_android_free(ICamHal* ihal)
798{
799 CamAndroidHal* hal = (CamAndroidHal*)ihal;
800 if (!hal)
801 return CAM_ERROR_CODE_NotInitialized;
802
803 HashTable_Free(hal->streams);
804 if (hal->manager)
805 ACameraManager_delete(hal->manager);
806 free(hal);
807 return CAM_ERROR_CODE_None;
808}
809
810FREERDP_ENTRY_POINT(UINT VCAPITYPE android_freerdp_rdpecam_client_subsystem_entry(
812{
813 WINPR_ASSERT(pEntryPoints);
814
815 CamAndroidHal* hal = (CamAndroidHal*)calloc(1, sizeof(CamAndroidHal));
816 if (!hal)
817 return CHANNEL_RC_NO_MEMORY;
818
819 hal->manager = ACameraManager_create();
820 if (!hal->manager)
821 {
822 free(hal);
823 return ERROR_INTERNAL_ERROR;
824 }
825
826 hal->streams = HashTable_New(FALSE);
827 if (!hal->streams)
828 goto error;
829
830 if (!HashTable_SetupForStringData(hal->streams, FALSE))
831 goto error;
832
833 wObject* obj = HashTable_ValueObject(hal->streams);
834 WINPR_ASSERT(obj);
835 obj->fnObjectFree = cam_android_stream_free;
836
837 hal->iHal.Enumerate = cam_android_enumerate;
838 hal->iHal.Activate = cam_android_activate;
839 hal->iHal.Deactivate = cam_android_deactivate;
840 hal->iHal.GetMediaTypeDescriptions = cam_android_get_media_type_descriptions;
841 hal->iHal.StartStream = cam_android_start_stream;
842 hal->iHal.StopStream = cam_android_stop_stream;
843 hal->iHal.Free = cam_android_free;
844
845 UINT ret = pEntryPoints->pRegisterCameraHal(pEntryPoints->plugin, &hal->iHal);
846 if (ret != CHANNEL_RC_OK)
847 {
848 WLog_ERR(TAG, "RegisterCameraHal failed: %" PRIu32, ret);
849 goto error;
850 }
851
852 return ret;
853
854error:
855 cam_android_free(&hal->iHal);
856 return ERROR_INTERNAL_ERROR;
857}
Definition camera.h:221
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:59