uvc_app 快速入门(sample_uvc_v4l2)
1. 概述与作用
该示例的作用是把 Hi3516CV610 模拟成 USB Video Class(UVC)设备,让 USB host 端把它识别为标准摄像头,host 端 Cheese/OBS/v4l2-ctl 即可看到实时画面,支持最多 2 个 UVC 设备同时枚举。
当前sample默认未开启UAC(USB Audio Class)功能,可以参考uvc_app目录下readme.txt和alsa_readme.txt文件开启和配置UAC功能。
专业名词与缩写:
- UVC — USB Video Class,USB 视频类标准,使设备在 host 端显示为摄像头
- UAC — USB Audio Class,USB 音频类标准,USB 麦克风
- V4L2 — Video4Linux2,Linux 视频框架
- ALSA — Advanced Linux Sound Architecture,UAC 路径使用
- ConfigFS — Linux 内核配置文件系统,用于在内存中构建 USB 设备描述符
- MJPEG / H.264 / H.265 / YUYV / NV21 / NV12 — UVC 流编码/像素格式选项
2. 执行准备
在编译本样例之前,请参照Hi3516CV610环境搭建指南和快速上手指南完成编译环境的搭建。
2.1 编译
进入样例目录执行 make 即可完成编译:
编译成功之后,在当前目录下会生成一个sample_uvc_v4l2的可执行文件,可以放社区支持的开发板上运行。
说明:
- 编译路径为
uvc_app,不是host_uvc;sample_uvc_v4l2使用纯 V4L2 框架(直接操作/dev/video0/1,不调ss_mpi_uvc_*等MPP接口)。- kernel 默认已开启 UVC 支持。如需详细配置项描述,参考《外围设备驱动 操作指南》。
2.2 加载 KO
ko加载脚本位于 output/mpp/ko/load_ko,有两个脚本,load3516cv610_20s和load3516cv610_20s_debug。区别为load3516cv610_20s_debug脚本会打开 logmpp_show=1(日志记录)、proc_show=1(proc信息打印),并将 mem_process_isolation 置为 0(不启用进程间mmz隔离)。
由于当前版本不支持进程间mmz隔离,因此需要在load3516cv610_20s脚本中将 mem_process_isolation 置为 0或者使用load3516cv610_20s_debug脚本,此处以load3516cv610_20s_debug脚本为例,20s内存大小为128MB,默认分配为 32MB OS、96MB MMZ,加载命令为:
可以通过-h命令查看具体用法:
./load3516cv610_20s_debug -h
Usage: ./load3516cv610_20s_debug [-option] [-sensor0~1]
options:
-i insert modules
-r remove modules
-a remove modules first, then insert modules
-sensor0~1 sensor_name config sensor type [default: hy006_3814_0011]
-mmz_start config MMZ start addr [default: 0x42000000]
-mmz_size config MMZ mem size [unit: M, default: 96M]
-ir_auto config ir_auto enable [default: 0]
-profile config sys_profile [default: 0]
-h help information
Available sensors:hy006_3814_0011 bt1120/bt656/bt601
for example: ./load3516cv610_20s_debug -i -sensor0 hy006_3814_0011 -sensor1 hy006_3814_0011 -mmz_start 0x42000000 -mmz_size 96M -ir_auto 1
如果需要调整os和mmz的大小,需要参考《内存布局调整指南》对内存大小进行调整。
如果是使用社区默认提供的镜像,这些ko已经打包到文件系统中,烧录到单板上后位于
/ko目录下,在系统启动后会自动加载这些ko。
2.3 运行时依赖
在运行样例前,需要提前准备:
- 指定运行时依赖的so库路径
MPP样例依赖的so库位于soc/libs/musl下,需要将该目录下的so库拷贝到开发板上(或将这些so库所在的目录挂载到开发板上),并指定运行时依赖库的环境变量,假设这些so库位于/mnt/libs/,执行如下命令:
```shell
export LD_LIBRARY_PATH=/mnt/libs/:$LD_LIBRARY_PATH
```
如果是使用社区默认提供的镜像,这些so库已经打包到文件系统中,烧录到单板上后位于
/usr/lib目录下,该目录是系统默认搜索目录,不需要执行export LD_LIBRARY_PATH命令去指定。
- 启动MPP服务
当前MPP媒体模块处于用户态,通过一个独立进程提供相关接口的调用服务,该进程位于samples/mpp/sdk_server下,执行如下命令即可生成sdk_server可执行文件:
```shell
cd samples/mpp/sdk_server && make
```
在单板上执行./sdk_server &即可启动MPP服务。
- sdk_server同样依赖
soc/libs/musl下的so库,因此也需要参照上述export LD_LIBRARY_PATH指定路径;- 如果是使用社区默认提供的镜像,sdk_server进程已经打包到文件系统中,烧录到单板上后位于
/usr/bin目录下,并在系统启动时会自动启动sdk_server。
- 样例运行时候需要的资源文件
对于本样例,无输入依赖文件。
2.4 确认执行模式
执行 sample 前,需设置为 device 模式(默认为 device 模式)。
# 查询当前模式
cat /proc/wing-usb/10300000.usb20drd/mode
# 设置为 device 模式
echo device > /proc/wing-usb/10300000.usb20drd/mode
注意:路径为
/proc/wing-usb/10300000.usb20drd/mode,含wing-usb段。
2.5 创建并挂载 ConfigFS
脚本通过 Linux 的 ConfigFS 接口在内存中构建 USB 设备的描述符(定义分辨率、编码格式如 YUYV/MJPEG/H264 等),最后将其绑定到物理 USB 控制器上供电脑识别使用。
若已挂载,会出现
mount: mounting none on /sys/kernel/config/ failed: Resource busy,属正常现象。
2.6 sample 脚本准备
2.6.1 ConfigUVC.sh — ConfigFS 描述符构建脚本
创建 ConfigUVC.sh,用于在 ConfigFS 中构建 UVC 设备描述符(分辨率、编码格式等):
#!/bin/sh
######################################################
# set_resolution() fill resolution
# $1 formats
# $2 base_path
# $3 0 or 1: means need fill dwMaxVideoFrameBufferSize
# $4 format name string
#######################################################
function set_resolution()
{
for i in $1
do
echo "$i"
case $i in
"360p")
mkdir $2/360p/
echo -e "333333" > $2/360p/dwFrameInterval
echo "333333" > $2/360p/dwDefaultFrameInterval
echo "110592000" > $2/360p/dwMaxBitRate
if [ $3 -eq 1 ]; then
echo "460800" > $2/360p/dwMaxVideoFrameBufferSize
fi
echo "110592000" > $2/360p/dwMinBitRate
echo "360" > $2/360p/wHeight
echo "640" > $2/360p/wWidth
;;
"480p")
mkdir $2/480p/
echo -e "333333" > $2/480p/dwFrameInterval
echo "333333" > $2/480p/dwDefaultFrameInterval
echo "147456000" > $2/480p/dwMaxBitRate
if [ $3 -eq 1 ]; then
echo "614400" > $2/480p/dwMaxVideoFrameBufferSize
fi
echo "147456000" > $2/480p/dwMinBitRate
echo "480" > $2/480p/wHeight
echo "640" > $2/480p/wWidth
;;
"720p")
mkdir $2/720p/
echo -e "333333" > $2/720p/dwFrameInterval
echo "333333" > $2/720p/dwDefaultFrameInterval
echo "442368000" > $2/720p/dwMaxBitRate
if [ $3 -eq 1 ]; then
echo "1843200" > $2/720p/dwMaxVideoFrameBufferSize
fi
echo "442368000" > $2/720p/dwMinBitRate
echo "720" > $2/720p/wHeight
echo "1280" > $2/720p/wWidth
;;
"1080p")
mkdir $2/1080p/
echo -e "333333"> $2/1080p/dwFrameInterval
echo "333333" > $2/1080p/dwDefaultFrameInterval
echo "995328000" > $2/1080p/dwMaxBitRate
if [ $3 -eq 1 ]; then
echo "4147200" > $2/1080p/dwMaxVideoFrameBufferSize
fi
echo "995328000" > $2/1080p/dwMinBitRate
echo "1080" > $2/1080p/wHeight
echo "1920" > $2/1080p/wWidth
;;
"1440p")
mkdir $2/1440p/
echo -e "333333" > $2/1440p/dwFrameInterval
echo "333333" > $2/1440p/dwDefaultFrameInterval
echo "1769472000" > $2/1440p/dwMaxBitRate
if [ $3 -eq 1 ]; then
echo "7372800" > $2/1440p/dwMaxVideoFrameBufferSize
fi
echo "1769472000" > $2/1440p/dwMinBitRate
echo "1440" > $2/1440p/wHeight
echo "2560" > $2/1440p/wWidth
;;
"2160p")
mkdir $2/2160p/
echo -e "333333" > $2/2160p/dwFrameInterval
echo "333333" > $2/2160p/dwDefaultFrameInterval
echo "3981312000" > $2/2160p/dwMaxBitRate
if [ $3 -eq 1 ]; then
echo "16588800" > $2/2160p/dwMaxVideoFrameBufferSize
fi
echo "3981312000" > $2/2160p/dwMinBitRate
echo "2160" > $2/2160p/wHeight
echo "3840" > $2/2160p/wWidth
;;
*)
echo "$4 $i is invalid!"
;;
esac
done
}
######################################################
# set_format() fill format
# no argument
#######################################################
function set_format()
{
#YUV
if [ -n "$YUYV" ]; then
echo "Add YUYV..."
mkdir streaming/uncompressed/yuy2/
echo -en "\x59\x55\x59\x32\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71" > streaming/uncompressed/yuy2/guidFormat
echo 16 > streaming/uncompressed/yuy2/bBitsPerPixel
set_resolution "$YUYV" streaming/uncompressed/yuy2/ 1 "YUYV"
ln -s streaming/uncompressed/yuy2/ streaming/header/h/
echo -e "Added YUYV\n"
fi
#NV21
if [ -n "$NV21" ]; then
echo "Add NV21..."
mkdir streaming/uncompressed/nv21/
echo -en "\x4E\x56\x32\x31\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71" > streaming/uncompressed/nv21/guidFormat
echo 12 > streaming/uncompressed/nv21/bBitsPerPixel
set_resolution "$NV21" streaming/uncompressed/nv21/ 1 "NV21"
ln -s streaming/uncompressed/nv21/ streaming/header/h/
echo -e "Added NV21\n"
fi
#NV12
if [ -n "$NV12" ]; then
echo "Add NV12..."
mkdir streaming/uncompressed/nv12/
echo -en "\x4E\x56\x31\x32\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71" > streaming/uncompressed/nv12/guidFormat
echo 12 > streaming/uncompressed/nv12/bBitsPerPixel
set_resolution "$NV12" streaming/uncompressed/nv12/ 1 "NV12"
ln -s streaming/uncompressed/nv12/ streaming/header/h/
echo -e "Added NV12\n"
fi
#MJPEG
if [ -n "$MJPEG" ]; then
echo "Add MJPEG..."
mkdir streaming/mjpeg/m/
set_resolution "$MJPEG" streaming/mjpeg/m/ 1 "MJPEG"
ln -s streaming/mjpeg/m/ streaming/header/h/
echo -e "Added MJPEG\n"
fi
#H264
if [ -n "$H264" ]; then
echo "Add H264..."
mkdir streaming/framebased/h264/
echo -en "\x48\x32\x36\x34\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71" > streaming/framebased/h264/guidFormat
set_resolution "$H264" streaming/framebased/h264/ 0 "H264"
ln -s streaming/framebased/h264/ streaming/header/h/
echo -e "Added H264\n"
fi
#HEVC aka H265
if [ -n "$H265" ]; then
echo "Add HEVC(H265)..."
mkdir streaming/framebased/h265/
echo -en "\x48\x32\x36\x35\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71" > streaming/framebased/h265/guidFormat
set_resolution "$H265" streaming/framebased/h265/ 0 "HEVC(H265)"
ln -s streaming/framebased/h265/ streaming/header/h/
echo -e "Added HEVC(H265)\n"
fi
}
mount -t configfs none /sys/kernel/config/
cd /sys/kernel/config/usb_gadget/
mkdir camera
cd camera
echo "0x01" > bDeviceProtocol
echo "0x02" > bDeviceSubClass
echo "0xEF" > bDeviceClass
echo $VID > idVendor
echo $PID > idProduct
mkdir strings/0x409
echo $MANUFACTURER > strings/0x409/manufacturer
echo $PRODUCT > strings/0x409/product
echo $SERIALNUMBER > strings/0x409/serialnumber
make_function()
{
mkdir functions/uvc.usb${1}
cd functions/uvc.usb${1}
mkdir control/header/h/
echo $BCDUVC > control/header/h/bcdUVC
echo "48000000" > control/header/h/dwClockFrequency
ln -s control/header/h/ control/class/fs/
ln -s control/header/h/ control/class/ss/
cat <<EOF> control/terminal/camera/default/bmControls
$CamControl1
$CamControl2
$CamControl3
EOF
cat <<EOF> control/processing/default/bmControls
$ProcControl1
$ProcControl2
$ProcControl3
EOF
cat <<EOF> control/encoding/default/bmControls
$EcdControl1
$EcdControl2
$EcdControl3
EOF
cat <<EOF> control/encoding/default/bmControlsRuntime
$EcdRtControl1
$EcdRtControl2
$EcdRtControl3
EOF
cat <<EOF> control/extension/default/bmControls
$ExtControl1
$ExtControl2
EOF
mkdir streaming/header/h/
set_format
if [ "$PerfMode" != "v4l2" ]; then
echo "WARNING: Performance mode does not support still image yet."
echo 0 > streaming/header/h/bStillCaptureMethod
elif [ "$StillCaptureMethod" == 3 -o "$StillCaptureMethod" == 2 -o "$StillCaptureMethod" == 1 ];then
echo $StillCaptureMethod > streaming/header/h/bStillCaptureMethod
else
echo 0 > streaming/header/h/bStillCaptureMethod
fi
ln -s streaming/header/h/ streaming/class/fs/
ln -s streaming/header/h/ streaming/class/hs/
ln -s streaming/header/h/ streaming/class/ss/
if [ "$TransferMode" == "bulk" ];then
echo -e "setting mode to bulk"
echo "bulk" > streaming_transfer
echo 32768 > streaming_maxpacket
echo 15 > streaming_maxburst
else
echo -e "setting mode to isoc"
echo "isoc" > streaming_transfer
echo 1024 > streaming_maxpacket
fi
if [ "$PerfMode" == "v4l2" ]; then
echo "v4l2" > performance_mode
fi
if [ "$PerfMode" != "v4l2" ]; then
echo 0 > still_capture_method
elif [ "$StillCaptureMethod" == 3 -o "$StillCaptureMethod" == 2 -o "$StillCaptureMethod" == 1 ];then
echo $StillCaptureMethod > still_capture_method
if [ "$StillCaptureMethod" == 3 ];then
echo 32768 > still_maxpacket
fi
else
echo 0 > still_capture_method
fi
#-Create and setup configuration
cd ../../
}
for i in `seq 0 $(expr ${UVC_DEVICE_CNT} - 1)` ; do
make_function $i
done
#mkdir functions/uac1.usb0
#echo 8 > functions/uac1.usb0/req_number
mkdir configs/c.1/
echo "0x01" > configs/c.1/MaxPower
echo "0xc0" > configs/c.1/bmAttributes
mkdir configs/c.1/strings/0x409/
echo "Config 1" > configs/c.1/strings/0x409/configuration
for i in `seq 0 $(expr ${UVC_DEVICE_CNT} - 1)` ; do
ln -s functions/uvc.usb${i}/ configs/c.1/
done
#ln -s functions/uac1.usb0/ configs/c.1/
echo "$(ls /sys/class/udc)" > UDC
2.6.2 run_uvc.sh — 执行用例脚本
run_uvc.sh 是执行用例脚本,其中会进行环境变量配置、ConfigUVC.sh 调用、ko 加载、sdk_server 加载、sample 启动。
export VID="0x1D6B"
export PID="0x0004"
export MANUFACTURER="Vendor"
export PRODUCT="Camera"
export SERIALNUMBER="123456789012"
export BCDUVC="0x0110"
#export TransferMode="bulk"
export PerfMode="v4l2"
#export StillCaptureMethod=2 # Supported only in PerfMode="v4l2".
export UVC_DEVICE_CNT=1 # 1 or 2, only 1 if enable still image (StillCaptureMethod)
export CamControl1=0xFF
export CamControl2=0x7F
export CamControl3=0x3E
export ProcControl1=0xFF
export ProcControl2=0xFF
export ProcControl3=0x07
export EcdControl1=0xff
export EcdControl2=0xff
export EcdControl3=0x0f
export EcdRtControl1=0xff
export EcdRtControl2=0xff
export EcdRtControl3=0x0f
export ExtControl1=0xff
export ExtControl2=0xff
export YUYV="360p 720p 1080p 1440p"
export NV21="360p 720p 1080p 1440p"
export NV12="360p 720p 1080p 1440p"
export MJPEG="360p 720p 1080p 1440p"
export H264="360p 720p 1080p 1440p"
export H265="360p 720p 1080p 1440p"
# Memory size may not support some resolutions in v4l2 mode.
if [ "$StillCaptureMethod" == 2 -o "$StillCaptureMethod" == 3 -o "${PerfMode}" == "v4l2" ] ; then
export YUYV="360p 720p 1080p"
export NV21="360p 720p 1080p"
export NV12="360p 720p 1080p"
fi
# Memory size and usb bandwidth may not support some resolutions in Dual-channel uvc mode.
if [ ${UVC_DEVICE_CNT} != 1 ] ; then
export YUYV="360p"
export NV21="360p"
export NV12="360p"
export MJPEG="360p 720p 1080p"
export H264="360p 720p 1080p"
export H265="360p 720p 1080p"
fi
./ConfigUVC.sh
# 本示例中加载脚本所在的代码目录是 output/mpp/ko/load_ko,单板上的目录需要根据实际情况指定
cd output/mpp/ko/load_ko
./load3516cv610_20s_debug -i
cd -
# 根据sdk_server在单板上的目录的实际情况指定
./sdk_server &
sleep 3
./sample_uvc_v4l2 ${UVC_DEVICE_CNT} &
说明:
sdk_server是 MPP 用户态服务进程,启动后需sleep 3等待初始化完成再启动 sample。
3. 支持的 Case 列表
选项式调用,非数字 case。
3.1 sample_uvc_v4l2
纯 V4L2 ioctl(/dev/video0/1),不调 hi_mpi_uvc_*,无 DATA_MODE 选项。
无交互菜单;运行中按 Ctrl+C 触发 SIGINT 优雅拆除。
3.2 一键执行
执行./run_uvc.sh可以完成一键运行
脚本依次完成:环境变量配置 → ConfigUVC.sh 构建 ConfigFS 描述符 → 加载 KO(load3516cv610_20s_debug -i)→ 启动 sdk_server → 启动 sample_uvc_v4l2。
3.3 执行结果验证
3.3.1 执行 run_uvc.sh
正常输出示例:
mount: mounting none on /sys/kernel/config/ failed: Resource busy
Add YUYV...
360p
720p
1080p
Added YUYV
Add NV21...
360p
720p
1080p
Added NV21
Add NV12...
360p
720p
1080p
Added NV12
Add MJPEG...
360p
720p
1080p
1440p
Added MJPEG
Add H264...
360p
720p
1080p
1440p
Added H264
Add HEVC(H265)...
360p
720p
1080p
1440p
Added HEVC(H265)
./ConfigUVC.sh: line 240: can't create streaming/header/h/bStillCaptureMethod: Permission denied
setting mode to isoc
./ConfigUVC.sh: line 270: can't create still_capture_method: Permission denied
configfs-gadget gadget: uvc: uvc_function_bind()
0x0110
[uvcg_uvc_pack_init] UVC is not in high performance mode!
sys_config: loading out-of-tree module taints kernel.
sys_config_parse_board,180: ==========board_type: dmeb_qfn==========
sys_config_parse_board,187: g_board 1
sensor_cfg,413: ==========sensor0: hy006_3814_0011==========
sensor_cfg,413: ==========sensor1: hy006_3814_0011==========
Media Memory Zone Manager
load mmz ....OK!
ot_base: module license 'Proprietary' taints kernel.
Disabling lock debugging due to kernel taint
load base.ko ....OK!
enter ot_irq_probe
load user_proc.ko ....OK!
load sys.ko ....OK!
load aio.ko ....OK!
load ai.ko ....OK!
load ao.ko ....OK!
load aenc.ko ....OK!
load adec.ko ....OK!
load acodec.ko ....OK!
load mipirx_dev.ko ....OK!
ot_irq_mmapdev_init ok
load km.ko ....OK!
load cipher.ko ....OK!
/home/uvc/ko/load3516cv610_userspace
/home/uvc
========================================
SDK Server Starting...
========================================
load base ....OK!
load vb ....OK!
load vca ....OK!
load sys ....OK!
load pm ....OK!
load chnl.ko ....OK!
load region.ko ....OK!
load vgs.ko ....OK!
load vpp.ko ....OK!
load vpss.ko ....OK!
load vi ....OK!
load isp.ko ....OK!
load svp_npu.ko ....OK!
load ive.ko ....OK!
load aiisp.ko ....OK!
load ot_piris.ko ....OK!
load venc.ko ....OK!
load h265e.ko ....OK!
load svac3e.ko ....OK!
load h264e.ko ....OK!
load jpege.ko ....OK!
load rc.ko ....OK!
load sensor_i2c ....OK!
[sdk_init]-546: sdk init ok.
[SDK Server] initialized successfully, entering main loop...
temperature compensation init ok!
/home/uvc # UVC device count 1
create_cache_node_list bufsz:3840 count:6
@@@@@ UVC App Sample @@@@@
[osal_uds_get_thread_ctx:81]:create thread ctx by thread 3069637868
[osal_uds_server_loop:1069]:accept client 19
[uds_connect_with_ctx:110]:thread(3069637868) fd 3 connect to server /tmp/osal_uds_server.sock success
[osal_uds_create_heartbeat_thread:498]:thread(3069637868) create threadbeat thread(3064053068)
[osal_uds_client_handler:1000]:client(19) process thread(3057622348) START
[osal_uds_get_thread_ctx:81]:create thread ctx by thread 3064053068
[osal_uds_add_virtual_fd_to_pid:225]:created pid_heartbeat for pid=919, vfd=10
[osal_uds_server_loop:1069]:accept client 21
[uds_connect_with_ctx:110]:thread(3064053068) fd 4 connect to server /tmp/osal_uds_server.sock success
[osal_uds_client_handler:1000]:client(21) process thread(3057478988) START
[MPP] Version: [HI3516CV610_MPP_V3.0.2.0 B001 Release], Build Time[Jul 25 2026, 01:35:53]
get_bulk_mode, isoc
configfs-gadget gadget: uvc: uvc_function_set_alt(0, 0)
configfs-gadget gadget: uvc: reset UVC Control
configfs-gadget gadget: uvc: uvc_function_set_alt(1, 0)
549, read_file failed!
configfs-gadget gadget: uvc: uvc_function_set_alt(1, 0)
549, read_file failed!
configfs-gadget gadget: uvc: uvc_function_set_alt(1, 0)
注意:内核默认未开 still image 功能,执行 uvc 配置脚本时出现以下报错属正常现象:
3.3.2 输出文件
默认无文件输出;USB host 端可见 UVC 设备枚举。可选 output/ 缓存 dump(运行时按需写入)。
3.3.3 控制台日志
正常序列:
UVC device count N, data mode M(0 MPP_BIND_UVC, 1 SEND_VENC_STREAM/YUV_FRAME, 2 SEND_USER_STREAM) # sample_uvc
UVC device count N # sample_uvc_v4l2
UVC sample exit!
exit app normally
program exit abnormally!。
3.3.4 正确性判断
- USB host 端
lsusb看到 HiSilicon UVC 设备(Vendor ID0x1D6B/ Product ID0x0004,与run_uvc.sh配置一致)。 - host 端
v4l2-ctl --list-devices列出/dev/video*,Cheese/OBS 打开能看到实时画面。 sample_uvc的data mode字符串与传入参数一致;sample_uvc_v4l2的device count与传入参数一致。- Ctrl+C 触发 SIGINT 优雅拆除,控制台最终出现
exit app normally。 - 顶层 readme DDR 限制:Hi3516CV610_10B / Hi3516CV608 上
sample_uvc不可运行。