20#include <winpr/assert.h>
21#include <winpr/cast.h>
22#include <winpr/interlocked.h>
26#define TAG CHANNELS_TAG("rdpecam-device.client")
33#if defined(WITH_INPUT_FORMAT_H264)
34 { CAM_MEDIA_FORMAT_H264, CAM_MEDIA_FORMAT_H264 },
35 { CAM_MEDIA_FORMAT_MJPG_H264, CAM_MEDIA_FORMAT_H264 },
37#if defined(WITH_INPUT_FORMAT_MJPG)
38 { CAM_MEDIA_FORMAT_MJPG, CAM_MEDIA_FORMAT_H264 },
39 { CAM_MEDIA_FORMAT_MJPG, CAM_MEDIA_FORMAT_MJPG },
41 { CAM_MEDIA_FORMAT_I420, CAM_MEDIA_FORMAT_H264 },
42 { CAM_MEDIA_FORMAT_YUY2, CAM_MEDIA_FORMAT_H264 },
43 { CAM_MEDIA_FORMAT_NV12, CAM_MEDIA_FORMAT_H264 },
44 { CAM_MEDIA_FORMAT_RGB24, CAM_MEDIA_FORMAT_H264 },
45 { CAM_MEDIA_FORMAT_RGB32, CAM_MEDIA_FORMAT_H264 },
47static const size_t nSupportedFormats = ARRAYSIZE(supportedFormats);
51 WINPR_ASSERT(mediaType);
53 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, mediaType->Format));
54 Stream_Write_UINT32(s, mediaType->Width);
55 Stream_Write_UINT32(s, mediaType->Height);
56 Stream_Write_UINT32(s, mediaType->FrameRateNumerator);
57 Stream_Write_UINT32(s, mediaType->FrameRateDenominator);
58 Stream_Write_UINT32(s, mediaType->PixelAspectRatioNumerator);
59 Stream_Write_UINT32(s, mediaType->PixelAspectRatioDenominator);
60 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, mediaType->Flags));
65 WINPR_ASSERT(mediaType);
67 Stream_Read_UINT8(s, mediaType->Format);
68 Stream_Read_UINT32(s, mediaType->Width);
69 Stream_Read_UINT32(s, mediaType->Height);
70 Stream_Read_UINT32(s, mediaType->FrameRateNumerator);
71 Stream_Read_UINT32(s, mediaType->FrameRateDenominator);
72 Stream_Read_UINT32(s, mediaType->PixelAspectRatioNumerator);
73 Stream_Read_UINT32(s, mediaType->PixelAspectRatioDenominator);
74 Stream_Read_UINT8(s, mediaType->Flags);
80 WINPR_ASSERT(mediaType);
82 WLog_DBG(TAG,
"Format: %u, width: %u, height: %u, fps: %u, flags: %u", mediaType->Format,
83 mediaType->Width, mediaType->Height, mediaType->FrameRateNumerator, mediaType->Flags);
91static UINT ecam_dev_send_sample_response(
CameraDevice* dev,
size_t streamIndex,
const BYTE* sample,
97 CAM_MSG_ID msg = CAM_MSG_ID_SampleResponse;
99 Stream_SetPosition(stream->sampleRespBuffer, 0);
101 Stream_Write_UINT8(stream->sampleRespBuffer,
102 WINPR_ASSERTING_INT_CAST(uint8_t, dev->ecam->version));
103 Stream_Write_UINT8(stream->sampleRespBuffer, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
104 Stream_Write_UINT8(stream->sampleRespBuffer, WINPR_ASSERTING_INT_CAST(uint8_t, streamIndex));
106 Stream_Write(stream->sampleRespBuffer, sample, size);
109 return ecam_channel_write(dev->ecam, stream->hSampleReqChannel, msg, stream->sampleRespBuffer,
113static BOOL mediaSupportDrops(CAM_MEDIA_FORMAT format)
117 case CAM_MEDIA_FORMAT_H264:
127 WINPR_ASSERT(stream);
129 if (stream->samplesRequested <= 0)
131 WLog_VRB(TAG,
"Frame delayed: No sample requested");
132 return CHANNEL_RC_OK;
135 if (!stream->haveSample)
137 WLog_VRB(TAG,
"Frame response delayed: No sample available");
138 return CHANNEL_RC_OK;
141 BYTE* encodedSample = Stream_Buffer(stream->pendingSample);
142 size_t encodedSize = Stream_Length(stream->pendingSample);
143 if (streamInputFormat(stream) != streamOutputFormat(stream))
145 if (!ecam_encoder_compress(stream, encodedSample, encodedSize, &encodedSample,
148 WLog_DBG(TAG,
"Frame dropped: error in ecam_encoder_compress");
149 stream->haveSample = FALSE;
150 return CHANNEL_RC_OK;
153 if (!stream->streaming)
155 WLog_DBG(TAG,
"Frame delayed/dropped: stream stopped");
156 return CHANNEL_RC_OK;
160 stream->samplesRequested--;
161 stream->haveSample = FALSE;
163 return ecam_dev_send_sample_response(dev, streamIndex, encodedSample, encodedSize);
166static UINT ecam_dev_sample_captured_callback(
CameraDevice* dev,
int streamIndex,
167 const BYTE* sample,
size_t size)
171 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
172 return ERROR_INVALID_INDEX;
176 if (!stream->streaming)
178 WLog_DBG(TAG,
"Frame drop: stream not running");
179 return CHANNEL_RC_OK;
182 EnterCriticalSection(&stream->lock);
183 UINT ret = CHANNEL_RC_NO_MEMORY;
190 if (stream->haveSample && !mediaSupportDrops(stream->formats.inputFormat))
199 DWORD waitDelay = (1000 * stream->currMediaType.FrameRateDenominator) /
200 stream->currMediaType.FrameRateNumerator;
206 while (stream->haveSample && stream->streaming)
208 LeaveCriticalSection(&stream->lock);
210 SleepEx(waitDelay, TRUE);
212 EnterCriticalSection(&stream->lock);
215 if (!stream->streaming)
217 WLog_DBG(TAG,
"Frame drop: stream not running");
223 Stream_SetPosition(stream->pendingSample, 0);
224 if (!Stream_EnsureRemainingCapacity(stream->pendingSample, size))
227 Stream_Write(stream->pendingSample, sample, size);
228 Stream_SealLength(stream->pendingSample);
229 stream->haveSample = TRUE;
231 ret = ecam_dev_send_pending(dev, WINPR_ASSERTING_INT_CAST(
size_t, streamIndex), stream);
234 LeaveCriticalSection(&stream->lock);
238static void ecam_dev_stop_stream(
CameraDevice* dev,
size_t streamIndex)
242 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
247 if (stream->streaming)
249 stream->streaming = FALSE;
250 dev->ihal->StopStream(dev->ihal, dev->deviceId, 0);
252 DeleteCriticalSection(&stream->lock);
255 Stream_Free(stream->sampleRespBuffer, TRUE);
256 stream->sampleRespBuffer = NULL;
258 Stream_Free(stream->pendingSample, TRUE);
259 stream->pendingSample = NULL;
261 ecam_encoder_context_free(stream);
269static UINT ecam_dev_process_stop_streams_request(
CameraDevice* dev,
275 for (
size_t i = 0; i < ECAM_DEVICE_MAX_STREAMS; i++)
276 ecam_dev_stop_stream(dev, i);
278 return ecam_channel_send_generic_msg(dev->ecam, hchannel, CAM_MSG_ID_SuccessResponse);
286static UINT ecam_dev_process_start_streams_request(
CameraDevice* dev,
289 BYTE streamIndex = 0;
294 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1 + 26))
295 return ERROR_INVALID_DATA;
297 Stream_Read_UINT8(s, streamIndex);
299 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
301 WLog_ERR(TAG,
"Incorrect streamIndex %" PRIu8, streamIndex);
302 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_InvalidStreamNumber);
303 return ERROR_INVALID_INDEX;
306 if (!ecam_dev_read_media_type(s, &mediaType))
308 WLog_ERR(TAG,
"Unable to read MEDIA_TYPE_DESCRIPTION");
309 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_InvalidMessage);
310 return ERROR_INVALID_DATA;
313 ecam_dev_print_media_type(&mediaType);
317 if (stream->streaming)
319 WLog_ERR(TAG,
"Streaming already in progress, device %s, streamIndex %d", dev->deviceId,
321 return CAM_ERROR_CODE_UnexpectedError;
327 stream->currMediaType = mediaType;
330 if (streamInputFormat(stream) != streamOutputFormat(stream) &&
331 !ecam_encoder_context_init(stream))
333 WLog_ERR(TAG,
"stream_ecam_encoder_init failed");
334 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_UnexpectedError);
335 return ERROR_INVALID_DATA;
338 stream->sampleRespBuffer = Stream_New(NULL, ECAM_SAMPLE_RESPONSE_BUFFER_SIZE);
339 if (!stream->sampleRespBuffer)
341 WLog_ERR(TAG,
"Stream_New failed");
342 ecam_dev_stop_stream(dev, streamIndex);
343 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_OutOfMemory);
344 return ERROR_INVALID_DATA;
348 mediaType.Format = streamInputFormat(stream);
350 stream->samplesRequested = 0;
351 stream->haveSample = FALSE;
353 if (!InitializeCriticalSectionEx(&stream->lock, 0, 0))
355 WLog_ERR(TAG,
"InitializeCriticalSectionEx failed");
356 ecam_dev_stop_stream(dev, streamIndex);
357 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_OutOfMemory);
358 return ERROR_INVALID_DATA;
361 stream->pendingSample = Stream_New(NULL, 4ull * mediaType.Width * mediaType.Height);
362 if (!stream->pendingSample)
364 WLog_ERR(TAG,
"pending stream failed");
365 ecam_dev_stop_stream(dev, streamIndex);
366 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_OutOfMemory);
367 return ERROR_INVALID_DATA;
370 UINT error = dev->ihal->StartStream(dev->ihal, dev, streamIndex, &mediaType,
371 ecam_dev_sample_captured_callback);
374 WLog_ERR(TAG,
"StartStream failure");
375 ecam_dev_stop_stream(dev, streamIndex);
376 ecam_channel_send_error_response(dev->ecam, hchannel, error);
377 return ERROR_INVALID_DATA;
380 stream->streaming = TRUE;
381 return ecam_channel_send_generic_msg(dev->ecam, hchannel, CAM_MSG_ID_SuccessResponse);
389static UINT ecam_dev_process_property_list_request(
CameraDevice* dev,
396 return ecam_channel_send_generic_msg(dev->ecam, hchannel, CAM_MSG_ID_PropertyListResponse);
404static UINT ecam_dev_send_current_media_type_response(
CameraDevice* dev,
408 CAM_MSG_ID msg = CAM_MSG_ID_CurrentMediaTypeResponse;
415 WLog_ERR(TAG,
"Stream_New failed");
416 return ERROR_NOT_ENOUGH_MEMORY;
419 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, dev->ecam->version));
420 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
422 ecam_dev_write_media_type(s, mediaType);
424 return ecam_channel_write(dev->ecam, hchannel, msg, s, TRUE);
435 BYTE streamIndex = 0;
439 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
440 return ERROR_INVALID_DATA;
442 Stream_Read_UINT8(s, streamIndex);
444 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
446 WLog_ERR(TAG,
"Incorrect streamIndex %d", streamIndex);
447 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_InvalidStreamNumber);
448 return ERROR_INVALID_INDEX;
453 EnterCriticalSection(&stream->lock);
456 if (stream->hSampleReqChannel != hchannel)
457 stream->hSampleReqChannel = hchannel;
459 stream->samplesRequested++;
460 const UINT ret = ecam_dev_send_pending(dev, streamIndex, stream);
462 LeaveCriticalSection(&stream->lock);
471static UINT ecam_dev_process_current_media_type_request(
CameraDevice* dev,
475 BYTE streamIndex = 0;
479 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
480 return ERROR_INVALID_DATA;
482 Stream_Read_UINT8(s, streamIndex);
484 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
486 WLog_ERR(TAG,
"Incorrect streamIndex %d", streamIndex);
487 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_InvalidStreamNumber);
488 return ERROR_INVALID_INDEX;
493 if (stream->currMediaType.Format == 0)
495 WLog_ERR(TAG,
"Current media type unknown for streamIndex %d", streamIndex);
496 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_NotInitialized);
497 return ERROR_DEVICE_REINITIALIZATION_NEEDED;
500 return ecam_dev_send_current_media_type_response(dev, hchannel, &stream->currMediaType);
508static UINT ecam_dev_send_media_type_list_response(
CameraDevice* dev,
513 CAM_MSG_ID msg = CAM_MSG_ID_MediaTypeListResponse;
517 wStream* s = Stream_New(NULL, CAM_HEADER_SIZE + ECAM_MAX_MEDIA_TYPE_DESCRIPTORS *
521 WLog_ERR(TAG,
"Stream_New failed");
522 return ERROR_NOT_ENOUGH_MEMORY;
525 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, dev->ecam->version));
526 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
528 for (
size_t i = 0; i < nMediaTypes; i++, mediaTypes++)
530 ecam_dev_write_media_type(s, mediaTypes);
533 return ecam_channel_write(dev->ecam, hchannel, msg, s, TRUE);
541static UINT ecam_dev_process_media_type_list_request(
CameraDevice* dev,
544 UINT error = CHANNEL_RC_OK;
545 BYTE streamIndex = 0;
547 size_t nMediaTypes = ECAM_MAX_MEDIA_TYPE_DESCRIPTORS;
551 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
552 return ERROR_INVALID_DATA;
554 Stream_Read_UINT8(s, streamIndex);
556 if (streamIndex >= ECAM_DEVICE_MAX_STREAMS)
558 WLog_ERR(TAG,
"Incorrect streamIndex %d", streamIndex);
559 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_InvalidStreamNumber);
560 return ERROR_INVALID_INDEX;
568 WLog_ERR(TAG,
"calloc failed");
569 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_OutOfMemory);
570 return CHANNEL_RC_NO_MEMORY;
574 dev->ihal->GetMediaTypeDescriptions(dev->ihal, dev->deviceId, streamIndex, supportedFormats,
575 nSupportedFormats, mediaTypes, &nMediaTypes);
576 if (formatIndex == -1 || nMediaTypes == 0)
578 WLog_ERR(TAG,
"Camera doesn't support any compatible video formats");
579 ecam_channel_send_error_response(dev->ecam, hchannel, CAM_ERROR_CODE_ItemNotFound);
580 error = ERROR_DEVICE_FEATURE_NOT_SUPPORTED;
584 stream->formats = supportedFormats[formatIndex];
587 for (
size_t i = 0; i < nMediaTypes; i++)
589 mediaTypes[i].Format = streamOutputFormat(stream);
590 mediaTypes[i].Flags = CAM_MEDIA_TYPE_DESCRIPTION_FLAG_DecodingRequired;
593 if (stream->currMediaType.Format == 0)
596 stream->currMediaType = mediaTypes[0];
599 error = ecam_dev_send_media_type_list_response(dev, hchannel, mediaTypes, nMediaTypes);
611static UINT ecam_dev_send_stream_list_response(
CameraDevice* dev,
614 CAM_MSG_ID msg = CAM_MSG_ID_StreamListResponse;
621 WLog_ERR(TAG,
"Stream_New failed");
622 return ERROR_NOT_ENOUGH_MEMORY;
625 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, dev->ecam->version));
626 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
629 Stream_Write_UINT16(s, CAM_STREAM_FRAME_SOURCE_TYPE_Color);
630 Stream_Write_UINT8(s, CAM_STREAM_CATEGORY_Capture);
631 Stream_Write_UINT8(s, TRUE );
632 Stream_Write_UINT8(s, FALSE );
634 return ecam_channel_write(dev->ecam, hchannel, msg, s, TRUE);
642static UINT ecam_dev_process_stream_list_request(
CameraDevice* dev,
646 return ecam_dev_send_stream_list_response(dev, hchannel);
654static UINT ecam_dev_process_activate_device_request(
CameraDevice* dev,
659 UINT32 errorCode = 0;
661 if (dev->ihal->Activate(dev->ihal, dev->deviceId, &errorCode))
662 return ecam_channel_send_generic_msg(dev->ecam, hchannel, CAM_MSG_ID_SuccessResponse);
664 return ecam_channel_send_error_response(dev->ecam, hchannel, errorCode);
672static UINT ecam_dev_process_deactivate_device_request(
CameraDevice* dev,
679 for (
size_t i = 0; i < ECAM_DEVICE_MAX_STREAMS; i++)
680 ecam_dev_stop_stream(dev, i);
682 UINT32 errorCode = 0;
683 if (dev->ihal->Deactivate(dev->ihal, dev->deviceId, &errorCode))
684 return ecam_channel_send_generic_msg(dev->ecam, hchannel, CAM_MSG_ID_SuccessResponse);
686 return ecam_channel_send_error_response(dev->ecam, hchannel, errorCode);
694static UINT ecam_dev_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
wStream* data)
696 UINT error = CHANNEL_RC_OK;
701 if (!hchannel || !data)
702 return ERROR_INVALID_PARAMETER;
707 return ERROR_INTERNAL_ERROR;
709 if (!Stream_CheckAndLogRequiredCapacity(TAG, data, CAM_HEADER_SIZE))
710 return ERROR_NO_DATA;
712 Stream_Read_UINT8(data, version);
713 Stream_Read_UINT8(data, messageId);
714 WLog_DBG(TAG,
"ChannelId=%" PRIu32
", MessageId=0x%02" PRIx8
", Version=%d",
715 hchannel->channel_mgr->GetChannelId(hchannel->channel), messageId, version);
719 case CAM_MSG_ID_ActivateDeviceRequest:
720 error = ecam_dev_process_activate_device_request(dev, hchannel, data);
723 case CAM_MSG_ID_DeactivateDeviceRequest:
724 error = ecam_dev_process_deactivate_device_request(dev, hchannel, data);
727 case CAM_MSG_ID_StreamListRequest:
728 error = ecam_dev_process_stream_list_request(dev, hchannel, data);
731 case CAM_MSG_ID_MediaTypeListRequest:
732 error = ecam_dev_process_media_type_list_request(dev, hchannel, data);
735 case CAM_MSG_ID_CurrentMediaTypeRequest:
736 error = ecam_dev_process_current_media_type_request(dev, hchannel, data);
739 case CAM_MSG_ID_PropertyListRequest:
740 error = ecam_dev_process_property_list_request(dev, hchannel, data);
743 case CAM_MSG_ID_StartStreamsRequest:
744 error = ecam_dev_process_start_streams_request(dev, hchannel, data);
747 case CAM_MSG_ID_StopStreamsRequest:
748 error = ecam_dev_process_stop_streams_request(dev, hchannel, data);
751 case CAM_MSG_ID_SampleRequest:
752 error = ecam_dev_process_sample_request(dev, hchannel, data);
756 WLog_WARN(TAG,
"unknown MessageId=0x%02" PRIx8
"", messageId);
757 error = ERROR_INVALID_DATA;
758 ecam_channel_send_error_response(dev->ecam, hchannel,
759 CAM_ERROR_CODE_OperationNotSupported);
771static UINT ecam_dev_on_open(WINPR_ATTR_UNUSED IWTSVirtualChannelCallback* pChannelCallback)
773 WLog_DBG(TAG,
"entered");
774 return CHANNEL_RC_OK;
782static UINT ecam_dev_on_close(IWTSVirtualChannelCallback* pChannelCallback)
785 WINPR_ASSERT(hchannel);
790 WLog_DBG(TAG,
"entered");
792 for (
size_t i = 0; i < ECAM_DEVICE_MAX_STREAMS; i++)
793 ecam_dev_stop_stream(dev, i);
796 for (
size_t i = 0; i < ECAM_DEVICE_MAX_STREAMS; i++)
797 if (dev->streams[i].hSampleReqChannel == hchannel)
798 dev->streams[i].hSampleReqChannel = NULL;
801 return CHANNEL_RC_OK;
809static UINT ecam_dev_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
810 IWTSVirtualChannel* pChannel,
811 WINPR_ATTR_UNUSED BYTE* Data,
812 WINPR_ATTR_UNUSED BOOL* pbAccept,
813 IWTSVirtualChannelCallback** ppCallback)
817 if (!hlistener || !hlistener->plugin)
818 return ERROR_INTERNAL_ERROR;
820 WLog_DBG(TAG,
"entered");
826 WLog_ERR(TAG,
"calloc failed");
827 return CHANNEL_RC_NO_MEMORY;
830 hchannel->iface.OnDataReceived = ecam_dev_on_data_received;
831 hchannel->iface.OnOpen = ecam_dev_on_open;
832 hchannel->iface.OnClose = ecam_dev_on_close;
833 hchannel->plugin = hlistener->plugin;
834 hchannel->channel_mgr = hlistener->channel_mgr;
835 hchannel->channel = pChannel;
836 *ppCallback = (IWTSVirtualChannelCallback*)hchannel;
837 return CHANNEL_RC_OK;
846 WINPR_ATTR_UNUSED
const char* deviceName)
849 WINPR_ASSERT(ecam->hlistener);
851 IWTSVirtualChannelManager* pChannelMgr = ecam->hlistener->channel_mgr;
852 WINPR_ASSERT(pChannelMgr);
854 WLog_DBG(TAG,
"entered for %s", deviceId);
860 WLog_ERR(TAG,
"calloc failed");
865 dev->ihal = ecam->ihal;
866 strncpy(dev->deviceId, deviceId,
sizeof(dev->deviceId) - 1);
872 WLog_ERR(TAG,
"calloc failed");
876 dev->hlistener->iface.OnNewChannelConnection = ecam_dev_on_new_channel_connection;
877 dev->hlistener->plugin = (IWTSPlugin*)dev;
878 dev->hlistener->channel_mgr = pChannelMgr;
879 if (CHANNEL_RC_OK != pChannelMgr->CreateListener(pChannelMgr, deviceId, 0,
880 &dev->hlistener->iface, &dev->listener))
882 free(dev->hlistener);
884 WLog_ERR(TAG,
"CreateListener failed");
902 WLog_DBG(TAG,
"entered for %s", dev->deviceId);
906 IWTSVirtualChannelManager* mgr = dev->hlistener->channel_mgr;
908 IFCALL(mgr->DestroyListener, mgr, dev->listener);
911 free(dev->hlistener);
913 for (
size_t i = 0; i < ECAM_DEVICE_MAX_STREAMS; i++)
914 ecam_dev_stop_stream(dev, i);