20#include <winpr/assert.h>
21#include <winpr/cast.h>
22#include <winpr/interlocked.h>
25#include "rdpecam-utils.h"
27#define TAG CHANNELS_TAG("rdpecam-device.client")
42 const CAM_MEDIA_FORMAT available[] = { CAM_MEDIA_FORMAT_H264, CAM_MEDIA_FORMAT_MJPG,
43 CAM_MEDIA_FORMAT_YUY2, CAM_MEDIA_FORMAT_NV12,
44 CAM_MEDIA_FORMAT_I420, CAM_MEDIA_FORMAT_RGB24,
45 CAM_MEDIA_FORMAT_RGB32 };
48 formats[ARRAYSIZE(available) * ARRAYSIZE(available)];
49 static size_t count = 0;
50 static BOOL initialized = FALSE;
54 for (
size_t dst = 0; dst < ARRAYSIZE(available); dst++)
56 const CAM_MEDIA_FORMAT dstFormat = available[dst];
58 for (
size_t src = 0; src < ARRAYSIZE(available); src++)
60 const CAM_MEDIA_FORMAT srcFormat = available[src];
61 if (freerdp_video_conversion_supported(ecamToVideoFormat(srcFormat),
62 ecamToVideoFormat(dstFormat)))
77 WINPR_ASSERT(mediaType);
79 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, mediaType->Format));
80 Stream_Write_UINT32(s, mediaType->Width);
81 Stream_Write_UINT32(s, mediaType->Height);
82 Stream_Write_UINT32(s, mediaType->FrameRateNumerator);
83 Stream_Write_UINT32(s, mediaType->FrameRateDenominator);
84 Stream_Write_UINT32(s, mediaType->PixelAspectRatioNumerator);
85 Stream_Write_UINT32(s, mediaType->PixelAspectRatioDenominator);
86 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, mediaType->Flags));
91 WINPR_ASSERT(mediaType);
93 const uint8_t format = Stream_Get_UINT8(s);
94 if (!rdpecam_valid_CamMediaFormat(format))
97 mediaType->Format = WINPR_ASSERTING_INT_CAST(CAM_MEDIA_FORMAT, format);
98 Stream_Read_UINT32(s, mediaType->Width);
99 Stream_Read_UINT32(s, mediaType->Height);
100 Stream_Read_UINT32(s, mediaType->FrameRateNumerator);
101 Stream_Read_UINT32(s, mediaType->FrameRateDenominator);
102 Stream_Read_UINT32(s, mediaType->PixelAspectRatioNumerator);
103 Stream_Read_UINT32(s, mediaType->PixelAspectRatioDenominator);
105 const uint8_t flags = Stream_Get_UINT8(s);
106 if (!rdpecam_valid_MediaTypeDescriptionFlags(flags))
108 mediaType->Flags = WINPR_ASSERTING_INT_CAST(CAM_MEDIA_TYPE_DESCRIPTION_FLAGS, flags);
114 WINPR_ASSERT(mediaType);
116 WLog_DBG(TAG,
"Format: %u, width: %u, height: %u, fps: %u, flags: %u", mediaType->Format,
117 mediaType->Width, mediaType->Height, mediaType->FrameRateNumerator, mediaType->Flags);
131 CAM_MSG_ID msg = CAM_MSG_ID_SampleResponse;
133 Stream_ResetPosition(stream->sampleRespBuffer);
134 if (!Stream_EnsureRemainingCapacity(stream->sampleRespBuffer, 3))
137 Stream_Write_UINT8(stream->sampleRespBuffer,
138 WINPR_ASSERTING_INT_CAST(uint8_t, dev->ecam->version));
139 Stream_Write_UINT8(stream->sampleRespBuffer, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
140 Stream_Write_UINT8(stream->sampleRespBuffer, WINPR_ASSERTING_INT_CAST(uint8_t, streamIndex));
142 return stream->sampleRespBuffer;
145static BOOL mediaSupportDrops(CAM_MEDIA_FORMAT format)
149 case CAM_MEDIA_FORMAT_H264:
159 WINPR_ASSERT(stream);
161 if (stream->samplesRequested <= 0)
163 WLog_VRB(TAG,
"Frame delayed: No sample requested");
164 return CHANNEL_RC_OK;
167 if (!stream->haveSample)
169 WLog_VRB(TAG,
"Frame response delayed: No sample available");
170 return CHANNEL_RC_OK;
173 wStream* output = ecam_dev_prepare_sample_response(dev, streamIndex);
175 return CHANNEL_RC_OK;
177 const BYTE* encodedSample = Stream_Buffer(stream->pendingSample);
178 const size_t encodedSize = Stream_Length(stream->pendingSample);
179 if (!ecam_encoder_compress(stream, encodedSample, encodedSize, output))
181 WLog_DBG(TAG,
"Frame dropped: error in ecam_encoder_compress");
182 stream->haveSample = FALSE;
183 return CHANNEL_RC_OK;
186 if (!stream->streaming)
188 WLog_DBG(TAG,
"Frame delayed/dropped: stream stopped");
189 return CHANNEL_RC_OK;
192 stream->samplesRequested--;
193 stream->haveSample = FALSE;
196 return ecam_channel_write(dev->ecam, stream->hSampleReqChannel, CAM_MSG_ID_SampleResponse,
200static UINT ecam_dev_sample_captured_callback(
CameraDevice* dev,
size_t streamIndex,
201 const BYTE* sample,
size_t size)
205 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
206 return ERROR_INVALID_INDEX;
210 if (!stream->streaming)
212 WLog_DBG(TAG,
"Frame drop: stream not running");
213 return CHANNEL_RC_OK;
216 EnterCriticalSection(&stream->lock);
217 UINT ret = CHANNEL_RC_NO_MEMORY;
224 if (stream->haveSample && !mediaSupportDrops(stream->formats.inputFormat))
233 DWORD waitDelay = (1000 * stream->currMediaType.FrameRateDenominator) /
234 stream->currMediaType.FrameRateNumerator;
240 while (stream->haveSample && stream->streaming)
242 LeaveCriticalSection(&stream->lock);
244 SleepEx(waitDelay, TRUE);
246 EnterCriticalSection(&stream->lock);
249 if (!stream->streaming)
251 WLog_DBG(TAG,
"Frame drop: stream not running");
257 Stream_ResetPosition(stream->pendingSample);
258 if (!Stream_EnsureRemainingCapacity(stream->pendingSample, size))
261 Stream_Write(stream->pendingSample, sample, size);
262 Stream_SealLength(stream->pendingSample);
263 stream->haveSample = TRUE;
265 ret = ecam_dev_send_pending(dev, streamIndex, stream);
268 LeaveCriticalSection(&stream->lock);
272static void ecam_dev_stop_stream(
CameraDevice* dev,
size_t streamIndex)
276 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
281 if (stream->streaming)
283 stream->streaming = FALSE;
284 dev->ihal->StopStream(dev->ihal, dev->deviceId, 0);
286 DeleteCriticalSection(&stream->lock);
289 Stream_Free(stream->sampleRespBuffer, TRUE);
290 stream->sampleRespBuffer =
nullptr;
292 Stream_Free(stream->pendingSample, TRUE);
293 stream->pendingSample =
nullptr;
295 ecam_encoder_context_free(stream);
303static UINT ecam_dev_process_stop_streams_request(
CameraDevice* dev,
309 for (
size_t i = 0; i < ECAM_DEVICE_MAX_STREAMS; i++)
310 ecam_dev_stop_stream(dev, i);
312 return ecam_channel_send_generic_msg(dev->ecam, hchannel, CAM_MSG_ID_SuccessResponse);
320static UINT ecam_dev_process_start_streams_request(
CameraDevice* dev,
323 BYTE streamIndex = 0;
328 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1 + 26))
329 return ERROR_INVALID_DATA;
331 Stream_Read_UINT8(s, streamIndex);
333 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
335 WLog_ERR(TAG,
"Incorrect streamIndex %" PRIu8, streamIndex);
336 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_InvalidStreamNumber);
337 return ERROR_INVALID_INDEX;
340 if (!ecam_dev_read_media_type(s, &mediaType))
342 WLog_ERR(TAG,
"Unable to read MEDIA_TYPE_DESCRIPTION");
343 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_InvalidMessage);
344 return ERROR_INVALID_DATA;
347 ecam_dev_print_media_type(&mediaType);
351 if (stream->streaming)
353 WLog_ERR(TAG,
"Streaming already in progress, device %s, streamIndex %u", dev->deviceId,
355 return CAM_ERROR_CODE_UnexpectedError;
361 stream->currMediaType = mediaType;
363 if (!ecam_encoder_context_init(stream))
365 WLog_ERR(TAG,
"stream_ecam_encoder_init failed");
366 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_UnexpectedError);
367 return ERROR_INVALID_DATA;
370 stream->sampleRespBuffer = Stream_New(
nullptr, ECAM_SAMPLE_RESPONSE_BUFFER_SIZE);
371 if (!stream->sampleRespBuffer)
373 WLog_ERR(TAG,
"Stream_New failed");
374 ecam_dev_stop_stream(dev, streamIndex);
375 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_OutOfMemory);
376 return ERROR_INVALID_DATA;
380 mediaType.Format = streamInputFormat(stream);
382 stream->samplesRequested = 0;
383 stream->haveSample = FALSE;
385 if (!InitializeCriticalSectionEx(&stream->lock, 0, 0))
387 WLog_ERR(TAG,
"InitializeCriticalSectionEx failed");
388 ecam_dev_stop_stream(dev, streamIndex);
389 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_OutOfMemory);
390 return ERROR_INVALID_DATA;
393 stream->pendingSample = Stream_New(
nullptr, 4ull * mediaType.Width * mediaType.Height);
394 if (!stream->pendingSample)
396 WLog_ERR(TAG,
"pending stream failed");
397 ecam_dev_stop_stream(dev, streamIndex);
398 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_OutOfMemory);
399 return ERROR_INVALID_DATA;
402 const CAM_ERROR_CODE error = dev->ihal->StartStream(dev->ihal, dev, streamIndex, &mediaType,
403 ecam_dev_sample_captured_callback);
406 WLog_ERR(TAG,
"StartStream failure");
407 ecam_dev_stop_stream(dev, streamIndex);
408 ecam_channel_send_error_response(dev->ecam, hchannel, error);
409 return ERROR_INVALID_DATA;
412 stream->streaming = TRUE;
413 return ecam_channel_send_generic_msg(dev->ecam, hchannel, CAM_MSG_ID_SuccessResponse);
421static UINT ecam_dev_process_property_list_request(
CameraDevice* dev,
428 return ecam_channel_send_generic_msg(dev->ecam, hchannel, CAM_MSG_ID_PropertyListResponse);
436static UINT ecam_dev_send_current_media_type_response(
CameraDevice* dev,
440 CAM_MSG_ID msg = CAM_MSG_ID_CurrentMediaTypeResponse;
447 WLog_ERR(TAG,
"Stream_New failed");
448 return ERROR_NOT_ENOUGH_MEMORY;
451 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, dev->ecam->version));
452 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
454 ecam_dev_write_media_type(s, mediaType);
456 return ecam_channel_write(dev->ecam, hchannel, msg, s, TRUE);
467 BYTE streamIndex = 0;
471 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
472 return ERROR_INVALID_DATA;
474 Stream_Read_UINT8(s, streamIndex);
476 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
478 WLog_ERR(TAG,
"Incorrect streamIndex %u", streamIndex);
479 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_InvalidStreamNumber);
480 return ERROR_INVALID_INDEX;
485 EnterCriticalSection(&stream->lock);
488 if (stream->hSampleReqChannel != hchannel)
489 stream->hSampleReqChannel = hchannel;
491 stream->samplesRequested++;
492 const UINT ret = ecam_dev_send_pending(dev, streamIndex, stream);
494 LeaveCriticalSection(&stream->lock);
503static UINT ecam_dev_process_current_media_type_request(
CameraDevice* dev,
507 BYTE streamIndex = 0;
511 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
512 return ERROR_INVALID_DATA;
514 Stream_Read_UINT8(s, streamIndex);
516 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
518 WLog_ERR(TAG,
"Incorrect streamIndex %u", streamIndex);
519 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_InvalidStreamNumber);
520 return ERROR_INVALID_INDEX;
525 if (stream->currMediaType.Format == 0)
527 WLog_ERR(TAG,
"Current media type unknown for streamIndex %u", streamIndex);
528 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_NotInitialized);
529 return ERROR_DEVICE_REINITIALIZATION_NEEDED;
532 return ecam_dev_send_current_media_type_response(dev, hchannel, &stream->currMediaType);
540static UINT ecam_dev_send_media_type_list_response(
CameraDevice* dev,
545 CAM_MSG_ID msg = CAM_MSG_ID_MediaTypeListResponse;
549 wStream* s = Stream_New(
nullptr, CAM_HEADER_SIZE + ECAM_MAX_MEDIA_TYPE_DESCRIPTORS *
553 WLog_ERR(TAG,
"Stream_New failed");
554 return ERROR_NOT_ENOUGH_MEMORY;
557 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, dev->ecam->version));
558 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
560 for (
size_t i = 0; i < nMediaTypes; i++, mediaTypes++)
562 ecam_dev_write_media_type(s, mediaTypes);
565 return ecam_channel_write(dev->ecam, hchannel, msg, s, TRUE);
573static UINT ecam_dev_process_media_type_list_request(
CameraDevice* dev,
576 UINT error = CHANNEL_RC_OK;
577 BYTE streamIndex = 0;
579 size_t nMediaTypes = ECAM_MAX_MEDIA_TYPE_DESCRIPTORS;
583 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
584 return ERROR_INVALID_DATA;
586 Stream_Read_UINT8(s, streamIndex);
588 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
590 WLog_ERR(TAG,
"Incorrect streamIndex %u", streamIndex);
591 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_InvalidStreamNumber);
592 return ERROR_INVALID_INDEX;
600 WLog_ERR(TAG,
"calloc failed");
601 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_OutOfMemory);
602 return CHANNEL_RC_NO_MEMORY;
605 size_t nSupportedFormats = 0;
609 dev->ihal->GetMediaTypeDescriptions(dev->ihal, dev->deviceId, streamIndex, supportedFormats,
610 nSupportedFormats, mediaTypes, &nMediaTypes);
611 if (formatIndex == -1 || nMediaTypes == 0)
613 WLog_ERR(TAG,
"Camera doesn't support any compatible video formats");
614 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_ItemNotFound);
615 error = ERROR_DEVICE_FEATURE_NOT_SUPPORTED;
619 stream->formats = supportedFormats[formatIndex];
622 for (
size_t i = 0; i < nMediaTypes; i++)
624 mediaTypes[i].Format = streamOutputFormat(stream);
625 mediaTypes[i].Flags = CAM_MEDIA_TYPE_DESCRIPTION_FLAG_DecodingRequired;
628 if (stream->currMediaType.Format == 0)
631 stream->currMediaType = mediaTypes[0];
634 error = ecam_dev_send_media_type_list_response(dev, hchannel, mediaTypes, nMediaTypes);
646static UINT ecam_dev_send_stream_list_response(
CameraDevice* dev,
649 CAM_MSG_ID msg = CAM_MSG_ID_StreamListResponse;
656 WLog_ERR(TAG,
"Stream_New failed");
657 return ERROR_NOT_ENOUGH_MEMORY;
660 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, dev->ecam->version));
661 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
664 Stream_Write_UINT16(s, CAM_STREAM_FRAME_SOURCE_TYPE_Color);
665 Stream_Write_UINT8(s, CAM_STREAM_CATEGORY_Capture);
666 Stream_Write_UINT8(s, TRUE );
667 Stream_Write_UINT8(s, FALSE );
669 return ecam_channel_write(dev->ecam, hchannel, msg, s, TRUE);
677static UINT ecam_dev_process_stream_list_request(
CameraDevice* dev,
681 return ecam_dev_send_stream_list_response(dev, hchannel);
689static UINT ecam_dev_process_activate_device_request(
CameraDevice* dev,
694 CAM_ERROR_CODE errorCode = CAM_ERROR_CODE_None;
696 if (dev->ihal->Activate(dev->ihal, dev->deviceId, &errorCode))
697 return ecam_channel_send_generic_msg(dev->ecam, hchannel, CAM_MSG_ID_SuccessResponse);
699 return ecam_channel_send_error_response(dev->ecam, hchannel, errorCode);
707static UINT ecam_dev_process_deactivate_device_request(
CameraDevice* dev,
714 for (
size_t i = 0; i < ECAM_DEVICE_MAX_STREAMS; i++)
715 ecam_dev_stop_stream(dev, i);
717 CAM_ERROR_CODE errorCode = CAM_ERROR_CODE_None;
718 if (dev->ihal->Deactivate(dev->ihal, dev->deviceId, &errorCode))
719 return ecam_channel_send_generic_msg(dev->ecam, hchannel, CAM_MSG_ID_SuccessResponse);
721 return ecam_channel_send_error_response(dev->ecam, hchannel, errorCode);
729static UINT ecam_dev_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
wStream* data)
731 UINT error = CHANNEL_RC_OK;
736 if (!hchannel || !data)
737 return ERROR_INVALID_PARAMETER;
742 return ERROR_INTERNAL_ERROR;
744 if (!Stream_CheckAndLogRequiredCapacity(TAG, data, CAM_HEADER_SIZE))
745 return ERROR_NO_DATA;
747 Stream_Read_UINT8(data, version);
748 Stream_Read_UINT8(data, messageId);
749 WLog_DBG(TAG,
"ChannelId=%" PRIu32
", MessageId=0x%02" PRIx8
", Version=%d",
750 hchannel->channel_mgr->GetChannelId(hchannel->channel), messageId, version);
754 case CAM_MSG_ID_ActivateDeviceRequest:
755 error = ecam_dev_process_activate_device_request(dev, hchannel, data);
758 case CAM_MSG_ID_DeactivateDeviceRequest:
759 error = ecam_dev_process_deactivate_device_request(dev, hchannel, data);
762 case CAM_MSG_ID_StreamListRequest:
763 error = ecam_dev_process_stream_list_request(dev, hchannel, data);
766 case CAM_MSG_ID_MediaTypeListRequest:
767 error = ecam_dev_process_media_type_list_request(dev, hchannel, data);
770 case CAM_MSG_ID_CurrentMediaTypeRequest:
771 error = ecam_dev_process_current_media_type_request(dev, hchannel, data);
774 case CAM_MSG_ID_PropertyListRequest:
775 error = ecam_dev_process_property_list_request(dev, hchannel, data);
778 case CAM_MSG_ID_StartStreamsRequest:
779 error = ecam_dev_process_start_streams_request(dev, hchannel, data);
782 case CAM_MSG_ID_StopStreamsRequest:
783 error = ecam_dev_process_stop_streams_request(dev, hchannel, data);
786 case CAM_MSG_ID_SampleRequest:
787 error = ecam_dev_process_sample_request(dev, hchannel, data);
791 WLog_WARN(TAG,
"unknown MessageId=0x%02" PRIx8
"", messageId);
792 error = ERROR_INVALID_DATA;
793 ecam_channel_send_error_response(dev->ecam, hchannel,
794 CAM_ERROR_CODE_OperationNotSupported);
806static UINT ecam_dev_on_open(WINPR_ATTR_UNUSED IWTSVirtualChannelCallback* pChannelCallback)
808 WLog_DBG(TAG,
"entered");
809 return CHANNEL_RC_OK;
817static UINT ecam_dev_on_close(IWTSVirtualChannelCallback* pChannelCallback)
820 WINPR_ASSERT(hchannel);
825 WLog_DBG(TAG,
"entered");
827 for (
size_t i = 0; i < ECAM_DEVICE_MAX_STREAMS; i++)
828 ecam_dev_stop_stream(dev, i);
831 for (
size_t i = 0; i < ECAM_DEVICE_MAX_STREAMS; i++)
832 if (dev->streams[i].hSampleReqChannel == hchannel)
833 dev->streams[i].hSampleReqChannel =
nullptr;
836 return CHANNEL_RC_OK;
844static UINT ecam_dev_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
845 IWTSVirtualChannel* pChannel,
846 WINPR_ATTR_UNUSED BYTE* Data,
847 WINPR_ATTR_UNUSED BOOL* pbAccept,
848 IWTSVirtualChannelCallback** ppCallback)
852 if (!hlistener || !hlistener->plugin)
853 return ERROR_INTERNAL_ERROR;
855 WLog_DBG(TAG,
"entered");
861 WLog_ERR(TAG,
"calloc failed");
862 return CHANNEL_RC_NO_MEMORY;
865 hchannel->iface.OnDataReceived = ecam_dev_on_data_received;
866 hchannel->iface.OnOpen = ecam_dev_on_open;
867 hchannel->iface.OnClose = ecam_dev_on_close;
868 hchannel->plugin = hlistener->plugin;
869 hchannel->channel_mgr = hlistener->channel_mgr;
870 hchannel->channel = pChannel;
871 *ppCallback = (IWTSVirtualChannelCallback*)hchannel;
872 return CHANNEL_RC_OK;
881 WINPR_ATTR_UNUSED
const char* deviceName)
884 WINPR_ASSERT(ecam->hlistener);
886 IWTSVirtualChannelManager* pChannelMgr = ecam->hlistener->channel_mgr;
887 WINPR_ASSERT(pChannelMgr);
889 WLog_DBG(TAG,
"entered for %s", deviceId);
895 WLog_ERR(TAG,
"calloc failed");
900 dev->ihal = ecam->ihal;
901 strncpy(dev->deviceId, deviceId,
sizeof(dev->deviceId) - 1);
907 WLog_ERR(TAG,
"calloc failed");
911 dev->hlistener->iface.OnNewChannelConnection = ecam_dev_on_new_channel_connection;
912 dev->hlistener->plugin = (IWTSPlugin*)dev;
913 dev->hlistener->channel_mgr = pChannelMgr;
914 if (CHANNEL_RC_OK != pChannelMgr->CreateListener(pChannelMgr, deviceId, 0,
915 &dev->hlistener->iface, &dev->listener))
917 free(dev->hlistener);
919 WLog_ERR(TAG,
"CreateListener failed");
937 WLog_DBG(TAG,
"entered for %s", dev->deviceId);
941 IWTSVirtualChannelManager* mgr = dev->hlistener->channel_mgr;
943 IFCALL(mgr->DestroyListener, mgr, dev->listener);
946 free(dev->hlistener);
948 for (
size_t i = 0; i < ECAM_DEVICE_MAX_STREAMS; i++)
949 ecam_dev_stop_stream(dev, i);