1. 功能概述
本 sample 提供全链路场景 1V,4V,7V 使用演示,实现单路及多路 VIO 接入,逐级经过 ISP、PYM、BPU、CODEC 模块处理。
1.1. 软件架构说明
本 sample 调用 VIO API 来实现整个数据通路,实现单路、多路 camera 接入,并能够 dump 每一路数据的图像。调用 BPU API 使用 BPU 模块,并依赖 libhbucp.so
等库中的算法 API 实现对视频图像的目标检测与识别。调用 MediaCodec API,经过 VPU 实现对视频图像的编码,保存为本地的 H265 格式视频。
软件控制图如下:

1.2. 硬件数据流说明
每个场景的图中已经标明硬件连接关系。
1V 场景如下:

4V 场景如下:

7V 场景如下:

1.3. 代码位置及目录结构
sample 代码位置在 SDK 工程目录 {sdk_dir}/test/samples/platform_samples/source/S83_Sample/S83E06_Tros/vio_full_link_sample
;
目录结构如下:
markdown
vio_full_link_sample/
├── Kconfig
├── Makefile
├── Makefile.in
├── cfg # JSON files
├── lib # dynamic libraries
| ├── libhbucp.so
| └── ...
├── res
| └── fcos.hbm # model hbm file
└── src
├── include
├── utils # utility codes
| ├── bpu_dnn.cpp
| ├── codec_venc.c
| └── ...
├── vio_full_link_sample.c # core code
├── run_vio_full_link_sample.sh # sample execution script
├── run_vio_full_link_sample_4V.sh # sample execution script
├── run_vio_full_link_sample_7V.sh # sample execution script
└── Makefile
根目录包含 Kconfig
和外部接口的 Makefile
, Kconfig
用于控制本 sample 是否加入整体编译; 根目录中的 cfg 目录包含本 sample 用到的 JSON 配置文件; lib 目录包含本 sample 用到的动态库;res 目录包含本 sample 用到的 hbm 模型文件。
src 目录下的 Makefile
用于 src 目录下的编译; vio_full_link_sample.c
是 main 入口的所在文件; run_vio_full_link_sample.sh
run_vio_full_link_sample_4V.sh
run_vio_full_link_sample_7V.sh
是本 sample 在板端的运行脚本; src 目录中的 include 目录为头文件所在目录;utils 目录包含一些功能性代码。
1.4. API 流程说明
API 流程图如下:

