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