FreeRDP
Loading...
Searching...
No Matches
client/camera_device_main.c
1
20#include <winpr/assert.h>
21#include <winpr/cast.h>
22#include <winpr/interlocked.h>
23#include <winpr/sysinfo.h>
24
25#include "camera.h"
26#include "rdpecam-utils.h"
27
28#define TAG CHANNELS_TAG("rdpecam-device.client")
29
39WINPR_ATTR_NODISCARD
40static const CAM_MEDIA_FORMAT_INFO* getSupportedFormats(size_t* pCount)
41{
42 WINPR_ASSERT(pCount);
43
44 const CAM_MEDIA_FORMAT available[] = { CAM_MEDIA_FORMAT_H264, CAM_MEDIA_FORMAT_MJPG,
45 CAM_MEDIA_FORMAT_YUY2, CAM_MEDIA_FORMAT_NV12,
46 CAM_MEDIA_FORMAT_I420, CAM_MEDIA_FORMAT_RGB24,
47 CAM_MEDIA_FORMAT_RGB32 };
48
50 formats[ARRAYSIZE(available) * ARRAYSIZE(available)]; /* Max possible formats */
51 static size_t count = 0;
52 static BOOL initialized = FALSE;
53
54 if (!initialized)
55 {
56 for (size_t dst = 0; dst < ARRAYSIZE(available); dst++)
57 {
58 const CAM_MEDIA_FORMAT dstFormat = available[dst];
59
60 for (size_t src = 0; src < ARRAYSIZE(available); src++)
61 {
62 const CAM_MEDIA_FORMAT srcFormat = available[src];
63 if (freerdp_video_conversion_supported(ecamToVideoFormat(srcFormat),
64 ecamToVideoFormat(dstFormat)))
65 {
66 formats[count++] = (CAM_MEDIA_FORMAT_INFO){ srcFormat, dstFormat };
67 }
68 }
69 }
70 initialized = TRUE;
71 }
72
73 *pCount = count;
74 return formats;
75}
76
77/* DecodingRequired describes the transmitted format: compressed H.264 and MJPEG
78 * samples need decoding on the server; raw samples do not. */
79WINPR_ATTR_NODISCARD
80static CAM_MEDIA_TYPE_DESCRIPTION_FLAGS ecam_media_type_flags(CAM_MEDIA_FORMAT format)
81{
82 if ((format == CAM_MEDIA_FORMAT_H264) || (format == CAM_MEDIA_FORMAT_MJPG))
83 return CAM_MEDIA_TYPE_DESCRIPTION_FLAG_DecodingRequired;
84 return AM_MEDIA_TYPE_DESCRIPTION_FLAG_Invalid;
85}
86
87static void ecam_dev_write_media_type(wStream* s, const CAM_MEDIA_TYPE_DESCRIPTION* mediaType)
88{
89 WINPR_ASSERT(mediaType);
90
91 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, mediaType->Format));
92 Stream_Write_UINT32(s, mediaType->Width);
93 Stream_Write_UINT32(s, mediaType->Height);
94 Stream_Write_UINT32(s, mediaType->FrameRateNumerator);
95 Stream_Write_UINT32(s, mediaType->FrameRateDenominator);
96 Stream_Write_UINT32(s, mediaType->PixelAspectRatioNumerator);
97 Stream_Write_UINT32(s, mediaType->PixelAspectRatioDenominator);
98 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, mediaType->Flags));
99}
100
101static BOOL ecam_dev_read_media_type(wStream* s, CAM_MEDIA_TYPE_DESCRIPTION* mediaType)
102{
103 WINPR_ASSERT(mediaType);
104
105 const uint8_t format = Stream_Get_UINT8(s);
106 if (!rdpecam_valid_CamMediaFormat(format))
107 return FALSE;
108
109 mediaType->Format = WINPR_ASSERTING_INT_CAST(CAM_MEDIA_FORMAT, format);
110 mediaType->Width = Stream_Get_UINT32(s);
111 mediaType->Height = Stream_Get_UINT32(s);
112 mediaType->FrameRateNumerator = Stream_Get_UINT32(s);
113 mediaType->FrameRateDenominator = Stream_Get_UINT32(s);
114 mediaType->PixelAspectRatioNumerator = Stream_Get_UINT32(s);
115 mediaType->PixelAspectRatioDenominator = Stream_Get_UINT32(s);
116
117 if (mediaType->FrameRateNumerator == 0)
118 return FALSE;
119 if (mediaType->FrameRateDenominator == 0)
120 return FALSE;
121 if (mediaType->PixelAspectRatioNumerator == 0)
122 return FALSE;
123 if (mediaType->PixelAspectRatioDenominator == 0)
124 return FALSE;
125
126 const uint8_t flags = Stream_Get_UINT8(s);
127 if (!rdpecam_valid_MediaTypeDescriptionFlags(flags))
128 return FALSE;
129 mediaType->Flags = WINPR_ASSERTING_INT_CAST(CAM_MEDIA_TYPE_DESCRIPTION_FLAGS, flags);
130 return TRUE;
131}
132
133static void ecam_dev_print_media_type(CAM_MEDIA_TYPE_DESCRIPTION* mediaType)
134{
135 WINPR_ASSERT(mediaType);
136
137 WLog_DBG(TAG, "Format: %u, width: %u, height: %u, fps: %u, flags: %u", mediaType->Format,
138 mediaType->Width, mediaType->Height, mediaType->FrameRateNumerator, mediaType->Flags);
139}
140
146WINPR_ATTR_NODISCARD
147static wStream* ecam_dev_prepare_sample_response(CameraDevice* dev, size_t streamIndex)
148{
149 WINPR_ASSERT(dev);
150
151 CameraDeviceStream* stream = &dev->streams[streamIndex];
152 CAM_MSG_ID msg = CAM_MSG_ID_SampleResponse;
153
154 Stream_ResetPosition(stream->sampleRespBuffer);
155 if (!Stream_EnsureRemainingCapacity(stream->sampleRespBuffer, 3))
156 return nullptr;
157
158 Stream_Write_UINT8(stream->sampleRespBuffer,
159 WINPR_ASSERTING_INT_CAST(uint8_t, dev->ecam->version));
160 Stream_Write_UINT8(stream->sampleRespBuffer, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
161 Stream_Write_UINT8(stream->sampleRespBuffer, WINPR_ASSERTING_INT_CAST(uint8_t, streamIndex));
162
163 return stream->sampleRespBuffer;
164}
165
166static BOOL mediaSupportDrops(CAM_MEDIA_FORMAT format)
167{
168 switch (format)
169 {
170 case CAM_MEDIA_FORMAT_H264:
171 return FALSE;
172 default:
173 return TRUE;
174 }
175}
176
177static UINT ecam_dev_send_sample_error_response(CameraDevice* dev,
178 GENERIC_CHANNEL_CALLBACK* hchannel,
179 size_t streamIndex, CAM_ERROR_CODE errorCode)
180{
181 WINPR_ASSERT(dev);
182
183 wStream* s = Stream_New(nullptr, CAM_HEADER_SIZE + 5);
184 if (!s)
185 return ERROR_NOT_ENOUGH_MEMORY;
186
187 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, dev->ecam->version));
188 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, CAM_MSG_ID_SampleErrorResponse));
189 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, streamIndex));
190 Stream_Write_UINT32(s, (UINT32)errorCode);
191
192 return ecam_channel_write(dev->ecam, hchannel, CAM_MSG_ID_SampleErrorResponse, s, TRUE);
193}
194
195static UINT ecam_dev_send_pending(CameraDevice* dev, size_t streamIndex, CameraDeviceStream* stream)
196{
197 WINPR_ASSERT(dev);
198 WINPR_ASSERT(stream);
199
200 if (stream->samplesRequested <= 0)
201 {
202 WLog_VRB(TAG, "Frame delayed: No sample requested");
203 return CHANNEL_RC_OK;
204 }
205
206 if (!stream->haveSample)
207 {
208 WLog_VRB(TAG, "Frame response delayed: No sample available");
209 return CHANNEL_RC_OK;
210 }
211
212 wStream* output = ecam_dev_prepare_sample_response(dev, streamIndex);
213 if (!output)
214 return CHANNEL_RC_OK;
215
216 const BYTE* encodedSample = Stream_Buffer(stream->pendingSample);
217 const size_t encodedSize = Stream_Length(stream->pendingSample);
218 if (!ecam_encoder_compress(stream, encodedSample, encodedSize, output))
219 {
220 WLog_DBG(TAG, "Frame dropped: error in ecam_encoder_compress");
221 stream->haveSample = FALSE;
222 return CHANNEL_RC_OK;
223 }
224
225 if (!stream->streaming)
226 {
227 WLog_DBG(TAG, "Frame delayed/dropped: stream stopped");
228 return CHANNEL_RC_OK;
229 }
230
231 stream->samplesRequested--;
232 stream->haveSample = FALSE;
233
234 /* channel write is protected by critical section in dvcman_write_channel */
235 return ecam_channel_write(dev->ecam, stream->hSampleReqChannel, CAM_MSG_ID_SampleResponse,
236 output, FALSE /* don't free stream */);
237}
238
239static UINT ecam_dev_sample_captured_callback(CameraDevice* dev, size_t streamIndex,
240 const BYTE* sample, size_t size)
241{
242 WINPR_ASSERT(dev);
243
244 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
245 return ERROR_INVALID_INDEX;
246
247 CameraDeviceStream* stream = &dev->streams[streamIndex];
248
249 if (!stream->streaming)
250 {
251 WLog_DBG(TAG, "Frame drop: stream not running");
252 return CHANNEL_RC_OK;
253 }
254
255 EnterCriticalSection(&stream->lock);
256 UINT ret = CHANNEL_RC_NO_MEMORY;
257 if (dev->ihal->RequestDrivenCapture && (stream->samplesRequested <= 0))
258 {
259 /* The server requests samples one at a time, so the count is zero between
260 * two requests. Keep capturing and only release the device once the server
261 * went quiet for a while, e.g. because the application closed the camera. */
262 const UINT64 idle = GetTickCount64() - stream->lastSampleRequestTime;
263 if (idle >= ECAM_CAPTURE_IDLE_TIMEOUT_MS)
264 {
265 WLog_DBG(TAG, "No sample requested for %" PRIu64 " ms, releasing capture device", idle);
266 stream->captureRunning = FALSE;
267 ret = ECAM_SAMPLE_CAPTURE_DRAINED;
268 goto out;
269 }
270 }
271
272 /* If we already have a waiting sample, let's see if the input format support dropping
273 * frames so that we could just "refresh" the pending sample, otherwise we must wait until
274 * a frame request flushes it
275 */
276
277 if (stream->haveSample && !mediaSupportDrops(stream->formats.inputFormat))
278 {
279 /* we can't drop samples, so we have to wait until the pending sample is
280 * sent, by a sample request.
281 *
282 * When we're here we already have a sample ready to be sent, the delay between 2 frames
283 * seems like a reasonable wait delay. For instance 60 FPS means a frame every 16ms.
284 * We also cap that wait delay to not spinloop and not get stuck for too long.
285 * */
286 DWORD waitDelay = (1000 * stream->currMediaType.FrameRateDenominator) /
287 stream->currMediaType.FrameRateNumerator;
288 if (waitDelay < 16)
289 waitDelay = 16;
290 if (waitDelay > 100)
291 waitDelay = 100;
292
293 while (stream->haveSample && stream->streaming)
294 {
295 LeaveCriticalSection(&stream->lock);
296
297 SleepEx(waitDelay, TRUE);
298
299 EnterCriticalSection(&stream->lock);
300 }
301
302 if (!stream->streaming)
303 {
304 WLog_DBG(TAG, "Frame drop: stream not running");
305 ret = CHANNEL_RC_OK;
306 goto out;
307 }
308 }
309
310 Stream_ResetPosition(stream->pendingSample);
311 if (!Stream_EnsureRemainingCapacity(stream->pendingSample, size))
312 goto out;
313
314 Stream_Write(stream->pendingSample, sample, size);
315 Stream_SealLength(stream->pendingSample);
316 stream->haveSample = TRUE;
317
318 ret = ecam_dev_send_pending(dev, streamIndex, stream);
319
320out:
321 LeaveCriticalSection(&stream->lock);
322 return ret;
323}
324
325static void ecam_dev_stop_stream(CameraDevice* dev, size_t streamIndex)
326{
327 WINPR_ASSERT(dev);
328
329 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
330 return;
331
332 CameraDeviceStream* stream = &dev->streams[streamIndex];
333
334 if (stream->streaming)
335 {
336 stream->streaming = FALSE;
337 stream->captureRunning = FALSE;
338 dev->ihal->StopStream(dev->ihal, dev->deviceId, 0);
339
340 DeleteCriticalSection(&stream->lock);
341 }
342
343 Stream_Free(stream->sampleRespBuffer, TRUE);
344 stream->sampleRespBuffer = nullptr;
345
346 Stream_Free(stream->pendingSample, TRUE);
347 stream->pendingSample = nullptr;
348
349 ecam_encoder_context_free(stream);
350}
351
357static UINT ecam_dev_process_stop_streams_request(CameraDevice* dev,
358 GENERIC_CHANNEL_CALLBACK* hchannel, wStream* s)
359{
360 WINPR_ASSERT(dev);
361 WINPR_UNUSED(s);
362
363 for (size_t i = 0; i < ECAM_DEVICE_MAX_STREAMS; i++)
364 ecam_dev_stop_stream(dev, i);
365
366 return ecam_channel_send_generic_msg(dev->ecam, hchannel, CAM_MSG_ID_SuccessResponse);
367}
368
369WINPR_ATTR_NODISCARD
370static BOOL media_type_valid(CameraDevice* dev, UINT8 streamIndex,
371 const CAM_MEDIA_TYPE_DESCRIPTION* type)
372{
373 WINPR_ASSERT(dev);
374 WINPR_ASSERT(type);
375
376 CAM_MEDIA_TYPE_DESCRIPTION supported[ECAM_MAX_MEDIA_TYPE_DESCRIPTORS] = WINPR_C_ARRAY_INIT;
377 size_t nMediaTypes = ARRAYSIZE(supported);
378
379 size_t nSupportedFormats = 0;
380 const CAM_MEDIA_FORMAT_INFO* supportedFormats = getSupportedFormats(&nSupportedFormats);
381 INT16 formatIndex =
382 dev->ihal->GetMediaTypeDescriptions(dev->ihal, dev->deviceId, streamIndex, supportedFormats,
383 nSupportedFormats, supported, &nMediaTypes);
384 if (formatIndex < 0)
385 return FALSE;
386
387 for (size_t x = 0; x < nMediaTypes; x++)
388 {
389 CAM_MEDIA_TYPE_DESCRIPTION cur = supported[x];
390 cur.Format = supportedFormats[formatIndex].outputFormat;
391 /* Validate the same wire-format flags advertised in the media type list. */
392 cur.Flags = ecam_media_type_flags(cur.Format);
393 if (memcmp(&cur, type, sizeof(CAM_MEDIA_TYPE_DESCRIPTION)) == 0)
394 return TRUE;
395 }
396 return FALSE;
397}
398
404static UINT ecam_dev_process_start_streams_request(CameraDevice* dev,
405 GENERIC_CHANNEL_CALLBACK* hchannel, wStream* s)
406{
407 BYTE streamIndex = 0;
408 CAM_MEDIA_TYPE_DESCRIPTION mediaType = WINPR_C_ARRAY_INIT;
409
410 WINPR_ASSERT(dev);
411
412 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1 + 26))
413 return ERROR_INVALID_DATA;
414
415 Stream_Read_UINT8(s, streamIndex);
416
417 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
418 {
419 WLog_ERR(TAG, "Incorrect streamIndex %" PRIu8, streamIndex);
420 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_InvalidStreamNumber);
421 return ERROR_INVALID_INDEX;
422 }
423
424 if (!ecam_dev_read_media_type(s, &mediaType))
425 {
426 WLog_ERR(TAG, "Unable to read MEDIA_TYPE_DESCRIPTION");
427 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_InvalidMessage);
428 return ERROR_INVALID_DATA;
429 }
430
431 ecam_dev_print_media_type(&mediaType);
432 if (!media_type_valid(dev, streamIndex, &mediaType))
433 {
434 WLog_ERR(TAG, "Unannounced MEDIA_TYPE_DESCRIPTION");
435 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_InvalidMessage);
436 return ERROR_INVALID_DATA;
437 }
438
439 CameraDeviceStream* stream = &dev->streams[streamIndex];
440
441 if (stream->streaming)
442 {
443 WLog_ERR(TAG, "Streaming already in progress, device %s, streamIndex %u", dev->deviceId,
444 streamIndex);
445 return CAM_ERROR_CODE_UnexpectedError;
446 }
447
448 /* saving media type description for CurrentMediaTypeRequest,
449 * to be done before calling ecam_encoder_context_init
450 */
451 stream->currMediaType = mediaType;
452
453 if (!ecam_encoder_context_init(stream))
454 {
455 WLog_ERR(TAG, "stream_ecam_encoder_init failed");
456 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_UnexpectedError);
457 return ERROR_INVALID_DATA;
458 }
459
460 stream->sampleRespBuffer = Stream_New(nullptr, ECAM_SAMPLE_RESPONSE_BUFFER_SIZE);
461 if (!stream->sampleRespBuffer)
462 {
463 WLog_ERR(TAG, "Stream_New failed");
464 ecam_dev_stop_stream(dev, streamIndex);
465 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_OutOfMemory);
466 return ERROR_INVALID_DATA;
467 }
468
469 /* replacing outputFormat with inputFormat in mediaType before starting stream */
470 mediaType.Format = streamInputFormat(stream);
471
472 stream->samplesRequested = 0;
473 stream->lastSampleRequestTime = GetTickCount64();
474 stream->haveSample = FALSE;
475
476 if (!InitializeCriticalSectionEx(&stream->lock, 0, 0))
477 {
478 WLog_ERR(TAG, "InitializeCriticalSectionEx failed");
479 ecam_dev_stop_stream(dev, streamIndex);
480 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_OutOfMemory);
481 return ERROR_INVALID_DATA;
482 }
483
484 stream->pendingSample = Stream_New(nullptr, 4ull * mediaType.Width * mediaType.Height);
485 if (!stream->pendingSample)
486 {
487 WLog_ERR(TAG, "pending stream failed");
488 ecam_dev_stop_stream(dev, streamIndex);
489 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_OutOfMemory);
490 return ERROR_INVALID_DATA;
491 }
492
493 stream->captureRunning = TRUE;
494 const CAM_ERROR_CODE error = dev->ihal->StartStream(dev->ihal, dev, streamIndex, &mediaType,
495 ecam_dev_sample_captured_callback);
496 if (error)
497 {
498 stream->captureRunning = FALSE;
499 WLog_ERR(TAG, "StartStream failure");
500 ecam_dev_stop_stream(dev, streamIndex);
501 ecam_channel_send_error_response(dev->ecam, hchannel, error);
502 return ERROR_INVALID_DATA;
503 }
504
505 stream->streaming = TRUE;
506 return ecam_channel_send_generic_msg(dev->ecam, hchannel, CAM_MSG_ID_SuccessResponse);
507}
508
514static UINT ecam_dev_process_property_list_request(CameraDevice* dev,
515 GENERIC_CHANNEL_CALLBACK* hchannel,
516 WINPR_ATTR_UNUSED wStream* s)
517{
518 WINPR_ASSERT(dev);
519 // TODO: supported properties implementation
520
521 return ecam_channel_send_generic_msg(dev->ecam, hchannel, CAM_MSG_ID_PropertyListResponse);
522}
523
529static UINT ecam_dev_send_current_media_type_response(CameraDevice* dev,
530 GENERIC_CHANNEL_CALLBACK* hchannel,
532{
533 CAM_MSG_ID msg = CAM_MSG_ID_CurrentMediaTypeResponse;
534
535 WINPR_ASSERT(dev);
536
537 wStream* s = Stream_New(nullptr, CAM_HEADER_SIZE + sizeof(CAM_MEDIA_TYPE_DESCRIPTION));
538 if (!s)
539 {
540 WLog_ERR(TAG, "Stream_New failed");
541 return ERROR_NOT_ENOUGH_MEMORY;
542 }
543
544 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, dev->ecam->version));
545 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
546
547 ecam_dev_write_media_type(s, mediaType);
548
549 return ecam_channel_write(dev->ecam, hchannel, msg, s, TRUE);
550}
551
557static UINT ecam_dev_process_sample_request(CameraDevice* dev, GENERIC_CHANNEL_CALLBACK* hchannel,
558 wStream* s)
559{
560 BYTE streamIndex = 0;
561
562 WINPR_ASSERT(dev);
563
564 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
565 return ERROR_INVALID_DATA;
566
567 Stream_Read_UINT8(s, streamIndex);
568
569 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
570 {
571 WLog_ERR(TAG, "Incorrect streamIndex %u", streamIndex);
572 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_InvalidStreamNumber);
573 return ERROR_INVALID_INDEX;
574 }
575
576 CameraDeviceStream* stream = &dev->streams[streamIndex];
577 BOOL startCapture = FALSE;
578 CAM_MEDIA_TYPE_DESCRIPTION mediaType = WINPR_C_ARRAY_INIT;
579
580 EnterCriticalSection(&stream->lock);
581
582 /* need to save channel because responses are asynchronous and coming from capture thread */
583 if (stream->hSampleReqChannel != hchannel)
584 stream->hSampleReqChannel = hchannel;
585
586 stream->samplesRequested++;
587 stream->lastSampleRequestTime = GetTickCount64();
588 const UINT ret = ecam_dev_send_pending(dev, streamIndex, stream);
589 if (dev->ihal->RequestDrivenCapture && (ret == CHANNEL_RC_OK) && !stream->captureRunning)
590 {
591 stream->captureRunning = TRUE;
592 mediaType = stream->currMediaType;
593 mediaType.Format = streamInputFormat(stream);
594 startCapture = TRUE;
595 }
596
597 LeaveCriticalSection(&stream->lock);
598
599 if (startCapture)
600 {
601 WLog_DBG(TAG, "Sample requested, restarting capture device");
602 const CAM_ERROR_CODE error = dev->ihal->StartStream(dev->ihal, dev, streamIndex, &mediaType,
603 ecam_dev_sample_captured_callback);
604 if (error)
605 {
606 WLog_ERR(TAG, "StartStream failure");
607 EnterCriticalSection(&stream->lock);
608 stream->captureRunning = FALSE;
609 if (stream->samplesRequested > 0)
610 stream->samplesRequested--;
611 LeaveCriticalSection(&stream->lock);
612 ecam_dev_send_sample_error_response(dev, hchannel, streamIndex, error);
613 return ERROR_INVALID_DATA;
614 }
615 }
616
617 return ret;
618}
619
625static UINT ecam_dev_process_current_media_type_request(CameraDevice* dev,
626 GENERIC_CHANNEL_CALLBACK* hchannel,
627 wStream* s)
628{
629 BYTE streamIndex = 0;
630
631 WINPR_ASSERT(dev);
632
633 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
634 return ERROR_INVALID_DATA;
635
636 Stream_Read_UINT8(s, streamIndex);
637
638 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
639 {
640 WLog_ERR(TAG, "Incorrect streamIndex %u", streamIndex);
641 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_InvalidStreamNumber);
642 return ERROR_INVALID_INDEX;
643 }
644
645 CameraDeviceStream* stream = &dev->streams[streamIndex];
646
647 if (stream->currMediaType.Format == 0)
648 {
649 WLog_ERR(TAG, "Current media type unknown for streamIndex %u", streamIndex);
650 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_NotInitialized);
651 return ERROR_DEVICE_REINITIALIZATION_NEEDED;
652 }
653
654 return ecam_dev_send_current_media_type_response(dev, hchannel, &stream->currMediaType);
655}
656
662static UINT ecam_dev_send_media_type_list_response(CameraDevice* dev,
663 GENERIC_CHANNEL_CALLBACK* hchannel,
664 CAM_MEDIA_TYPE_DESCRIPTION* mediaTypes,
665 size_t nMediaTypes)
666{
667 CAM_MSG_ID msg = CAM_MSG_ID_MediaTypeListResponse;
668
669 WINPR_ASSERT(dev);
670
671 wStream* s = Stream_New(nullptr, CAM_HEADER_SIZE + ECAM_MAX_MEDIA_TYPE_DESCRIPTORS *
673 if (!s)
674 {
675 WLog_ERR(TAG, "Stream_New failed");
676 return ERROR_NOT_ENOUGH_MEMORY;
677 }
678
679 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, dev->ecam->version));
680 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
681
682 for (size_t i = 0; i < nMediaTypes; i++, mediaTypes++)
683 {
684 ecam_dev_write_media_type(s, mediaTypes);
685 }
686
687 return ecam_channel_write(dev->ecam, hchannel, msg, s, TRUE);
688}
689
695static UINT ecam_dev_process_media_type_list_request(CameraDevice* dev,
696 GENERIC_CHANNEL_CALLBACK* hchannel, wStream* s)
697{
698 UINT error = CHANNEL_RC_OK;
699 BYTE streamIndex = 0;
700 CAM_MEDIA_TYPE_DESCRIPTION* mediaTypes = nullptr;
701 size_t nMediaTypes = ECAM_MAX_MEDIA_TYPE_DESCRIPTORS;
702
703 WINPR_ASSERT(dev);
704
705 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
706 return ERROR_INVALID_DATA;
707
708 Stream_Read_UINT8(s, streamIndex);
709
710 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
711 {
712 WLog_ERR(TAG, "Incorrect streamIndex %u", streamIndex);
713 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_InvalidStreamNumber);
714 return ERROR_INVALID_INDEX;
715 }
716 CameraDeviceStream* stream = &dev->streams[streamIndex];
717
718 mediaTypes =
719 (CAM_MEDIA_TYPE_DESCRIPTION*)calloc(nMediaTypes, sizeof(CAM_MEDIA_TYPE_DESCRIPTION));
720 if (!mediaTypes)
721 {
722 WLog_ERR(TAG, "calloc failed");
723 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_OutOfMemory);
724 return CHANNEL_RC_NO_MEMORY;
725 }
726
727 size_t nSupportedFormats = 0;
728 const CAM_MEDIA_FORMAT_INFO* supportedFormats = getSupportedFormats(&nSupportedFormats);
729
730 INT16 formatIndex =
731 dev->ihal->GetMediaTypeDescriptions(dev->ihal, dev->deviceId, streamIndex, supportedFormats,
732 nSupportedFormats, mediaTypes, &nMediaTypes);
733 if (formatIndex == -1 || nMediaTypes == 0)
734 {
735 WLog_ERR(TAG, "Camera doesn't support any compatible video formats");
736 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_ItemNotFound);
737 error = ERROR_DEVICE_FEATURE_NOT_SUPPORTED;
738 goto error;
739 }
740
741 stream->formats = supportedFormats[formatIndex];
742
743 /* replacing inputFormat with outputFormat in mediaTypes before sending response */
744 for (size_t i = 0; i < nMediaTypes; i++)
745 {
746 mediaTypes[i].Format = streamOutputFormat(stream);
747 /* Set decoding requirements for the transmitted format, not the capture format. */
748 mediaTypes[i].Flags = ecam_media_type_flags(mediaTypes[i].Format);
749 }
750
751 if (stream->currMediaType.Format == 0)
752 {
753 /* saving 1st media type description for CurrentMediaTypeRequest */
754 stream->currMediaType = mediaTypes[0];
755 }
756
757 error = ecam_dev_send_media_type_list_response(dev, hchannel, mediaTypes, nMediaTypes);
758
759error:
760 free(mediaTypes);
761 return error;
762}
763
769static UINT ecam_dev_send_stream_list_response(CameraDevice* dev,
770 GENERIC_CHANNEL_CALLBACK* hchannel)
771{
772 CAM_MSG_ID msg = CAM_MSG_ID_StreamListResponse;
773
774 WINPR_ASSERT(dev);
775
776 wStream* s = Stream_New(nullptr, CAM_HEADER_SIZE + sizeof(CAM_STREAM_DESCRIPTION));
777 if (!s)
778 {
779 WLog_ERR(TAG, "Stream_New failed");
780 return ERROR_NOT_ENOUGH_MEMORY;
781 }
782
783 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, dev->ecam->version));
784 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
785
786 /* single stream description */
787 Stream_Write_UINT16(s, CAM_STREAM_FRAME_SOURCE_TYPE_Color);
788 Stream_Write_UINT8(s, CAM_STREAM_CATEGORY_Capture);
789 Stream_Write_UINT8(s, TRUE /* Selected */);
790 Stream_Write_UINT8(s, FALSE /* CanBeShared */);
791
792 return ecam_channel_write(dev->ecam, hchannel, msg, s, TRUE);
793}
794
800static UINT ecam_dev_process_stream_list_request(CameraDevice* dev,
801 GENERIC_CHANNEL_CALLBACK* hchannel,
802 WINPR_ATTR_UNUSED wStream* s)
803{
804 return ecam_dev_send_stream_list_response(dev, hchannel);
805}
806
812static UINT ecam_dev_process_activate_device_request(CameraDevice* dev,
813 GENERIC_CHANNEL_CALLBACK* hchannel,
814 WINPR_ATTR_UNUSED wStream* s)
815{
816 WINPR_ASSERT(dev);
817 CAM_ERROR_CODE errorCode = CAM_ERROR_CODE_None;
818
819 if (dev->ihal->Activate(dev->ihal, dev->deviceId, &errorCode))
820 return ecam_channel_send_generic_msg(dev->ecam, hchannel, CAM_MSG_ID_SuccessResponse);
821
822 return ecam_channel_send_error_response(dev->ecam, hchannel, errorCode);
823}
824
830static UINT ecam_dev_process_deactivate_device_request(CameraDevice* dev,
831 GENERIC_CHANNEL_CALLBACK* hchannel,
832 wStream* s)
833{
834 WINPR_ASSERT(dev);
835 WINPR_UNUSED(s);
836
837 for (size_t i = 0; i < ECAM_DEVICE_MAX_STREAMS; i++)
838 ecam_dev_stop_stream(dev, i);
839
840 CAM_ERROR_CODE errorCode = CAM_ERROR_CODE_None;
841 if (dev->ihal->Deactivate(dev->ihal, dev->deviceId, &errorCode))
842 return ecam_channel_send_generic_msg(dev->ecam, hchannel, CAM_MSG_ID_SuccessResponse);
843
844 return ecam_channel_send_error_response(dev->ecam, hchannel, errorCode);
845}
846
852static UINT ecam_dev_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
853{
854 UINT error = CHANNEL_RC_OK;
855 BYTE version = 0;
856 BYTE messageId = 0;
857 GENERIC_CHANNEL_CALLBACK* hchannel = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
858
859 if (!hchannel || !data)
860 return ERROR_INVALID_PARAMETER;
861
862 CameraDevice* dev = (CameraDevice*)hchannel->plugin;
863
864 if (!dev)
865 return ERROR_INTERNAL_ERROR;
866
867 if (!Stream_CheckAndLogRequiredLength(TAG, data, CAM_HEADER_SIZE))
868 return ERROR_NO_DATA;
869
870 Stream_Read_UINT8(data, version);
871 Stream_Read_UINT8(data, messageId);
872 WLog_DBG(TAG, "ChannelId=%" PRIu32 ", MessageId=0x%02" PRIx8 ", Version=%d",
873 hchannel->channel_mgr->GetChannelId(hchannel->channel), messageId, version);
874
875 switch (messageId)
876 {
877 case CAM_MSG_ID_ActivateDeviceRequest:
878 error = ecam_dev_process_activate_device_request(dev, hchannel, data);
879 break;
880
881 case CAM_MSG_ID_DeactivateDeviceRequest:
882 error = ecam_dev_process_deactivate_device_request(dev, hchannel, data);
883 break;
884
885 case CAM_MSG_ID_StreamListRequest:
886 error = ecam_dev_process_stream_list_request(dev, hchannel, data);
887 break;
888
889 case CAM_MSG_ID_MediaTypeListRequest:
890 error = ecam_dev_process_media_type_list_request(dev, hchannel, data);
891 break;
892
893 case CAM_MSG_ID_CurrentMediaTypeRequest:
894 error = ecam_dev_process_current_media_type_request(dev, hchannel, data);
895 break;
896
897 case CAM_MSG_ID_PropertyListRequest:
898 error = ecam_dev_process_property_list_request(dev, hchannel, data);
899 break;
900
901 case CAM_MSG_ID_StartStreamsRequest:
902 error = ecam_dev_process_start_streams_request(dev, hchannel, data);
903 break;
904
905 case CAM_MSG_ID_StopStreamsRequest:
906 error = ecam_dev_process_stop_streams_request(dev, hchannel, data);
907 break;
908
909 case CAM_MSG_ID_SampleRequest:
910 error = ecam_dev_process_sample_request(dev, hchannel, data);
911 break;
912
913 default:
914 WLog_WARN(TAG, "unknown MessageId=0x%02" PRIx8 "", messageId);
915 error = ERROR_INVALID_DATA;
916 ecam_channel_send_error_response(dev->ecam, hchannel,
917 CAM_ERROR_CODE_OperationNotSupported);
918 break;
919 }
920
921 return error;
922}
923
929static UINT ecam_dev_on_open(WINPR_ATTR_UNUSED IWTSVirtualChannelCallback* pChannelCallback)
930{
931 WLog_DBG(TAG, "entered");
932 return CHANNEL_RC_OK;
933}
934
940static UINT ecam_dev_on_close(IWTSVirtualChannelCallback* pChannelCallback)
941{
942 GENERIC_CHANNEL_CALLBACK* hchannel = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
943 WINPR_ASSERT(hchannel);
944
945 CameraDevice* dev = (CameraDevice*)hchannel->plugin;
946 WINPR_ASSERT(dev);
947
948 WLog_DBG(TAG, "entered");
949
950 for (size_t i = 0; i < ECAM_DEVICE_MAX_STREAMS; i++)
951 ecam_dev_stop_stream(dev, i);
952
953 /* make sure this channel is not used for sample responses */
954 for (size_t i = 0; i < ECAM_DEVICE_MAX_STREAMS; i++)
955 if (dev->streams[i].hSampleReqChannel == hchannel)
956 dev->streams[i].hSampleReqChannel = nullptr;
957
958 free(hchannel);
959 return CHANNEL_RC_OK;
960}
961
967static UINT ecam_dev_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
968 IWTSVirtualChannel* pChannel,
969 WINPR_ATTR_UNUSED BYTE* Data,
970 WINPR_ATTR_UNUSED BOOL* pbAccept,
971 IWTSVirtualChannelCallback** ppCallback)
972{
973 GENERIC_LISTENER_CALLBACK* hlistener = (GENERIC_LISTENER_CALLBACK*)pListenerCallback;
974
975 if (!hlistener || !hlistener->plugin)
976 return ERROR_INTERNAL_ERROR;
977
978 WLog_DBG(TAG, "entered");
979 GENERIC_CHANNEL_CALLBACK* hchannel =
981
982 if (!hchannel)
983 {
984 WLog_ERR(TAG, "calloc failed");
985 return CHANNEL_RC_NO_MEMORY;
986 }
987
988 hchannel->iface.OnDataReceived = ecam_dev_on_data_received;
989 hchannel->iface.OnOpen = ecam_dev_on_open;
990 hchannel->iface.OnClose = ecam_dev_on_close;
991 hchannel->plugin = hlistener->plugin;
992 hchannel->channel_mgr = hlistener->channel_mgr;
993 hchannel->channel = pChannel;
994 *ppCallback = (IWTSVirtualChannelCallback*)hchannel;
995 return CHANNEL_RC_OK;
996}
997
1003CameraDevice* ecam_dev_create(CameraPlugin* ecam, const char* deviceId,
1004 WINPR_ATTR_UNUSED const char* deviceName)
1005{
1006 WINPR_ASSERT(ecam);
1007 WINPR_ASSERT(ecam->hlistener);
1008
1009 IWTSVirtualChannelManager* pChannelMgr = ecam->hlistener->channel_mgr;
1010 WINPR_ASSERT(pChannelMgr);
1011
1012 WLog_DBG(TAG, "entered for %s", deviceId);
1013
1014 CameraDevice* dev = (CameraDevice*)calloc(1, sizeof(CameraDevice));
1015
1016 if (!dev)
1017 {
1018 WLog_ERR(TAG, "calloc failed");
1019 return nullptr;
1020 }
1021
1022 dev->ecam = ecam;
1023 dev->ihal = ecam->ihal;
1024 strncpy(dev->deviceId, deviceId, sizeof(dev->deviceId) - 1);
1025 dev->hlistener = (GENERIC_LISTENER_CALLBACK*)calloc(1, sizeof(GENERIC_LISTENER_CALLBACK));
1026
1027 if (!dev->hlistener)
1028 {
1029 free(dev);
1030 WLog_ERR(TAG, "calloc failed");
1031 return nullptr;
1032 }
1033
1034 dev->hlistener->iface.OnNewChannelConnection = ecam_dev_on_new_channel_connection;
1035 dev->hlistener->plugin = (IWTSPlugin*)dev;
1036 dev->hlistener->channel_mgr = pChannelMgr;
1037 if (CHANNEL_RC_OK != pChannelMgr->CreateListener(pChannelMgr, deviceId, 0,
1038 &dev->hlistener->iface, &dev->listener))
1039 {
1040 free(dev->hlistener);
1041 free(dev);
1042 WLog_ERR(TAG, "CreateListener failed");
1043 return nullptr;
1044 }
1045
1046 return dev;
1047}
1048
1055void ecam_dev_destroy(CameraDevice* dev)
1056{
1057 if (!dev)
1058 return;
1059
1060 WLog_DBG(TAG, "entered for %s", dev->deviceId);
1061
1062 if (dev->hlistener)
1063 {
1064 IWTSVirtualChannelManager* mgr = dev->hlistener->channel_mgr;
1065 if (mgr)
1066 IFCALL(mgr->DestroyListener, mgr, dev->listener);
1067 }
1068
1069 free(dev->hlistener);
1070
1071 for (size_t i = 0; i < ECAM_DEVICE_MAX_STREAMS; i++)
1072 ecam_dev_stop_stream(dev, i);
1073
1074 free(dev);
1075}