需要注意的是,当将 pym 数据帧送入 VPU 编码时,对应 buffer 会先放入 buf_ctl 的状态队列中,当该帧编码完成后才会释放。
主干代码:
markdown
int main(int argc, char *argv[])
{
int ret = 0;
pthread_t thid[HB_VIO_PIPELINE_MAX];
uint32_t pipe_id[HB_VIO_PIPELINE_MAX];
signal(SIGINT, get_exit_signal);
// get opts
ret = get_opts_for_vio(argc, argv);
if (ret < 0) {
vio_print_err("get opts err!\n");
return -1;
}
#ifdef ENABLE_BPU
ret = bpu_dnn_init(g_bpu_hbm);
if (ret < 0) {
vio_print_err("bpu_hbm_init\n");
goto err;
}
#endif
ret = hb_vio_init(g_vpm_config);
if (ret < 0) {
vio_print_err("hb_vio_init\n");
goto err;
}
ret = hb_cam_init(0, g_cam_config);
if (ret < 0) {
hb_vio_deinit();
vio_print_err("hb_cam_init\n");
goto err;
}
#ifdef ENABLE_HBPLAYER
vio_full_link_sample_event = hb_tool_start_transfer(0);
if (vio_full_link_sample_event != NULL) {
hb_tool_event_setcb(vio_full_link_sample_event, NULL, NULL, NULL, NULL, NULL);
} else {
vio_print_err("vio_full_link_sample_event is NULL\n");
ret = -1;
goto err;
}
#endif
start_timer();
for (uint32_t id = 0; id < HB_VIO_PIPELINE_MAX; id++) {
if (((g_port_mask >> id) & 0x0001) == 0)
continue;
ret = hb_vio_start_pipeline(id);
if (ret < 0) {
hb_cam_deinit(0);
hb_vio_deinit();
vio_print_err("hb_vio_start_pipeline");
goto err;
}
else {
vio_print_info("hb_vio_start_pipeline done");
}
ret = hb_cam_start(id);
if (ret < 0) {
hb_vio_stop_pipeline(id);
hb_cam_deinit(0);
hb_vio_deinit();
vio_print_err("hb_cam_start");
goto err;
}
pipe_id[id] = id;
#ifdef ENABLE_MULTI_MEDIA
ret = pthread_create(&codec_thid[id], NULL, codec_encoding,
(void *)(&pipe_id[id]));
vio_print_dbg("pipe(%d)Test encode work thread---running.\n", id);
#endif
ret = pthread_create(&thid[id], NULL, vio_worker_thread,
(void *)(&pipe_id[id]));
vio_print_dbg("pipe(%d)Test vio work thread---running.\n", id);
}
sleep(1);
for (uint32_t id = 0; id < HB_VIO_PIPELINE_MAX; id++) {
if (((g_port_mask >> id) & 0x0001) == 0)
continue;
vio_print_dbg("pipe(%d)Test vio work thread---try stop.\n", id);
}
for (uint32_t id = 0; id < HB_VIO_PIPELINE_MAX; id++) {
if (((g_port_mask >> id) & 0x0001) == 0)
continue;
pthread_join(thid[id], NULL);
vio_print_dbg("pipe(%d)Test vio work thread---join done.\n", id);
}
for (uint32_t id = 0; id < HB_VIO_PIPELINE_MAX; id++) {
if (((g_port_mask >> id) & 0x0001) == 0)
continue;
ret = hb_cam_stop(id);
if (ret < 0) {
hb_vio_stop_pipeline(id);
hb_cam_deinit(0);
hb_vio_deinit();
vio_print_err("hb_cam_stop");
goto err;
}
ret = hb_vio_stop_pipeline(id);
if (ret < 0) {
hb_cam_deinit(0);
hb_vio_deinit();
vio_print_err("hb_vio_stop_pipeline");
goto err;
}
}
#ifdef ENABLE_MULTI_MEDIA
for (uint32_t id = 0; id < HB_VIO_PIPELINE_MAX; id++) {
if (((g_port_mask >> id) & 0x0001) == 0)
continue;
pthread_join(codec_thid[id], NULL);
vio_print_dbg("pipe(%d)Test encode work thread---join done.\n", id);
}
#endif
#ifdef ENABLE_BPU
bpu_dnn_deinit();
#endif
ret = hb_cam_deinit(0);
ret |= hb_vio_deinit();
err:
return ret;
}
获取及处理数据的代码:
markdown
int vio_worker_func(uint32_t pipe_id, int loop_count)
{
int ret = 0;
pym_buffer_v3_t pym_buf;
VIO_DATA_TYPE_E pym_data_type;
pym_data_type = HB_VIO_PYM_DATA_V3;
dp_info_t dump_info = {0};
ret = hb_vio_get_data(pipe_id, pym_data_type, &pym_buf);
if (ret < 0) {
vio_print_err("Pipe(%u) HB_VIO_PYM_ADV_DATA get failed\n", pipe_id);
goto err;
}
#ifdef ENABLE_BPU
if (((g_bpu_enable_mask >> pipe_id) & 0x0001) != 0) {
address_info_t img_info_bpu;
memset(&img_info_bpu, 0, sizeof(address_info_t));
ret = pym_layer_img_info_attach(&pym_buf, (PYM_LAYER_TYPE)g_pym_show_layer, &img_info_bpu);
if (ret < 0) {
vio_print_err("pipe(%u)pym frame%d buf%d attch failed.\n", pipe_id, pym_buf.pym_img_info.frame_id,
pym_buf.pym_img_info.buf_index);
goto err;
}
pthread_mutex_lock(&g_pym_lock);
bpu_dnn_process(pipe_id, img_info_bpu.addr[0], img_info_bpu.width, img_info_bpu.height);
pthread_mutex_unlock(&g_pym_lock);
//vio_print_info("pipe(%u) bpu process done\n", pipe_id);
}
#endif
#ifdef ENABLE_HBPLAYER
if (g_pym_show_layer >= PYM_DS0_DATA && g_pym_show_layer < PYM_INVAILD_DATA) { pym_img_hbplayer_show((void *)vio_full_link_sample_event, &pym_buf, (PYM_LAYER_TYPE)g_pym_show_layer);
}
#endif
if (g_pym_save >= loop_count) {
//worker thread dump
dump_info.pipe_id = pipe_id;
dump_info.enable = PYM_BUF_DUMP;
memcpy(&dump_info.pym_buf, &pym_buf, sizeof(pym_buffer_v3_t)); dp_worker_data_process(&dump_info);
}
#ifdef ENABLE_MULTI_MEDIA
if (g_codec_save >= loop_count) {
push_pym_buffer_to_post_for_codec(pipe_id, &pym_buf, FALSE);
if (g_codec_save == loop_count) {
// if last frame, push an empty frame more.
push_pym_buffer_to_post_for_codec(pipe_id, NULL, TRUE);
}
} else {
ret = hb_vio_free_pymbuf(pipe_id, pym_data_type, &pym_buf);
if (ret < 0) {
vio_print_err("pipe(%u)pym frame%d buf%d free failed.\n", pipe_id, pym_buf.pym_img_info.frame_id,
pym_buf.pym_img_info.buf_index);
goto err;
}
}#else
ret = hb_vio_free_pymbuf(pipe_id, pym_data_type, &pym_buf);
if (ret < 0) {
vio_print_err("pipe(%u)pym frame%d buf%d free failed.\n", pipe_id, pym_buf.pym_img_info.frame_id,
pym_buf.pym_img_info.buf_index);
goto err;
}
#endif
err:
eturn ret;
}
二. 编译
2.1. 编译环境
本 sample 的编译环境使用 SDK 中的 build 工具,请参考: Build 环境建立。
2.2. 编译说明
本 sample 编译依赖 VPS 系统相关的头文件:
markdown
#include "hb_vin_data_info.h"
#include "hb_vpm_data_info.h"
#include "hb_vio_interface.h"
使用 BPU 时,需要 BPU 模型处理相关的头文件:
markdown
#include "base/algorithm.h"
#include "base/perception_common.h"
#include "hobot/dnn/hb_dnn.h"
#include "hobot/hb_ucp.h"
#include "hobot/hb_ucp_sys.h"
使用 VPU 时,需要 MM&CODEC 系统相关的头文件:
markdown
#include "hb_media_codec.h"
#include "hb_media_error.h"
#include
#include
#include "buf_queue.h"
使用 hbplayer 时,需要 hbplayer 相关的头文件:
markdown
#include "hb_tool_server.h"
sample 编译依赖的库:
markdown
LIBS += -lvio -lpthread -lalog -lhbmem
使能 BPU 编译依赖的库:
markdown
LIBS += -lbpu -ldnn -lopencv_world -lhlog -lhbucp
备注
当前本 sample 中使用的 dnn 版本: ucp_j6-v3.0.6_daily040101815
使能 MM&Codec 编译依赖的库:
markdown
LIBS += -lmultimedia -lavformat -lavcodec -lavutil -lswresample
使能 hbplayer 编译依赖的库:
markdown
LIBS += -lhbplayer
编译命令:
markdown
进入SDK所在目录{sdk_dir},并source构建环境(参见上)。
# 编译本sample:
bdm libvio-full-link-sample
# 输出文件:{sdk_dir}/out/debug-gcc_{gcc_version}/build/test/samples/platform_samples/source/S83_Sample/S83E06_Tros/vio_full_link_sample
三. 运行
3.1. 支持平台
Matrix 6E/M
3.2. 硬件环境搭建
接口连接如下:

