From e2d900c76c9a8a951ab7834a984065036de07e4c Mon Sep 17 00:00:00 2001 From: oleg0421 Date: Sun, 9 Feb 2025 18:00:07 -0800 Subject: [PATCH 1/4] [channel,rdpecam] support Logitech h264 stream mux format --- channels/rdpecam/client/camera.h | 13 + channels/rdpecam/client/camera_device_main.c | 7 +- channels/rdpecam/client/encoding.c | 148 +++++- channels/rdpecam/client/v4l/CMakeLists.txt | 2 +- channels/rdpecam/client/v4l/camera_v4l.c | 113 +++-- channels/rdpecam/client/v4l/camera_v4l.h | 54 +++ channels/rdpecam/client/v4l/uvc_h264.c | 476 +++++++++++++++++++ channels/rdpecam/client/v4l/uvc_h264.h | 169 +++++++ 8 files changed, 925 insertions(+), 57 deletions(-) create mode 100644 channels/rdpecam/client/v4l/camera_v4l.h create mode 100644 channels/rdpecam/client/v4l/uvc_h264.c create mode 100644 channels/rdpecam/client/v4l/uvc_h264.h diff --git a/channels/rdpecam/client/camera.h b/channels/rdpecam/client/camera.h index c0c167db3..68282f28f 100644 --- a/channels/rdpecam/client/camera.h +++ b/channels/rdpecam/client/camera.h @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -60,6 +61,12 @@ */ #define ECAM_SAMPLE_RESPONSE_BUFFER_SIZE (1024ULL * 4050ULL) +/* Special format addition for CAM_MEDIA_FORMAT enum formats + * used to support H264 stream muxed in MJPG container stream. + * The value picked not to overlap with enum values + */ +#define CAM_MEDIA_FORMAT_MJPG_H264 0x0401 + typedef struct s_ICamHal ICamHal; typedef struct @@ -105,6 +112,11 @@ typedef struct AVFrame* avOutFrame; #endif +#if defined(WITH_INPUT_FORMAT_H264) + size_t h264FrameMaxSize; + BYTE* h264Frame; +#endif + /* sws_scale */ struct SwsContext* sws; @@ -192,5 +204,6 @@ BOOL ecam_encoder_context_init(CameraDeviceStream* stream); BOOL ecam_encoder_context_free(CameraDeviceStream* stream); BOOL ecam_encoder_compress(CameraDeviceStream* stream, const BYTE* srcData, size_t srcSize, BYTE** ppDstData, size_t* pDstSize); +UINT32 h264_get_max_bitrate(UINT32 height); #endif /* FREERDP_CLIENT_CAMERA_H */ diff --git a/channels/rdpecam/client/camera_device_main.c b/channels/rdpecam/client/camera_device_main.c index 2f6beebaa..5c6dbb7fd 100644 --- a/channels/rdpecam/client/camera_device_main.c +++ b/channels/rdpecam/client/camera_device_main.c @@ -31,6 +31,7 @@ static const CAM_MEDIA_FORMAT_INFO supportedFormats[] = { /* inputFormat, outputFormat */ #if defined(WITH_INPUT_FORMAT_H264) { CAM_MEDIA_FORMAT_H264, CAM_MEDIA_FORMAT_H264 }, /* passthrough */ + { CAM_MEDIA_FORMAT_MJPG_H264, CAM_MEDIA_FORMAT_H264 }, #endif #if defined(WITH_INPUT_FORMAT_MJPG) { CAM_MEDIA_FORMAT_MJPG, CAM_MEDIA_FORMAT_H264 }, @@ -658,12 +659,6 @@ static UINT ecam_dev_on_data_received(IWTSVirtualChannelCallback* pChannelCallba */ static UINT ecam_dev_on_open(IWTSVirtualChannelCallback* pChannelCallback) { - GENERIC_CHANNEL_CALLBACK* hchannel = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback; - WINPR_ASSERT(hchannel); - - CameraDevice* dev = (CameraDevice*)hchannel->plugin; - WINPR_ASSERT(dev); - WLog_DBG(TAG, "entered"); return CHANNEL_RC_OK; } diff --git a/channels/rdpecam/client/encoding.c b/channels/rdpecam/client/encoding.c index bcc84b2d9..26bd9b40c 100644 --- a/channels/rdpecam/client/encoding.c +++ b/channels/rdpecam/client/encoding.c @@ -24,12 +24,123 @@ #define TAG CHANNELS_TAG("rdpecam-video.client") +#if defined(WITH_INPUT_FORMAT_H264) +/* + * demux a H264 frame from a MJPG container + * args: + * srcData - pointer to buffer with h264 muxed in MJPG container + * srcSize - buff size + * h264_data - pointer to h264 data + * h264_max_size - maximum size allowed by h264_data buffer + * + * Credits: + * guvcview http://guvcview.sourceforge.net + * Paulo Assis + * + * see Figure 5 Payload Size in USB_Video_Payload_H 264_1 0.pdf + * for format details + * + * @return: data size and copies demuxed data to h264 buffer + */ +static size_t demux_uvcH264(const BYTE* srcData, size_t srcSize, BYTE* h264_data, + size_t h264_max_size) +{ + WINPR_ASSERT(h264_data); + WINPR_ASSERT(srcData); + + const uint8_t* spl = NULL; + uint8_t* ph264 = h264_data; + + /* search for 1st APP4 marker + * (30 = 2 APP4 marker + 2 length + 22 header + 4 payload size) + */ + for (const uint8_t* sp = srcData; sp < srcData + srcSize - 30; sp++) + { + if (sp[0] == 0xFF && sp[1] == 0xE4) + { + spl = sp + 2; /* exclude APP4 marker */ + break; + } + } + + if (spl == NULL) + { + WLog_ERR(TAG, "Expected 1st APP4 marker but none found"); + return 0; + } + + /* 1st segment length in big endian + * includes payload size + header + 6 bytes (2 length + 4 payload size) + */ + uint16_t length = (uint16_t)spl[0] << 8; + length |= (uint16_t)spl[1]; + + spl += 2; /* header */ + /* header length in little endian at offset 2 */ + uint16_t header_length = (uint16_t)spl[2]; + header_length |= (uint16_t)spl[3] << 8; + + spl += header_length; + /* payload size in little endian */ + uint32_t payload_size = (uint32_t)spl[0] << 0; + payload_size |= (uint32_t)spl[1] << 8; + payload_size |= (uint32_t)spl[2] << 16; + payload_size |= (uint32_t)spl[3] << 24; + + if (payload_size > h264_max_size) + { + WLog_ERR(TAG, "Payload size bigger than h264_data buffer"); + return 0; + } + + spl += 4; /* payload start */ + const uint8_t* epl = spl + payload_size; /* payload end */ + + if (epl > srcData + srcSize) + { + WLog_ERR(TAG, "Payload size bigger than srcData buffer"); + return 0; + } + + length -= header_length + 6; + + /* copy 1st segment to h264 buffer */ + memcpy(ph264, spl, length); + ph264 += length; + spl += length; + + /* copy other segments */ + while (epl > spl + 4) + { + if (spl[0] != 0xFF || spl[1] != 0xE4) + { + WLog_ERR(TAG, "Expected 2nd+ APP4 marker but none found"); + return ph264 - h264_data; + } + + /* 2nd+ segment length in big endian */ + length = (uint16_t)spl[2] << 8; + length |= (uint16_t)spl[3]; + + length -= 2; + spl += 4; /* APP4 marker + length */ + + /* copy segment to h264 buffer */ + memcpy(ph264, spl, length); + ph264 += length; + spl += length; + } + + return ph264 - h264_data; +} +#endif + /** * Function description * * @return bitrate in bps */ -static UINT32 ecam_encoder_h264_get_max_bitrate(CameraDeviceStream* stream) +UINT32 h264_get_max_bitrate(UINT32 height) { static struct Bitrates { @@ -46,8 +157,6 @@ static UINT32 ecam_encoder_h264_get_max_bitrate(CameraDeviceStream* stream) }; const size_t nBitrates = ARRAYSIZE(bitrates); - UINT32 height = stream->currMediaType.Height; - for (size_t i = 0; i < nBitrates; i++) { if (height >= bitrates[i].height) @@ -162,8 +271,19 @@ static BOOL ecam_encoder_compress_h264(CameraDeviceStream* stream, const BYTE* s CAM_MEDIA_FORMAT inputFormat = streamInputFormat(stream); enum AVPixelFormat pixFormat = AV_PIX_FMT_NONE; +#if defined(WITH_INPUT_FORMAT_H264) + if (inputFormat == CAM_MEDIA_FORMAT_MJPG_H264) + { + dstSize = demux_uvcH264(srcData, srcSize, stream->h264Frame, stream->h264FrameMaxSize); + *ppDstData = stream->h264Frame; + *pDstSize = dstSize; + return dstSize > 0; + } + else +#endif + #if defined(WITH_INPUT_FORMAT_MJPG) - if (inputFormat == CAM_MEDIA_FORMAT_MJPG) + if (inputFormat == CAM_MEDIA_FORMAT_MJPG) { stream->avInputPkt->data = WINPR_CAST_CONST_PTR_AWAY(srcData, uint8_t*); WINPR_ASSERT(srcSize <= INT32_MAX); @@ -260,6 +380,14 @@ static void ecam_encoder_context_free_h264(CameraDeviceStream* stream) avcodec_free_context(&stream->avContext); /* sets to NULL */ #endif +#if defined(WITH_INPUT_FORMAT_H264) + if (stream->h264Frame) + { + free(stream->h264Frame); + stream->h264Frame = NULL; + } +#endif + if (stream->h264) { h264_context_free(stream->h264); @@ -331,6 +459,16 @@ static BOOL ecam_encoder_context_init_h264(CameraDeviceStream* stream) { WINPR_ASSERT(stream); +#if defined(WITH_INPUT_FORMAT_H264) + if (streamInputFormat(stream) == CAM_MEDIA_FORMAT_MJPG_H264) + { + stream->h264FrameMaxSize = + stream->currMediaType.Width * stream->currMediaType.Height; /* 1 byte per pixel */ + stream->h264Frame = (BYTE*)calloc(stream->h264FrameMaxSize, sizeof(BYTE)); + return TRUE; /* encoder not needed */ + } +#endif + if (!stream->h264) stream->h264 = h264_context_new(TRUE); @@ -350,7 +488,7 @@ static BOOL ecam_encoder_context_init_h264(CameraDeviceStream* stream) goto fail; if (!h264_context_set_option(stream->h264, H264_CONTEXT_OPTION_BITRATE, - ecam_encoder_h264_get_max_bitrate(stream))) + h264_get_max_bitrate(stream->currMediaType.Height))) goto fail; /* Using CQP mode for rate control. It produces more comparable quality diff --git a/channels/rdpecam/client/v4l/CMakeLists.txt b/channels/rdpecam/client/v4l/CMakeLists.txt index 4b102caa5..3f7699d1b 100644 --- a/channels/rdpecam/client/v4l/CMakeLists.txt +++ b/channels/rdpecam/client/v4l/CMakeLists.txt @@ -19,7 +19,7 @@ if(WITH_V4L) define_channel_client_subsystem("rdpecam" "v4l" "") - set(${MODULE_PREFIX}_SRCS camera_v4l.c) + set(${MODULE_PREFIX}_SRCS camera_v4l.c uvc_h264.c) set(${MODULE_PREFIX}_LIBS winpr freerdp ${V4L_TARGETS}) diff --git a/channels/rdpecam/client/v4l/camera_v4l.c b/channels/rdpecam/client/v4l/camera_v4l.c index 0fec525a7..c2b7a6960 100644 --- a/channels/rdpecam/client/v4l/camera_v4l.c +++ b/channels/rdpecam/client/v4l/camera_v4l.c @@ -26,8 +26,8 @@ /* v4l includes */ #include -#include "camera.h" -#include +#include "camera_v4l.h" +#include "uvc_h264.h" #define TAG CHANNELS_TAG("rdpecam-v4l.client") @@ -37,30 +37,6 @@ #define CAM_V4L2_FRAMERATE_NUMERATOR_DEFAULT 30 #define CAM_V4L2_FRAMERATE_DENOMINATOR_DEFAULT 1 -typedef struct -{ - void* start; - size_t length; - -} CamV4lBuffer; - -typedef struct -{ - CRITICAL_SECTION lock; - - /* members used to call the callback */ - CameraDevice* dev; - int streamIndex; - ICamHalSampleCapturedCallback sampleCallback; - - BOOL streaming; - int fd; - size_t nBuffers; - CamV4lBuffer* buffers; - HANDLE captureThread; - -} CamV4lStream; - typedef struct { ICamHal iHal; @@ -69,6 +45,7 @@ typedef struct } CamV4lHal; +static CamV4lStream* cam_v4l_stream_create(const char* deviceId, int streamIndex); static void cam_v4l_stream_free(void* obj); static void cam_v4l_stream_close_device(CamV4lStream* stream); static UINT cam_v4l_stream_stop(CamV4lStream* stream); @@ -183,17 +160,33 @@ static int cam_v4l_open_device(const char* deviceId, int flags) * @return -1 if error, otherwise index of supportedFormats array and mediaTypes/nMediaTypes filled * in */ -static INT16 cam_v4l_get_media_type_descriptions(ICamHal* hal, const char* deviceId, +static INT16 cam_v4l_get_media_type_descriptions(ICamHal* ihal, const char* deviceId, int streamIndex, const CAM_MEDIA_FORMAT_INFO* supportedFormats, size_t nSupportedFormats, CAM_MEDIA_TYPE_DESCRIPTION* mediaTypes, size_t* nMediaTypes) { + CamV4lHal* hal = (CamV4lHal*)ihal; size_t maxMediaTypes = *nMediaTypes; size_t nTypes = 0; BOOL formatFound = FALSE; + CamV4lStream* stream = (CamV4lStream*)HashTable_GetItemValue(hal->streams, deviceId); + + if (!stream) + { + stream = cam_v4l_stream_create(deviceId, streamIndex); + if (!stream) + return CAM_ERROR_CODE_OutOfMemory; + + if (!HashTable_Insert(hal->streams, deviceId, stream)) + { + cam_v4l_stream_free(stream); + return CAM_ERROR_CODE_UnexpectedError; + } + } + int fd = cam_v4l_open_device(deviceId, O_RDONLY); if (fd == -1) { @@ -204,7 +197,19 @@ static INT16 cam_v4l_get_media_type_descriptions(ICamHal* hal, const char* devic size_t formatIndex = 0; for (; formatIndex < nSupportedFormats; formatIndex++) { - UINT32 pixelFormat = ecamToV4L2PixFormat(supportedFormats[formatIndex].inputFormat); + UINT32 pixelFormat = 0; + if (supportedFormats[formatIndex].inputFormat == CAM_MEDIA_FORMAT_MJPG_H264) + { + if (stream->h264UnitId > 0) + pixelFormat = V4L2_PIX_FMT_MJPEG; + else + continue; /* not supported */ + } + else + { + pixelFormat = ecamToV4L2PixFormat(supportedFormats[formatIndex].inputFormat); + } + WINPR_ASSERT(pixelFormat != 0); struct v4l2_frmsizeenum frmsize = { 0 }; @@ -484,8 +489,7 @@ void cam_v4l_stream_close_device(CamV4lStream* stream) * * @return Null on failure, otherwise pointer to new CamV4lStream */ -static CamV4lStream* cam_v4l_stream_create(CameraDevice* dev, int streamIndex, - ICamHalSampleCapturedCallback callback) +static CamV4lStream* cam_v4l_stream_create(const char* deviceId, int streamIndex) { CamV4lStream* stream = calloc(1, sizeof(CamV4lStream)); @@ -494,11 +498,9 @@ static CamV4lStream* cam_v4l_stream_create(CameraDevice* dev, int streamIndex, WLog_ERR(TAG, "Failure in calloc"); return NULL; } - stream->dev = dev; stream->streamIndex = streamIndex; - stream->sampleCallback = callback; - stream->fd = -1; + stream->h264UnitId = get_uvc_h624_unit_id(deviceId); if (!InitializeCriticalSectionEx(&stream->lock, 0, 0)) { @@ -561,15 +563,9 @@ static UINT cam_v4l_stream_start(ICamHal* ihal, CameraDevice* dev, int streamInd if (!stream) { - stream = cam_v4l_stream_create(dev, streamIndex, callback); - if (!stream) - return CAM_ERROR_CODE_OutOfMemory; - - if (!HashTable_Insert(hal->streams, dev->deviceId, stream)) - { - cam_v4l_stream_free(stream); - return CAM_ERROR_CODE_UnexpectedError; - } + WLog_ERR(TAG, "Unable to find stream, device %s, streamIndex %d", dev->deviceId, + streamIndex); + return CAM_ERROR_CODE_UnexpectedError; } if (stream->streaming) @@ -579,6 +575,9 @@ static UINT cam_v4l_stream_start(ICamHal* ihal, CameraDevice* dev, int streamInd return CAM_ERROR_CODE_UnexpectedError; } + stream->dev = dev; + stream->sampleCallback = callback; + if ((stream->fd = cam_v4l_open_device(dev->deviceId, O_RDWR | O_NONBLOCK)) == -1) { WLog_ERR(TAG, "Unable to open device %s", dev->deviceId); @@ -588,15 +587,26 @@ static UINT cam_v4l_stream_start(ICamHal* ihal, CameraDevice* dev, int streamInd struct v4l2_format video_fmt = { 0 }; video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; video_fmt.fmt.pix.sizeimage = 0; - video_fmt.fmt.pix.width = mediaType->Width; - video_fmt.fmt.pix.height = mediaType->Height; - UINT32 pixelFormat = ecamToV4L2PixFormat(mediaType->Format); + + UINT32 pixelFormat = 0; + if (mediaType->Format == CAM_MEDIA_FORMAT_MJPG_H264) + { + pixelFormat = V4L2_PIX_FMT_MJPEG; + } + else + { + pixelFormat = ecamToV4L2PixFormat(mediaType->Format); + } + if (pixelFormat == 0) { cam_v4l_stream_close_device(stream); return CAM_ERROR_CODE_InvalidMediaType; } + video_fmt.fmt.pix.pixelformat = pixelFormat; + video_fmt.fmt.pix.width = mediaType->Width; + video_fmt.fmt.pix.height = mediaType->Height; /* set format and frame size */ if (ioctl(stream->fd, VIDIOC_S_FMT, &video_fmt) < 0) @@ -629,6 +639,19 @@ static UINT cam_v4l_stream_start(ICamHal* ihal, CameraDevice* dev, int streamInd } } + if (mediaType->Format == CAM_MEDIA_FORMAT_MJPG_H264) + { + if (!set_h264_muxed_format(stream, mediaType)) + { + WLog_ERR(TAG, "Failure to set H264 muxed format"); + cam_v4l_stream_close_device(stream); + return CAM_ERROR_CODE_UnexpectedError; + } + + /* set pixelFormat for following WLog_INFO */ + pixelFormat = V4L2_PIX_FMT_H264; + } + size_t maxSample = cam_v4l_stream_alloc_buffers(stream); if (maxSample == 0) { diff --git a/channels/rdpecam/client/v4l/camera_v4l.h b/channels/rdpecam/client/v4l/camera_v4l.h new file mode 100644 index 000000000..8f1a32fec --- /dev/null +++ b/channels/rdpecam/client/v4l/camera_v4l.h @@ -0,0 +1,54 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * MS-RDPECAM Implementation, V4L Interface + * + * Copyright 2025 Oleg Turovski + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CAMERA_V4L_H +#define CAMERA_V4L_H + +#include +#include + +#include "../camera.h" + +typedef struct +{ + void* start; + size_t length; + +} CamV4lBuffer; + +typedef struct +{ + CRITICAL_SECTION lock; + + /* members used to call the callback */ + CameraDevice* dev; + int streamIndex; + ICamHalSampleCapturedCallback sampleCallback; + + BOOL streaming; + int fd; + uint8_t h264UnitId; /* UVC H264 UnitId, if 0 then UVC H264 is not supported */ + + size_t nBuffers; + CamV4lBuffer* buffers; + HANDLE captureThread; + +} CamV4lStream; + +#endif /* CAMERA_V4L_H */ diff --git a/channels/rdpecam/client/v4l/uvc_h264.c b/channels/rdpecam/client/v4l/uvc_h264.c new file mode 100644 index 000000000..d657dfd34 --- /dev/null +++ b/channels/rdpecam/client/v4l/uvc_h264.c @@ -0,0 +1,476 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * MS-RDPECAM Implementation, UVC H264 support + * + * See USB_Video_Payload_H 264_1 0.pdf for more details + * + * Credits: + * guvcview http://guvcview.sourceforge.net + * Paulo Assis + * + * Copyright 2025 Oleg Turovski + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include "uvc_h264.h" + +/* UVC H.264 extension unit GUID: {A29E7641-DE04-47E3-8B2B-F4341AFF003B} */ +static uint8_t GUID_UVCX_H264_XU[16] = { 0x41, 0x76, 0x9E, 0xA2, 0x04, 0xDE, 0xE3, 0x47, + 0x8B, 0x2B, 0xF4, 0x34, 0x1A, 0xFF, 0x00, 0x3B }; + +#define TAG CHANNELS_TAG("uvc_h264.client") + +/* + * get length of xu control defined by unit id and selector + * args: + * stream - pointer to video device data + * unit - unit id of xu control + * selector - selector for control + * + * returns: length of xu control + */ +uint16_t get_length_xu_control(CamV4lStream* stream, uint8_t unit, uint8_t selector) +{ + WINPR_ASSERT(stream); + WINPR_ASSERT(stream->fd > 0); + + uint16_t length = 0; + + struct uvc_xu_control_query xu_ctrl_query = { .unit = unit, + .selector = selector, + .query = UVC_GET_LEN, + .size = sizeof(length), + .data = (uint8_t*)&length }; + + if (ioctl(stream->fd, UVCIOC_CTRL_QUERY, &xu_ctrl_query) < 0) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "UVCIOC_CTRL_QUERY (GET_LEN) - Error: %s", + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + return 0; + } + + return length; +} + +/* + * runs a query on xu control defined by unit id and selector + * args: + * stream - pointer to video device data + * unit - unit id of xu control + * selector - selector for control + * query - query type + * data - pointer to query data + * + * returns: 0 if query succeeded or error code on fail + */ +int query_xu_control(CamV4lStream* stream, uint8_t unit, uint8_t selector, uint8_t query, + void* data) +{ + int err = 0; + uint16_t len = get_length_xu_control(stream, unit, selector); + + struct uvc_xu_control_query xu_ctrl_query = { + .unit = unit, .selector = selector, .query = query, .size = len, .data = (uint8_t*)data + }; + + /*get query data*/ + if ((err = ioctl(stream->fd, UVCIOC_CTRL_QUERY, &xu_ctrl_query)) < 0) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "UVCIOC_CTRL_QUERY (%" PRIu8 ") - Error: %s", query, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + } + + return err; +} + +/* + * resets the h264 encoder + * args: + * stream - pointer to video device data + * + * returns: 0 on success or error code on fail + */ +static int uvcx_video_encoder_reset(CamV4lStream* stream) +{ + WINPR_ASSERT(stream); + + uvcx_encoder_reset encoder_reset_req = { 0 }; + + int err = 0; + + if ((err = query_xu_control(stream, stream->h264UnitId, UVCX_ENCODER_RESET, UVC_SET_CUR, + &encoder_reset_req)) < 0) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "UVCX_ENCODER_RESET error: %s", + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + } + + return err; +} + +/* + * probes the h264 encoder config + * args: + * stream - pointer to video device data + * query - probe query + * uvcx_video_config - pointer to probe/commit config data + * + * returns: 0 on success or error code on fail + */ +static int uvcx_video_probe(CamV4lStream* stream, uint8_t query, + uvcx_video_config_probe_commit_t* uvcx_video_config) +{ + WINPR_ASSERT(stream); + + int err = 0; + + if ((err = query_xu_control(stream, stream->h264UnitId, UVCX_VIDEO_CONFIG_PROBE, query, + uvcx_video_config)) < 0) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "UVCX_VIDEO_CONFIG_PROBE error: %s", + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + } + + return err; +} + +/* + * commits the h264 encoder config + * args: + * stream - pointer to video device data + * uvcx_video_config - pointer to probe/commit config data + * + * returns: 0 on success or error code on fail + */ +static int uvcx_video_commit(CamV4lStream* stream, + uvcx_video_config_probe_commit_t* uvcx_video_config) +{ + WINPR_ASSERT(stream); + + int err = 0; + + if ((err = query_xu_control(stream, stream->h264UnitId, UVCX_VIDEO_CONFIG_COMMIT, UVC_SET_CUR, + uvcx_video_config)) < 0) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "UVCX_VIDEO_CONFIG_COMMIT error: %s", + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + } + + return err; +} + +/* + * sets h264 muxed format (must not be called while streaming) + * args: + * stream - pointer to video device data + * mediaType + * + * returns: TRUE on success or FALSE on fail + */ +BOOL set_h264_muxed_format(CamV4lStream* stream, const CAM_MEDIA_TYPE_DESCRIPTION* mediaType) +{ + WINPR_ASSERT(stream); + WINPR_ASSERT(mediaType); + + uvcx_video_config_probe_commit_t config_probe_req = { 0 }; + int err = 0; + + /* reset the encoder */ + err = uvcx_video_encoder_reset(stream); + if (err != 0) + return FALSE; + + /* get default values */ + err = uvcx_video_probe(stream, UVC_GET_DEF, &config_probe_req); + if (err != 0) + return FALSE; + + /* set resolution */ + config_probe_req.wWidth = mediaType->Width; + config_probe_req.wHeight = mediaType->Height; + + /* set frame rate in 100ns units */ + uint32_t frame_interval = + (mediaType->FrameRateDenominator * 1000000000LL / mediaType->FrameRateNumerator) / 100; + config_probe_req.dwFrameInterval = frame_interval; + + /* quality settings */ + config_probe_req.wProfile = PROFILE_HIGH; + config_probe_req.bUsageType = USAGETYPE_REALTIME; + config_probe_req.bRateControlMode = RATECONTROL_VBR; + config_probe_req.dwBitRate = h264_get_max_bitrate(mediaType->Height); + config_probe_req.bEntropyCABAC = ENTROPY_CABAC; + config_probe_req.wIFramePeriod = 1000; /* ms, 1 sec */ + + /* hints which parameters are configured */ + config_probe_req.bmHints = BMHINTS_RESOLUTION | BMHINTS_FRAME_INTERVAL | BMHINTS_PROFILE | + BMHINTS_USAGE | BMHINTS_RATECONTROL | BMHINTS_BITRATE | + BMHINTS_ENTROPY | BMHINTS_IFRAMEPERIOD; + + /* set the aux stream */ + config_probe_req.bStreamMuxOption = STREAMMUX_H264; + + /* probe the format */ + err = uvcx_video_probe(stream, UVC_SET_CUR, &config_probe_req); + if (err != 0) + return FALSE; + + err = uvcx_video_probe(stream, UVC_GET_CUR, &config_probe_req); + if (err != 0) + return FALSE; + + if (config_probe_req.wWidth != mediaType->Width) + { + WLog_ERR(TAG, "Requested width %" PRIu16 " but got %" PRIu16, mediaType->Width, + config_probe_req.wWidth); + return FALSE; + } + if (config_probe_req.wHeight != mediaType->Height) + { + WLog_ERR(TAG, "Requested height %" PRIu16 " but got %" PRIu16, mediaType->Height, + config_probe_req.wHeight); + return FALSE; + } + if (config_probe_req.dwFrameInterval != frame_interval) + { + WLog_ERR(TAG, "Requested frame interval %" PRIu32 " but got %" PRIu32, frame_interval, + config_probe_req.dwFrameInterval); + return FALSE; + } + + /* commit the format */ + err = uvcx_video_commit(stream, &config_probe_req); + if (err != 0) + return FALSE; + + return TRUE; +} + +/* + * parses deviceId such as usb-0000:00:1a.0-1.2.2 to return devpath (1.2.2) + * + * deviceID format is: usb-- + * see kernel's usb_make_path() + * + * args: + * deviceId + * path - buffer to return devpath + * size - buffer size + * + * returns: TRUE if success, FALSE otherwise + */ +static BOOL get_devpath_from_device_id(const char* deviceId, char* path, size_t size) +{ + if (0 != strncmp(deviceId, "usb-", 4)) + return FALSE; + + /* find second `-` */ + const char* p = strchr(deviceId + 4, '-'); + if (!p) + return FALSE; + + p++; // now points to NULL terminated devpath + + strncpy(path, p, size - 1); + return TRUE; +} + +/* + * return devpath of a given libusb_device as text string such as: 1.2.2 or 2.3 + * + * args: + * device + * path - buffer to return devpath + * size - buffer size + * + * returns: TRUE if success, FALSE otherwise + */ +static BOOL get_devpath_from_device(libusb_device* device, char* path, size_t size) +{ + uint8_t ports[MAX_DEVPATH_DEPTH] = { 0 }; + int nPorts = libusb_get_port_numbers(device, ports, sizeof(ports)); + + if (nPorts <= 0) + return FALSE; + + for (size_t i = 0; i < nPorts; i++) + { + int nChars = snprintf(path, size, "%" PRIu8, ports[i]); + if (nChars <= 0 || nChars >= size) + return FALSE; + + size -= nChars; + path += nChars; + + if (i < nPorts - 1) + { + *path++ = '.'; + size--; + } + } + return TRUE; +} + +/* + * get GUID unit id from libusb_device, if any + * + * args: + * device + * guid - 16 byte xu GUID + * + * returns: unit id for the matching GUID or 0 if none + */ +static uint8_t get_guid_unit_id_from_device(libusb_device* device, const uint8_t* guid) +{ + struct libusb_device_descriptor ddesc = { 0 }; + + if (libusb_get_device_descriptor(device, &ddesc) != 0) + { + WLog_ERR(TAG, "Couldn't get device descriptor"); + return 0; + } + + for (uint8_t i = 0; i < ddesc.bNumConfigurations; ++i) + { + struct libusb_config_descriptor* config = NULL; + + if (libusb_get_config_descriptor(device, i, &config) != 0) + { + WLog_ERR(TAG, + "Couldn't get config descriptor for " + "configuration %" PRIu8, + i); + continue; + } + + for (uint8_t j = 0; j < config->bNumInterfaces; j++) + { + for (size_t k = 0; k < config->interface[j].num_altsetting; k++) + { + const struct libusb_interface_descriptor* interface = NULL; + const uint8_t* ptr = NULL; + + interface = &config->interface[j].altsetting[k]; + if (interface->bInterfaceClass != LIBUSB_CLASS_VIDEO || + interface->bInterfaceSubClass != USB_VIDEO_CONTROL) + continue; + ptr = interface->extra; + while (ptr - interface->extra + sizeof(xu_descriptor) < interface->extra_length) + { + xu_descriptor* desc = (xu_descriptor*)ptr; + + if (desc->bDescriptorType == USB_VIDEO_CONTROL_INTERFACE && + desc->bDescriptorSubType == USB_VIDEO_CONTROL_XU_TYPE && + memcmp(desc->guidExtensionCode, guid, 16) == 0) + { + uint8_t unit_id = desc->bUnitID; + + WLog_DBG(TAG, + "For camera %04" PRIx16 ":%04" PRIx16 + " found UVCX H264 UnitID %" PRIu8, + ddesc.idVendor, ddesc.idProduct, unit_id); + return unit_id; + } + ptr += desc->bLength; + } + } + } + } + + /* no match found */ + return 0; +} + +/* + * get GUID unit id, if any + * + * args: + * deviceId - camera deviceId such as: usb-0000:00:1a.0-1.2.2 + * guid - 16 byte xu GUID + * + * returns: unit id for the matching GUID or 0 if none + */ +static uint8_t get_guid_unit_id(const char* deviceId, const uint8_t* guid) +{ + char cam_devpath[MAX_DEVPATH_STR_SIZE] = { 0 }; + libusb_context* usb_ctx = NULL; + libusb_device** device_list = NULL; + uint8_t unit_id = 0; + + if (!get_devpath_from_device_id(deviceId, cam_devpath, sizeof(cam_devpath))) + { + WLog_ERR(TAG, "Unable to get devpath from deviceId %s", deviceId); + return 0; + } + + if (0 != libusb_init(&usb_ctx)) + { + WLog_ERR(TAG, "Unable to initialize libusb"); + return 0; + } + + ssize_t cnt = libusb_get_device_list(usb_ctx, &device_list); + + for (ssize_t i = 0; i < cnt; i++) + { + char path[MAX_DEVPATH_STR_SIZE] = { 0 }; + libusb_device* device = device_list[i]; + + if (!device || !get_devpath_from_device(device, path, sizeof(path))) + continue; + + if (0 != strcmp(cam_devpath, path)) + continue; + + /* found device with matching devpath, try to get guid unit id */ + unit_id = get_guid_unit_id_from_device(device, guid); + + if (unit_id > 0) + break; /* got it */ + + /* there's chance for another devpath match - continue */ + } + + libusb_free_device_list(device_list, TRUE); + libusb_exit(usb_ctx); + return unit_id; +} + +/* + * gets the uvc h264 xu control unit id, if any + * + * args: + * deviceId - camera deviceId such as: usb-0000:00:1a.0-1.2.2 + * + * returns: unit id or 0 if none + */ +uint8_t get_uvc_h624_unit_id(const char* deviceId) +{ + WINPR_ASSERT(deviceId); + + WLog_DBG(TAG, "Checking for UVCX H264 UnitID for %s", deviceId); + + return get_guid_unit_id(deviceId, GUID_UVCX_H264_XU); +} diff --git a/channels/rdpecam/client/v4l/uvc_h264.h b/channels/rdpecam/client/v4l/uvc_h264.h new file mode 100644 index 000000000..c8a4e3ce4 --- /dev/null +++ b/channels/rdpecam/client/v4l/uvc_h264.h @@ -0,0 +1,169 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * MS-RDPECAM Implementation, UVC H264 support + * + * See USB_Video_Payload_H 264_1 0.pdf for more details + * + * Credits: + * guvcview http://guvcview.sourceforge.net + * Paulo Assis + * + * Copyright 2025 Oleg Turovski + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UVC_H264_H +#define UVC_H264_H + +#include + +#include "../camera.h" +#include "camera_v4l.h" + +/* UVC H.264 control selectors */ +#define UVCX_VIDEO_CONFIG_PROBE 0x01 +#define UVCX_VIDEO_CONFIG_COMMIT 0x02 +#define UVCX_RATE_CONTROL_MODE 0x03 +#define UVCX_TEMPORAL_SCALE_MODE 0x04 +#define UVCX_SPATIAL_SCALE_MODE 0x05 +#define UVCX_SNR_SCALE_MODE 0x06 +#define UVCX_LTR_BUFFER_SIZE_CONTROL 0x07 +#define UVCX_LTR_PICTURE_CONTROL 0x08 +#define UVCX_PICTURE_TYPE_CONTROL 0x09 +#define UVCX_VERSION 0x0A +#define UVCX_ENCODER_RESET 0x0B +#define UVCX_FRAMERATE_CONFIG 0x0C +#define UVCX_VIDEO_ADVANCE_CONFIG 0x0D +#define UVCX_BITRATE_LAYERS 0x0E +#define UVCX_QP_STEPS_LAYERS 0x0F + +/* Video Class-Specific Request Codes */ +#define UVC_RC_UNDEFINED 0x00 +#define UVC_SET_CUR 0x01 +#define UVC_GET_CUR 0x81 +#define UVC_GET_MIN 0x82 +#define UVC_GET_MAX 0x83 +#define UVC_GET_RES 0x84 +#define UVC_GET_LEN 0x85 +#define UVC_GET_INFO 0x86 +#define UVC_GET_DEF 0x87 + +/* bStreamMuxOption defines */ +#define STREAMMUX_H264 (1 << 0) | (1 << 1) + +/* wProfile defines */ +#define PROFILE_BASELINE 0x4200 +#define PROFILE_MAIN 0x4D00 +#define PROFILE_HIGH 0x6400 + +/* bUsageType defines */ +#define USAGETYPE_REALTIME 0x01 + +/* bRateControlMode defines */ +#define RATECONTROL_CBR 0x01 +#define RATECONTROL_VBR 0x02 +#define RATECONTROL_CONST_QP 0x03 + +/* bEntropyCABAC defines */ +#define ENTROPY_CABAC 0x01 + +/* bmHints defines */ +#define BMHINTS_RESOLUTION 0x0001 +#define BMHINTS_PROFILE 0x0002 +#define BMHINTS_RATECONTROL 0x0004 +#define BMHINTS_USAGE 0x0008 +#define BMHINTS_SLICEMODE 0x0010 +#define BMHINTS_SLICEUNITS 0x0020 +#define BMHINTS_MVCVIEW 0x0040 +#define BMHINTS_TEMPORAL 0x0080 +#define BMHINTS_SNR 0x0100 +#define BMHINTS_SPATIAL 0x0200 +#define BMHINTS_SPATIAL_RATIO 0x0400 +#define BMHINTS_FRAME_INTERVAL 0x0800 +#define BMHINTS_LEAKY_BKT_SIZE 0x1000 +#define BMHINTS_BITRATE 0x2000 +#define BMHINTS_ENTROPY 0x4000 +#define BMHINTS_IFRAMEPERIOD 0x8000 + +/* USB related defines */ +#define USB_VIDEO_CONTROL 0x01 +#define USB_VIDEO_CONTROL_INTERFACE 0x24 +#define USB_VIDEO_CONTROL_XU_TYPE 0x06 + +#define MAX_DEVPATH_DEPTH 8 +#define MAX_DEVPATH_STR_SIZE 32 + +#define WINPR_PACK_PUSH +#include + +/* h264 probe commit struct (uvc 1.1) - packed */ +typedef struct +{ + uint32_t dwFrameInterval; + uint32_t dwBitRate; + uint16_t bmHints; + uint16_t wConfigurationIndex; + uint16_t wWidth; + uint16_t wHeight; + uint16_t wSliceUnits; + uint16_t wSliceMode; + uint16_t wProfile; + uint16_t wIFramePeriod; + uint16_t wEstimatedVideoDelay; + uint16_t wEstimatedMaxConfigDelay; + uint8_t bUsageType; + uint8_t bRateControlMode; + uint8_t bTemporalScaleMode; + uint8_t bSpatialScaleMode; + uint8_t bSNRScaleMode; + uint8_t bStreamMuxOption; + uint8_t bStreamFormat; + uint8_t bEntropyCABAC; + uint8_t bTimestamp; + uint8_t bNumOfReorderFrames; + uint8_t bPreviewFlipped; + uint8_t bView; + uint8_t bReserved1; + uint8_t bReserved2; + uint8_t bStreamID; + uint8_t bSpatialLayerRatio; + uint16_t wLeakyBucketSize; + +} uvcx_video_config_probe_commit_t; + +/* encoder reset struct - packed */ +typedef struct +{ + uint16_t wLayerID; + +} uvcx_encoder_reset; + +/* xu_descriptor struct - packed */ +typedef struct +{ + int8_t bLength; + int8_t bDescriptorType; + int8_t bDescriptorSubType; + int8_t bUnitID; + uint8_t guidExtensionCode[16]; + +} xu_descriptor; + +#define WINPR_PACK_POP +#include + +uint8_t get_uvc_h624_unit_id(const char* deviceId); +BOOL set_h264_muxed_format(CamV4lStream* stream, const CAM_MEDIA_TYPE_DESCRIPTION* mediaType); + +#endif /* UVC_H264_H */ From 6cf34d6ca96f7af0a2589d05c9418fa1ff5d767e Mon Sep 17 00:00:00 2001 From: akallabeth Date: Mon, 10 Feb 2025 10:56:08 +0100 Subject: [PATCH 2/4] [channels,rdpecam] fix missing length checks --- channels/rdpecam/client/encoding.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/channels/rdpecam/client/encoding.c b/channels/rdpecam/client/encoding.c index 26bd9b40c..25e7a8808 100644 --- a/channels/rdpecam/client/encoding.c +++ b/channels/rdpecam/client/encoding.c @@ -48,6 +48,11 @@ static size_t demux_uvcH264(const BYTE* srcData, size_t srcSize, BYTE* h264_data WINPR_ASSERT(h264_data); WINPR_ASSERT(srcData); + if (srcSize < 30) + { + WLog_ERR(TAG, "Expected srcSize >= 30, got %" PRIuz, srcSize); + return 0; + } const uint8_t* spl = NULL; uint8_t* ph264 = h264_data; @@ -69,6 +74,12 @@ static size_t demux_uvcH264(const BYTE* srcData, size_t srcSize, BYTE* h264_data return 0; } + if (spl > srcData + srcSize - 4) + { + WLog_ERR(TAG, "Payload + Header size bigger than srcData buffer"); + return 0; + } + /* 1st segment length in big endian * includes payload size + header + 6 bytes (2 length + 4 payload size) */ @@ -81,6 +92,12 @@ static size_t demux_uvcH264(const BYTE* srcData, size_t srcSize, BYTE* h264_data header_length |= (uint16_t)spl[3] << 8; spl += header_length; + if (spl > srcData + srcSize) + { + WLog_ERR(TAG, "Header size bigger than srcData buffer"); + return 0; + } + /* payload size in little endian */ uint32_t payload_size = (uint32_t)spl[0] << 0; payload_size |= (uint32_t)spl[1] << 8; @@ -121,6 +138,11 @@ static size_t demux_uvcH264(const BYTE* srcData, size_t srcSize, BYTE* h264_data /* 2nd+ segment length in big endian */ length = (uint16_t)spl[2] << 8; length |= (uint16_t)spl[3]; + if (length < 2) + { + WLog_ERR(TAG, "Expected 2nd+ APP4 length >= 2 but have %" PRIu16, length); + return 0; + } length -= 2; spl += 4; /* APP4 marker + length */ From e5390ff085453eca59e83fb449052adf7033550a Mon Sep 17 00:00:00 2001 From: akallabeth Date: Mon, 10 Feb 2025 10:49:21 +0100 Subject: [PATCH 3/4] [ci,codespell] exception for wHeight --- scripts/codespell.ignore | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/codespell.ignore b/scripts/codespell.ignore index 6fcb54f36..cfb9fc1c8 100644 --- a/scripts/codespell.ignore +++ b/scripts/codespell.ignore @@ -25,3 +25,4 @@ nome manuel shs udo +wheight From 574ab7f99923e2f943a83a5cdd026e5a822e2034 Mon Sep 17 00:00:00 2001 From: akallabeth Date: Mon, 10 Feb 2025 11:26:56 +0100 Subject: [PATCH 4/4] [channels,rdpecam] fix clang-tidy warnings --- channels/rdpecam/client/encoding.c | 12 +++++----- channels/rdpecam/client/v4l/uvc_h264.c | 31 ++++++++++++-------------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/channels/rdpecam/client/encoding.c b/channels/rdpecam/client/encoding.c index 25e7a8808..b6e74e4e3 100644 --- a/channels/rdpecam/client/encoding.c +++ b/channels/rdpecam/client/encoding.c @@ -83,7 +83,7 @@ static size_t demux_uvcH264(const BYTE* srcData, size_t srcSize, BYTE* h264_data /* 1st segment length in big endian * includes payload size + header + 6 bytes (2 length + 4 payload size) */ - uint16_t length = (uint16_t)spl[0] << 8; + uint16_t length = (uint16_t)(spl[0] << 8) & UINT16_MAX; length |= (uint16_t)spl[1]; spl += 2; /* header */ @@ -136,7 +136,7 @@ static size_t demux_uvcH264(const BYTE* srcData, size_t srcSize, BYTE* h264_data } /* 2nd+ segment length in big endian */ - length = (uint16_t)spl[2] << 8; + length = (uint16_t)(spl[2] << 8) & UINT16_MAX; length |= (uint16_t)spl[3]; if (length < 2) { @@ -296,7 +296,9 @@ static BOOL ecam_encoder_compress_h264(CameraDeviceStream* stream, const BYTE* s #if defined(WITH_INPUT_FORMAT_H264) if (inputFormat == CAM_MEDIA_FORMAT_MJPG_H264) { - dstSize = demux_uvcH264(srcData, srcSize, stream->h264Frame, stream->h264FrameMaxSize); + const size_t rc = + demux_uvcH264(srcData, srcSize, stream->h264Frame, stream->h264FrameMaxSize); + dstSize = WINPR_ASSERTING_INT_CAST(uint32_t, rc); *ppDstData = stream->h264Frame; *pDstSize = dstSize; return dstSize > 0; @@ -484,8 +486,8 @@ static BOOL ecam_encoder_context_init_h264(CameraDeviceStream* stream) #if defined(WITH_INPUT_FORMAT_H264) if (streamInputFormat(stream) == CAM_MEDIA_FORMAT_MJPG_H264) { - stream->h264FrameMaxSize = - stream->currMediaType.Width * stream->currMediaType.Height; /* 1 byte per pixel */ + stream->h264FrameMaxSize = 1ULL * stream->currMediaType.Width * + stream->currMediaType.Height; /* 1 byte per pixel */ stream->h264Frame = (BYTE*)calloc(stream->h264FrameMaxSize, sizeof(BYTE)); return TRUE; /* encoder not needed */ } diff --git a/channels/rdpecam/client/v4l/uvc_h264.c b/channels/rdpecam/client/v4l/uvc_h264.c index d657dfd34..69e8bbc24 100644 --- a/channels/rdpecam/client/v4l/uvc_h264.c +++ b/channels/rdpecam/client/v4l/uvc_h264.c @@ -46,7 +46,7 @@ static uint8_t GUID_UVCX_H264_XU[16] = { 0x41, 0x76, 0x9E, 0xA2, 0x04, 0xDE, 0xE * * returns: length of xu control */ -uint16_t get_length_xu_control(CamV4lStream* stream, uint8_t unit, uint8_t selector) +static uint16_t get_length_xu_control(CamV4lStream* stream, uint8_t unit, uint8_t selector) { WINPR_ASSERT(stream); WINPR_ASSERT(stream->fd > 0); @@ -81,8 +81,8 @@ uint16_t get_length_xu_control(CamV4lStream* stream, uint8_t unit, uint8_t selec * * returns: 0 if query succeeded or error code on fail */ -int query_xu_control(CamV4lStream* stream, uint8_t unit, uint8_t selector, uint8_t query, - void* data) +static int query_xu_control(CamV4lStream* stream, uint8_t unit, uint8_t selector, uint8_t query, + void* data) { int err = 0; uint16_t len = get_length_xu_control(stream, unit, selector); @@ -208,8 +208,8 @@ BOOL set_h264_muxed_format(CamV4lStream* stream, const CAM_MEDIA_TYPE_DESCRIPTIO return FALSE; /* set resolution */ - config_probe_req.wWidth = mediaType->Width; - config_probe_req.wHeight = mediaType->Height; + config_probe_req.wWidth = WINPR_ASSERTING_INT_CAST(uint16_t, mediaType->Width); + config_probe_req.wHeight = WINPR_ASSERTING_INT_CAST(uint16_t, mediaType->Height); /* set frame rate in 100ns units */ uint32_t frame_interval = @@ -315,10 +315,10 @@ static BOOL get_devpath_from_device(libusb_device* device, char* path, size_t si if (nPorts <= 0) return FALSE; - for (size_t i = 0; i < nPorts; i++) + for (int i = 0; i < nPorts; i++) { int nChars = snprintf(path, size, "%" PRIu8, ports[i]); - if (nChars <= 0 || nChars >= size) + if ((nChars <= 0) || (nChars >= size)) return FALSE; size -= nChars; @@ -367,20 +367,17 @@ static uint8_t get_guid_unit_id_from_device(libusb_device* device, const uint8_t for (uint8_t j = 0; j < config->bNumInterfaces; j++) { - for (size_t k = 0; k < config->interface[j].num_altsetting; k++) + const struct libusb_interface* cfg = &config->interface[j]; + for (int k = 0; k < cfg->num_altsetting; k++) { - const struct libusb_interface_descriptor* interface = NULL; - const uint8_t* ptr = NULL; - - interface = &config->interface[j].altsetting[k]; + const struct libusb_interface_descriptor* interface = &cfg->altsetting[k]; if (interface->bInterfaceClass != LIBUSB_CLASS_VIDEO || interface->bInterfaceSubClass != USB_VIDEO_CONTROL) continue; - ptr = interface->extra; - while (ptr - interface->extra + sizeof(xu_descriptor) < interface->extra_length) - { - xu_descriptor* desc = (xu_descriptor*)ptr; + const xu_descriptor* desc = (const xu_descriptor*)interface->extra; + while (((const uint8_t*)desc) < interface->extra + interface->extra_length) + { if (desc->bDescriptorType == USB_VIDEO_CONTROL_INTERFACE && desc->bDescriptorSubType == USB_VIDEO_CONTROL_XU_TYPE && memcmp(desc->guidExtensionCode, guid, 16) == 0) @@ -393,7 +390,7 @@ static uint8_t get_guid_unit_id_from_device(libusb_device* device, const uint8_t ddesc.idVendor, ddesc.idProduct, unit_id); return unit_id; } - ptr += desc->bLength; + desc++; } } }