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 mediaType->Width = Stream_Get_UINT32(s);
99 mediaType->Height = Stream_Get_UINT32(s);
100 mediaType->FrameRateNumerator = Stream_Get_UINT32(s);
101 mediaType->FrameRateDenominator = Stream_Get_UINT32(s);
102 mediaType->PixelAspectRatioNumerator = Stream_Get_UINT32(s);
103 mediaType->PixelAspectRatioDenominator = Stream_Get_UINT32(s);
105 if (mediaType->FrameRateNumerator == 0)
107 if (mediaType->FrameRateDenominator == 0)
109 if (mediaType->PixelAspectRatioNumerator == 0)
111 if (mediaType->PixelAspectRatioDenominator == 0)
114 const uint8_t flags = Stream_Get_UINT8(s);
115 if (!rdpecam_valid_MediaTypeDescriptionFlags(flags))
117 mediaType->Flags = WINPR_ASSERTING_INT_CAST(CAM_MEDIA_TYPE_DESCRIPTION_FLAGS, flags);
123 WINPR_ASSERT(mediaType);
125 WLog_DBG(TAG,
"Format: %u, width: %u, height: %u, fps: %u, flags: %u", mediaType->Format,
126 mediaType->Width, mediaType->Height, mediaType->FrameRateNumerator, mediaType->Flags);
140 CAM_MSG_ID msg = CAM_MSG_ID_SampleResponse;
142 Stream_ResetPosition(stream->sampleRespBuffer);
143 if (!Stream_EnsureRemainingCapacity(stream->sampleRespBuffer, 3))
146 Stream_Write_UINT8(stream->sampleRespBuffer,
147 WINPR_ASSERTING_INT_CAST(uint8_t, dev->ecam->version));
148 Stream_Write_UINT8(stream->sampleRespBuffer, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
149 Stream_Write_UINT8(stream->sampleRespBuffer, WINPR_ASSERTING_INT_CAST(uint8_t, streamIndex));
151 return stream->sampleRespBuffer;
154static BOOL mediaSupportDrops(CAM_MEDIA_FORMAT format)
158 case CAM_MEDIA_FORMAT_H264:
168 WINPR_ASSERT(stream);
170 if (stream->samplesRequested <= 0)
172 WLog_VRB(TAG,
"Frame delayed: No sample requested");
173 return CHANNEL_RC_OK;
176 if (!stream->haveSample)
178 WLog_VRB(TAG,
"Frame response delayed: No sample available");
179 return CHANNEL_RC_OK;
182 wStream* output = ecam_dev_prepare_sample_response(dev, streamIndex);
184 return CHANNEL_RC_OK;
186 const BYTE* encodedSample = Stream_Buffer(stream->pendingSample);
187 const size_t encodedSize = Stream_Length(stream->pendingSample);
188 if (!ecam_encoder_compress(stream, encodedSample, encodedSize, output))
190 WLog_DBG(TAG,
"Frame dropped: error in ecam_encoder_compress");
191 stream->haveSample = FALSE;
192 return CHANNEL_RC_OK;
195 if (!stream->streaming)
197 WLog_DBG(TAG,
"Frame delayed/dropped: stream stopped");
198 return CHANNEL_RC_OK;
201 stream->samplesRequested--;
202 stream->haveSample = FALSE;
205 return ecam_channel_write(dev->ecam, stream->hSampleReqChannel, CAM_MSG_ID_SampleResponse,
209static UINT ecam_dev_sample_captured_callback(
CameraDevice* dev,
size_t streamIndex,
210 const BYTE* sample,
size_t size)
214 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
215 return ERROR_INVALID_INDEX;
219 if (!stream->streaming)
221 WLog_DBG(TAG,
"Frame drop: stream not running");
222 return CHANNEL_RC_OK;
225 EnterCriticalSection(&stream->lock);
226 UINT ret = CHANNEL_RC_NO_MEMORY;
233 if (stream->haveSample && !mediaSupportDrops(stream->formats.inputFormat))
242 DWORD waitDelay = (1000 * stream->currMediaType.FrameRateDenominator) /
243 stream->currMediaType.FrameRateNumerator;
249 while (stream->haveSample && stream->streaming)
251 LeaveCriticalSection(&stream->lock);
253 SleepEx(waitDelay, TRUE);
255 EnterCriticalSection(&stream->lock);
258 if (!stream->streaming)
260 WLog_DBG(TAG,
"Frame drop: stream not running");
266 Stream_ResetPosition(stream->pendingSample);
267 if (!Stream_EnsureRemainingCapacity(stream->pendingSample, size))
270 Stream_Write(stream->pendingSample, sample, size);
271 Stream_SealLength(stream->pendingSample);
272 stream->haveSample = TRUE;
274 ret = ecam_dev_send_pending(dev, streamIndex, stream);
277 LeaveCriticalSection(&stream->lock);
281static void ecam_dev_stop_stream(
CameraDevice* dev,
size_t streamIndex)
285 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
290 if (stream->streaming)
292 stream->streaming = FALSE;
293 dev->ihal->StopStream(dev->ihal, dev->deviceId, 0);
295 DeleteCriticalSection(&stream->lock);
298 Stream_Free(stream->sampleRespBuffer, TRUE);
299 stream->sampleRespBuffer =
nullptr;
301 Stream_Free(stream->pendingSample, TRUE);
302 stream->pendingSample =
nullptr;
304 ecam_encoder_context_free(stream);
312static UINT ecam_dev_process_stop_streams_request(
CameraDevice* dev,
318 for (
size_t i = 0; i < ECAM_DEVICE_MAX_STREAMS; i++)
319 ecam_dev_stop_stream(dev, i);
321 return ecam_channel_send_generic_msg(dev->ecam, hchannel, CAM_MSG_ID_SuccessResponse);
329static UINT ecam_dev_process_start_streams_request(
CameraDevice* dev,
332 BYTE streamIndex = 0;
337 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1 + 26))
338 return ERROR_INVALID_DATA;
340 Stream_Read_UINT8(s, streamIndex);
342 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
344 WLog_ERR(TAG,
"Incorrect streamIndex %" PRIu8, streamIndex);
345 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_InvalidStreamNumber);
346 return ERROR_INVALID_INDEX;
349 if (!ecam_dev_read_media_type(s, &mediaType))
351 WLog_ERR(TAG,
"Unable to read MEDIA_TYPE_DESCRIPTION");
352 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_InvalidMessage);
353 return ERROR_INVALID_DATA;
356 ecam_dev_print_media_type(&mediaType);
360 if (stream->streaming)
362 WLog_ERR(TAG,
"Streaming already in progress, device %s, streamIndex %u", dev->deviceId,
364 return CAM_ERROR_CODE_UnexpectedError;
370 stream->currMediaType = mediaType;
372 if (!ecam_encoder_context_init(stream))
374 WLog_ERR(TAG,
"stream_ecam_encoder_init failed");
375 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_UnexpectedError);
376 return ERROR_INVALID_DATA;
379 stream->sampleRespBuffer = Stream_New(
nullptr, ECAM_SAMPLE_RESPONSE_BUFFER_SIZE);
380 if (!stream->sampleRespBuffer)
382 WLog_ERR(TAG,
"Stream_New failed");
383 ecam_dev_stop_stream(dev, streamIndex);
384 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_OutOfMemory);
385 return ERROR_INVALID_DATA;
389 mediaType.Format = streamInputFormat(stream);
391 stream->samplesRequested = 0;
392 stream->haveSample = FALSE;
394 if (!InitializeCriticalSectionEx(&stream->lock, 0, 0))
396 WLog_ERR(TAG,
"InitializeCriticalSectionEx 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 stream->pendingSample = Stream_New(
nullptr, 4ull * mediaType.Width * mediaType.Height);
403 if (!stream->pendingSample)
405 WLog_ERR(TAG,
"pending stream failed");
406 ecam_dev_stop_stream(dev, streamIndex);
407 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_OutOfMemory);
408 return ERROR_INVALID_DATA;
411 const CAM_ERROR_CODE error = dev->ihal->StartStream(dev->ihal, dev, streamIndex, &mediaType,
412 ecam_dev_sample_captured_callback);
415 WLog_ERR(TAG,
"StartStream failure");
416 ecam_dev_stop_stream(dev, streamIndex);
417 ecam_channel_send_error_response(dev->ecam, hchannel, error);
418 return ERROR_INVALID_DATA;
421 stream->streaming = TRUE;
422 return ecam_channel_send_generic_msg(dev->ecam, hchannel, CAM_MSG_ID_SuccessResponse);
430static UINT ecam_dev_process_property_list_request(
CameraDevice* dev,
437 return ecam_channel_send_generic_msg(dev->ecam, hchannel, CAM_MSG_ID_PropertyListResponse);
445static UINT ecam_dev_send_current_media_type_response(
CameraDevice* dev,
449 CAM_MSG_ID msg = CAM_MSG_ID_CurrentMediaTypeResponse;
456 WLog_ERR(TAG,
"Stream_New failed");
457 return ERROR_NOT_ENOUGH_MEMORY;
460 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, dev->ecam->version));
461 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
463 ecam_dev_write_media_type(s, mediaType);
465 return ecam_channel_write(dev->ecam, hchannel, msg, s, TRUE);
476 BYTE streamIndex = 0;
480 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
481 return ERROR_INVALID_DATA;
483 Stream_Read_UINT8(s, streamIndex);
485 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
487 WLog_ERR(TAG,
"Incorrect streamIndex %u", streamIndex);
488 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_InvalidStreamNumber);
489 return ERROR_INVALID_INDEX;
494 EnterCriticalSection(&stream->lock);
497 if (stream->hSampleReqChannel != hchannel)
498 stream->hSampleReqChannel = hchannel;
500 stream->samplesRequested++;
501 const UINT ret = ecam_dev_send_pending(dev, streamIndex, stream);
503 LeaveCriticalSection(&stream->lock);
512static UINT ecam_dev_process_current_media_type_request(
CameraDevice* dev,
516 BYTE streamIndex = 0;
520 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
521 return ERROR_INVALID_DATA;
523 Stream_Read_UINT8(s, streamIndex);
525 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
527 WLog_ERR(TAG,
"Incorrect streamIndex %u", streamIndex);
528 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_InvalidStreamNumber);
529 return ERROR_INVALID_INDEX;
534 if (stream->currMediaType.Format == 0)
536 WLog_ERR(TAG,
"Current media type unknown for streamIndex %u", streamIndex);
537 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_NotInitialized);
538 return ERROR_DEVICE_REINITIALIZATION_NEEDED;
541 return ecam_dev_send_current_media_type_response(dev, hchannel, &stream->currMediaType);
549static UINT ecam_dev_send_media_type_list_response(
CameraDevice* dev,
554 CAM_MSG_ID msg = CAM_MSG_ID_MediaTypeListResponse;
558 wStream* s = Stream_New(
nullptr, CAM_HEADER_SIZE + ECAM_MAX_MEDIA_TYPE_DESCRIPTORS *
562 WLog_ERR(TAG,
"Stream_New failed");
563 return ERROR_NOT_ENOUGH_MEMORY;
566 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, dev->ecam->version));
567 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
569 for (
size_t i = 0; i < nMediaTypes; i++, mediaTypes++)
571 ecam_dev_write_media_type(s, mediaTypes);
574 return ecam_channel_write(dev->ecam, hchannel, msg, s, TRUE);
582static UINT ecam_dev_process_media_type_list_request(
CameraDevice* dev,
585 UINT error = CHANNEL_RC_OK;
586 BYTE streamIndex = 0;
588 size_t nMediaTypes = ECAM_MAX_MEDIA_TYPE_DESCRIPTORS;
592 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
593 return ERROR_INVALID_DATA;
595 Stream_Read_UINT8(s, streamIndex);
597 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
599 WLog_ERR(TAG,
"Incorrect streamIndex %u", streamIndex);
600 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_InvalidStreamNumber);
601 return ERROR_INVALID_INDEX;
609 WLog_ERR(TAG,
"calloc failed");
610 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_OutOfMemory);
611 return CHANNEL_RC_NO_MEMORY;
614 size_t nSupportedFormats = 0;
618 dev->ihal->GetMediaTypeDescriptions(dev->ihal, dev->deviceId, streamIndex, supportedFormats,
619 nSupportedFormats, mediaTypes, &nMediaTypes);
620 if (formatIndex == -1 || nMediaTypes == 0)
622 WLog_ERR(TAG,
"Camera doesn't support any compatible video formats");
623 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_ItemNotFound);
624 error = ERROR_DEVICE_FEATURE_NOT_SUPPORTED;
628 stream->formats = supportedFormats[formatIndex];
631 for (
size_t i = 0; i < nMediaTypes; i++)
633 mediaTypes[i].Format = streamOutputFormat(stream);
634 mediaTypes[i].Flags = CAM_MEDIA_TYPE_DESCRIPTION_FLAG_DecodingRequired;
637 if (stream->currMediaType.Format == 0)
640 stream->currMediaType = mediaTypes[0];
643 error = ecam_dev_send_media_type_list_response(dev, hchannel, mediaTypes, nMediaTypes);
655static UINT ecam_dev_send_stream_list_response(
CameraDevice* dev,
658 CAM_MSG_ID msg = CAM_MSG_ID_StreamListResponse;
665 WLog_ERR(TAG,
"Stream_New failed");
666 return ERROR_NOT_ENOUGH_MEMORY;
669 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, dev->ecam->version));
670 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
673 Stream_Write_UINT16(s, CAM_STREAM_FRAME_SOURCE_TYPE_Color);
674 Stream_Write_UINT8(s, CAM_STREAM_CATEGORY_Capture);
675 Stream_Write_UINT8(s, TRUE );
676 Stream_Write_UINT8(s, FALSE );
678 return ecam_channel_write(dev->ecam, hchannel, msg, s, TRUE);
686static UINT ecam_dev_process_stream_list_request(
CameraDevice* dev,
690 return ecam_dev_send_stream_list_response(dev, hchannel);
698static UINT ecam_dev_process_activate_device_request(
CameraDevice* dev,
703 CAM_ERROR_CODE errorCode = CAM_ERROR_CODE_None;
705 if (dev->ihal->Activate(dev->ihal, dev->deviceId, &errorCode))
706 return ecam_channel_send_generic_msg(dev->ecam, hchannel, CAM_MSG_ID_SuccessResponse);
708 return ecam_channel_send_error_response(dev->ecam, hchannel, errorCode);
716static UINT ecam_dev_process_deactivate_device_request(
CameraDevice* dev,
723 for (
size_t i = 0; i < ECAM_DEVICE_MAX_STREAMS; i++)
724 ecam_dev_stop_stream(dev, i);
726 CAM_ERROR_CODE errorCode = CAM_ERROR_CODE_None;
727 if (dev->ihal->Deactivate(dev->ihal, dev->deviceId, &errorCode))
728 return ecam_channel_send_generic_msg(dev->ecam, hchannel, CAM_MSG_ID_SuccessResponse);
730 return ecam_channel_send_error_response(dev->ecam, hchannel, errorCode);
738static UINT ecam_dev_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
wStream* data)
740 UINT error = CHANNEL_RC_OK;
745 if (!hchannel || !data)
746 return ERROR_INVALID_PARAMETER;
751 return ERROR_INTERNAL_ERROR;
753 if (!Stream_CheckAndLogRequiredLength(TAG, data, CAM_HEADER_SIZE))
754 return ERROR_NO_DATA;
756 Stream_Read_UINT8(data, version);
757 Stream_Read_UINT8(data, messageId);
758 WLog_DBG(TAG,
"ChannelId=%" PRIu32
", MessageId=0x%02" PRIx8
", Version=%d",
759 hchannel->channel_mgr->GetChannelId(hchannel->channel), messageId, version);
763 case CAM_MSG_ID_ActivateDeviceRequest:
764 error = ecam_dev_process_activate_device_request(dev, hchannel, data);
767 case CAM_MSG_ID_DeactivateDeviceRequest:
768 error = ecam_dev_process_deactivate_device_request(dev, hchannel, data);
771 case CAM_MSG_ID_StreamListRequest:
772 error = ecam_dev_process_stream_list_request(dev, hchannel, data);
775 case CAM_MSG_ID_MediaTypeListRequest:
776 error = ecam_dev_process_media_type_list_request(dev, hchannel, data);
779 case CAM_MSG_ID_CurrentMediaTypeRequest:
780 error = ecam_dev_process_current_media_type_request(dev, hchannel, data);
783 case CAM_MSG_ID_PropertyListRequest:
784 error = ecam_dev_process_property_list_request(dev, hchannel, data);
787 case CAM_MSG_ID_StartStreamsRequest:
788 error = ecam_dev_process_start_streams_request(dev, hchannel, data);
791 case CAM_MSG_ID_StopStreamsRequest:
792 error = ecam_dev_process_stop_streams_request(dev, hchannel, data);
795 case CAM_MSG_ID_SampleRequest:
796 error = ecam_dev_process_sample_request(dev, hchannel, data);
800 WLog_WARN(TAG,
"unknown MessageId=0x%02" PRIx8
"", messageId);
801 error = ERROR_INVALID_DATA;
802 ecam_channel_send_error_response(dev->ecam, hchannel,
803 CAM_ERROR_CODE_OperationNotSupported);
815static UINT ecam_dev_on_open(WINPR_ATTR_UNUSED IWTSVirtualChannelCallback* pChannelCallback)
817 WLog_DBG(TAG,
"entered");
818 return CHANNEL_RC_OK;
826static UINT ecam_dev_on_close(IWTSVirtualChannelCallback* pChannelCallback)
829 WINPR_ASSERT(hchannel);
834 WLog_DBG(TAG,
"entered");
836 for (
size_t i = 0; i < ECAM_DEVICE_MAX_STREAMS; i++)
837 ecam_dev_stop_stream(dev, i);
840 for (
size_t i = 0; i < ECAM_DEVICE_MAX_STREAMS; i++)
841 if (dev->streams[i].hSampleReqChannel == hchannel)
842 dev->streams[i].hSampleReqChannel =
nullptr;
845 return CHANNEL_RC_OK;
853static UINT ecam_dev_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
854 IWTSVirtualChannel* pChannel,
855 WINPR_ATTR_UNUSED BYTE* Data,
856 WINPR_ATTR_UNUSED BOOL* pbAccept,
857 IWTSVirtualChannelCallback** ppCallback)
861 if (!hlistener || !hlistener->plugin)
862 return ERROR_INTERNAL_ERROR;
864 WLog_DBG(TAG,
"entered");
870 WLog_ERR(TAG,
"calloc failed");
871 return CHANNEL_RC_NO_MEMORY;
874 hchannel->iface.OnDataReceived = ecam_dev_on_data_received;
875 hchannel->iface.OnOpen = ecam_dev_on_open;
876 hchannel->iface.OnClose = ecam_dev_on_close;
877 hchannel->plugin = hlistener->plugin;
878 hchannel->channel_mgr = hlistener->channel_mgr;
879 hchannel->channel = pChannel;
880 *ppCallback = (IWTSVirtualChannelCallback*)hchannel;
881 return CHANNEL_RC_OK;
890 WINPR_ATTR_UNUSED
const char* deviceName)
893 WINPR_ASSERT(ecam->hlistener);
895 IWTSVirtualChannelManager* pChannelMgr = ecam->hlistener->channel_mgr;
896 WINPR_ASSERT(pChannelMgr);
898 WLog_DBG(TAG,
"entered for %s", deviceId);
904 WLog_ERR(TAG,
"calloc failed");
909 dev->ihal = ecam->ihal;
910 strncpy(dev->deviceId, deviceId,
sizeof(dev->deviceId) - 1);
916 WLog_ERR(TAG,
"calloc failed");
920 dev->hlistener->iface.OnNewChannelConnection = ecam_dev_on_new_channel_connection;
921 dev->hlistener->plugin = (IWTSPlugin*)dev;
922 dev->hlistener->channel_mgr = pChannelMgr;
923 if (CHANNEL_RC_OK != pChannelMgr->CreateListener(pChannelMgr, deviceId, 0,
924 &dev->hlistener->iface, &dev->listener))
926 free(dev->hlistener);
928 WLog_ERR(TAG,
"CreateListener failed");
946 WLog_DBG(TAG,
"entered for %s", dev->deviceId);
950 IWTSVirtualChannelManager* mgr = dev->hlistener->channel_mgr;
952 IFCALL(mgr->DestroyListener, mgr, dev->listener);
955 free(dev->hlistener);
957 for (
size_t i = 0; i < ECAM_DEVICE_MAX_STREAMS; i++)
958 ecam_dev_stop_stream(dev, i);