模组:联创模组 OVX8B、OVX3C,SENSING-ISX031。
具体型号:
主前视联创 x8b:LCE_M24F121D12_S1ROT8E5;
窄前视联创 x8b:LCE_M24F30D12_S1ROT8E5;
后视联创 x3c:LCE_GM24F60D12_S2T0E6;
4 个周视联创 x3c:LCE_GM24F103D12_S2R8T0E6;
4 个环视森云 isx031c:SENSING_M24F190D4_SOROT7。
连接方式:
一路 OVX8B FOV120 通过 4 合 1 线束的 A 口连接到板子的 DES1;
一路 OVX8B FOV30 通过 4 合 1 线束的 C 口连接到板子的 DES1;
一路 OVX3C FOV60 通过 4 合 1 线束的 D 口连接到板子的 DES1;
四路 OVX3C FOV100 通过通过 4 合 1 线束的 ABCD 口连接到板子的 DES2;
四路 ISX031C 通过 4 合 1 线束的 ABCD 口连接到板子的 DES3。
3.3. 板端部署及配置
刷写系统软件镜像后,本 sample 的可执行文件位于板端: /app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/bin/
;
需要用到的资源:
- 需要用到的配置文件位于板端:
/app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/cfg/
; - 需要用到的资源文件位于板端:
/app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/res/
; - 需要用到的第三方库位于板端:
/app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/lib/
;
3.4. 运行指南
3.4.1. 运行方法
1V 测试命令,可直接执行提供的 sample 运行脚本:
markdown
bash /app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/bin/run_vio_full_link_sample.sh
或使用完整指令:
markdown
export LD_LIBRARY_PATH=/app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/lib:$LD_LIBRARY_PATH
/app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/bin/vio_full_link_sample \
-v /app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/cfg/Scenario_1V_cpe0_sen_cim0_isp0_pym0_mode2_sample/vpm_config.json \
-c /app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/cfg/case_matrix/1V_OVX8B_RX0/hb_j6dev.json \
-O /app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/res/fcos.hbm -m 1 -o 1 -j 1 -l 1000 -s 10 -f 100 -e 0
4V 测试命令,可直接执行提供的 sample 运行脚本:
markdown
bash /app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/bin/run_vio_full_link_sample_4V.sh
或使用完整指令:
markdown
export LD_LIBRARY_PATH=/app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/lib:$LD_LIBRARY_PATH
/app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/bin/vio_full_link_sample \
-v /app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/cfg/Scenario_4V_cpe1_sen_cim1_isp1_pym1_mode1_sample/vpm_config.json \
-c /app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/cfg/Scenario_4V_cpe1_sen_cim1_isp1_pym1_mode1_sample/hb_j6dev.json \
-O /app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/res/fcos.hbm -m 15 -o 15 -j 15 -l 1000 -s 10 -f 10 -e 0
7V 测试命令,可直接执行提供的 sample 运行脚本:
markdown
bash /app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/bin/run_vio_full_link_sample_7V.sh
或使用完整指令:
markdown
export LD_LIBRARY_PATH=/app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/lib:$LD_LIBRARY_PATH
/app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/bin/vio_full_link_sample \
-v /app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/cfg/Scenario_7V_cpe01_sen_cim01_isp01_pym01_sample/vpm_config.json \
-c /app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/cfg/Scenario_7V_cpe01_sen_cim01_isp01_pym01_sample/hb_j6dev.json \
-O /app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/res/fcos.hbm -m 254 -o 254 -j 254 -l 1000 -s 10 -f 10 -e 0
3.4.2. 运行命令行说明
vio_full_link_sample
:应用程序名字。
下面的表格是具体参数的说明:
例如上面的 1V 测试命令:
markdown
/app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/bin/vio_full_link_sample \
-v /app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/cfg/Scenario_1V_cpe0_sen_cim0_isp0_pym0_mode2_sample/vpm_config.json \
-c /app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/cfg/case_matrix/1V_OVX8B_RX0/hb_j6dev.json \
-O /app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/res/fcos.hbm -m 1 -o 1 -j 1 -l 1000 -s 10 -f 100 -e 0
-v
参数是 /app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/cfg/Scenario_1V_cpe0_sen_cim0_isp0_pym0_mode2_sample/vpm_config.json
,代表 VIO 配置文件的所在位置,json 里面的具体配置可参考 VPS 配置文件说明。
-c
参数是 /app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/cfg/case_matrix/1V_OVX8B_RX0/hb_j6dev.json
,代表 camera 的配置文件的所在位置,json 里面的具体配置可参考 征程 6X camera 配置文件说明。
-O
参数是 /app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/res/fcos.hbm
,代表 hbm 模型文件的所在位置。
-m
参数是 1,表示使能 pipe0 数据流。
-o
参数是 1,表示对 pipe0 数据流进行 BPU 处理。
-j
参数是 1,表示对 pipe0 数据流进行 VPU 硬件编码。
-l
参数是 1000,表示获取 PYM 数据 1000 帧。
-s
参数是 10,表示 dump 前面 10 帧的数据。
-f
参数是 100,表示编码前面 100 帧的数据,输出 H265 视频。
-e
参数是 0,表示通过 hbplayer 工具实时显示 PYM DS0 层。
可增加 -t
参数,例如 -t
10000
表示程序获取数据的最长运行时间为 10000ms。 当 -t
参数与 -l
参数同时使用时,各线程在满足其中任意一个条件,即已获取指定的数据帧数或者已达到指定的运行时间后就会退出,一般情况下不建议同时使用这两个参数。
备注
BPU 对于 NV12 格式图像处理要求 Stride 32 对齐,通常 Pym 输出数据作为 BPU 输入就需要 Stride 32 对齐,如果不对齐,BPU 处理结果会不正确。 如果要强制停止,需要两次 ctrl+c 操作。原因是 hobot player 使用了 libevent 库,第一次 ctrl+c 操作被它吃掉了。
3.4.3. 运行结果说明
sample 运行时,通过下面的命令可以看到每个 IP 的帧率:
markdown
cat /sys/class/vps/flow/fps
sample 运行时的主要执行日志如下:
- 成功加载 hbm 模型:
markdown
root@hobot:~# bash /app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/bin/run_vio_full_link_sample_4V.sh
[DNN] HBTL_EXT_DNN log level:3
[UCP]: log level = 3[UCP]: UCP version = 3.0.6
[VP]: log level = 3[DNN]: log level = 3[HPL]: log level = 3[UCPT]: log level = 6[INFO] model is: /app/sample/S83_Sample/S83E06_Tros/vio_full_link_sample/res/fcos.hbm
[BPU][[BPU_MONITOR]][281472801308704][INFO]BPULib verison(2, 0, 5)[]!
[DNN]: 3.0.6_(4.0.19 HBRT)[INFO] Load model successfully!
start_hbplayer_thread start success. pid 281472495382208
create_socket_listen---177, listern 0xffff640154c0
create a listener, port is 10086
create_socket_event---202, t_base 0xaaab0d353e60 event base is 0xffff64014750, listener 0xffff640154c0
[15:07:04]start_callback_service[538] hbplayer L: start_dump_server_thread start success.
[INFO] pym: hb_vio_start_pipeline done[DBG] pym: pipe(0)Test vio work thread---running.
[DBG] pym: pipe(0) pym feedback worker_thread Ready !
[INFO] pym: hb_vio_start_pipeline done[DBG] pym: pipe(1)Test vio work thread---running.
[DBG] pym: pipe(1) pym feedback worker_thread Ready !
[INFO] pym: hb_vio_start_pipeline done[DBG] pym: pipe(2)Test vio work thread---running.
[DBG] pym: pipe(2) pym feedback worker_thread Ready !
[INFO] pym: hb_vio_start_pipeline done[DBG] pym: pipe(3)Test vio work thread---running.
[DBG] pym: pipe(3) pym feedback worker_thread Ready !
里面的相应的版本号会变动,仅供参考。
- Dump、CODEC 编码:

