mirror of
https://github.com/morgan9e/FreeRDP
synced 2026-04-15 00:44:19 +09:00
[channel,rdpecam] support Logitech h264 stream mux format
This commit is contained in:
@@ -33,6 +33,7 @@
|
||||
#include <libavutil/imgutils.h>
|
||||
|
||||
#include <winpr/wlog.h>
|
||||
#include <winpr/wtypes.h>
|
||||
|
||||
#include <freerdp/api.h>
|
||||
#include <freerdp/types.h>
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 <pj.assis@gmail.com>
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -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})
|
||||
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
/* v4l includes */
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include "camera.h"
|
||||
#include <winpr/handle.h>
|
||||
#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)
|
||||
{
|
||||
|
||||
54
channels/rdpecam/client/v4l/camera_v4l.h
Normal file
54
channels/rdpecam/client/v4l/camera_v4l.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* MS-RDPECAM Implementation, V4L Interface
|
||||
*
|
||||
* Copyright 2025 Oleg Turovski <oleg2104@hotmail.com>
|
||||
*
|
||||
* 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 <winpr/synch.h>
|
||||
#include <winpr/wtypes.h>
|
||||
|
||||
#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 */
|
||||
476
channels/rdpecam/client/v4l/uvc_h264.c
Normal file
476
channels/rdpecam/client/v4l/uvc_h264.c
Normal file
@@ -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 <pj.assis@gmail.com>
|
||||
*
|
||||
* Copyright 2025 Oleg Turovski <oleg2104@hotmail.com>
|
||||
*
|
||||
* 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 <sys/ioctl.h>
|
||||
|
||||
#include <linux/uvcvideo.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <libusb-1.0/libusb.h>
|
||||
|
||||
#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-<busname>-<devpath>
|
||||
* 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);
|
||||
}
|
||||
169
channels/rdpecam/client/v4l/uvc_h264.h
Normal file
169
channels/rdpecam/client/v4l/uvc_h264.h
Normal file
@@ -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 <pj.assis@gmail.com>
|
||||
*
|
||||
* Copyright 2025 Oleg Turovski <oleg2104@hotmail.com>
|
||||
*
|
||||
* 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 <winpr/wtypes.h>
|
||||
|
||||
#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 <winpr/pack.h>
|
||||
|
||||
/* 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 <winpr/pack.h>
|
||||
|
||||
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 */
|
||||
Reference in New Issue
Block a user