- 运行结束正常退出:

查看运行时的帧率如下:
1V 场景如下:

4V 场景如下:

7V 场景如下:

通过 -t
10000
指定运行时间为 10000ms。
3.4.4. 看图结果
本 sample 支持通过 hbplayer 工具进行看图。首先在 hbplayer 中设置平台、IP 地址等相关参数:

执行命令后,点击 hbplayer 左侧栏中的 connect 即可完成连接并在右侧界面看到实时全链路场景图像:
1V 场景如下:

4V 场景如下:

7V 场景如下:

当满足 -l
或 -t
参数指定的条件后,sample 将自动退出运行,也可通过 Ctrl+C
手动结束运行。
拿 1V 举例,Dump 的 YUV 图像以及 VPU 硬件编码输出的 H265 视频保存在当前目录下:

拿 1V 举例,查看 H265 视频:
t**
**
10000`** 指定运行时间为 10000ms。
3.4.4. 看图结果
本 sample 支持通过 hbplayer 工具进行看图。首先在 hbplayer 中设置平台、IP 地址等相关参数:
外链图片转存中...(img-1nCqRrLE-1757130134166)
执行命令后,点击 hbplayer 左侧栏中的 connect 即可完成连接并在右侧界面看到实时全链路场景图像:
1V 场景如下:
外链图片转存中...(img-KFabr48K-1757130134166)
4V 场景如下:
外链图片转存中...(img-1961xSFv-1757130134166)
7V 场景如下:
外链图片转存中...(img-icD5kcqY-1757130134166)
当满足 -l
或 -t
参数指定的条件后,sample 将自动退出运行,也可通过 Ctrl+C
手动结束运行。
拿 1V 举例,Dump 的 YUV 图像以及 VPU 硬件编码输出的 H265 视频保存在当前目录下:
外链图片转存中...(img-wZGXXcus-1757130134166)
拿 1V 举例,查看 H265 视频:
