提示:海思hi3516读取摄像头的数据是后续应用开发的前提,开发中发现相关文档并不齐全,官方只提供了一个sample。如果需要将其提取合并到自己的工程文件中,整理MakeFile耗时耗力;在踩完所有坑后,记录本文也希望帮助到有关的开发者朋友。如果你不想看具体分析和文件分析,可以直接跳至文件最后从百度网盘下载本人整理的源代码!
目录
前言
海思Hi3516读取摄像头的数据是后续应用开发的前提,开发中发现相关文档并不齐全,官方只提供了一个sample。如果需要将其提取合并到自己的工程文件中,根据MakeFile整理相关依赖耗时耗力;在踩完所有坑后,记录本文也希望帮助到有关的开发者朋友。如果你不想看具体使用过程,可以跳到文件最后从百度云盘链接中下载本人整理的全部工程文件,直接编译使用。
一、海思Hi3516
Hi3516是海思半导体主要应用在安防市场的 IPC 处理器内核 SoC,其H264多码流编码性能、优异的ISP和编码视频质量、高性能的智能加速引擎等特性,在满足客户差异化IPCamera产品功能、性能、图像质量要求的同时,可大大降低ebom成本。
关于易百纳G610Q-IPC-38E
G610Q-IPC-38E 模组采用 HI3516CV610(ARM Cortex-A7 MP2)芯片。该芯片基于 ARM Cortex-A7 主频 950MHz 20S,Hi3516CV610 是一颗应用在安防市场的IPC SoC。 G610Q-IPC-38E 模组集成集成 4M@30FPS Sensor,支持最高 6M@30fps 的 ISP 图像处理能力,支持 SVAC3.0 编码。
二、从摄像头读取数据应用
2.1 开发环境搭建(docker)
海思准备了开发环境需要的docker,安装使用和维护都非常方便,强烈推荐!docker内已经完整安装了交叉编译工具和其他开发工具。
# 拉取镜像
docker pull swr.cn-north-4.myhuaweicloud.com/hi_spark/qiankunbp:2.1.1
# 运行
docker run -it -d -v ${PROJECT_PATH}:${PROJECT_PATH} --shm-size 2g --name hi3516 swr.cn-north-4.myhuaweicloud.com/hi_spark/qiankunbp:2.1.1 /bin/bash
# 进入容器
docker exec -it hi3516 /bin/bash
2.2 下载相关依赖包
相关依赖包,hi3516cv610_musl.tar提供相关sample源码和依赖库,下载完成后,将其拷贝至docker宿主机映射目录下
2.3 提取依赖文件和依赖包,并修改示例代码
编译读取摄像头的文件在“hi3516cv610_musl/smp/a7_linux/source/mpp/sample/smart_ae/”目录下,如果你自己一层层整理MakeFIle,可以完整整理出一套完整依赖和编译选项,但是非常痛苦。用如下命令 可以帮你整理出所有依赖:
# 拷贝依赖的头文件和库到你的工程目下
cp -r hi3516cv610_musl/smp/a7_linux/source/out/lib/ /path/to/you/project
cp -r hi3516cv610_musl/smp/a7_linux/source/out/include/ /path/to/you/project
cp -r hi3516cv610_musl/smp/a7_linux/source/mpp/sample/common/ /path/to/you/project
# 拷贝sample_smart_ae.c
cp hi3516cv610_musl/smp/a7_linux/source/mpp/sample/smart_ae/sample_smart_ae.c /path/to/you/project/src/
修改sample_smart_ae.c文件12399-1271行,为如下内容(修改后可以手动设置图像大小)
# 修改后代码
hi_s32 sample_smart_ae_start_video_route(int img_width, int img_height)
{
sample_sns_type sns_type = SENSOR0_TYPE;
sample_smart_ae_get_vi_vpss_mode(HI_FALSE);
sample_comm_vi_get_default_vi_cfg(sns_type, &g_vi_cfg[0]);
sample_smart_ae_get_pipe_wrap_line(g_vi_cfg, 1);
sample_comm_vpss_get_default_vpss_cfg(&g_vpss_cfg, g_vie_sys_cfg.vpss_wrap_en);
g_vpss_cfg.chn_attr[1].width = img_width; /* model 1024x576 */
g_vpss_cfg.chn_attr[1].height = img_height; /* model 1024x576 */
sample_smart_ae_vpss_get_wrap_cfg(&g_vie_sys_cfg, &g_vpss_cfg);
if (sample_smart_ae_start_route(g_vi_cfg, &g_vpss_cfg, g_vie_sys_cfg.route_num) != HI_SUCCESS) {
return HI_FAILURE;
}
return HI_SUCCESS;
}
hi_void sample_smart_ae_stop_video_route()
{
sample_smart_ae_stop_route(g_vi_cfg, g_vie_sys_cfg.route_num);
}
static hi_s32 sample_smart_ae_start(hi_void)
{
hi_s32 ret;
// start video route
ret = sample_smart_ae_start_video_route(1024, 576);
if (ret != HI_SUCCESS) {
sample_print("start_video_route error!\n");
return ret;
}
# 注释掉sample_smart_ae.c中main函数
// int main(int argc, char *argv[])
// {
// hi_s32 ret;
// #ifndef __LITEOS__
// sample_register_sig_handler(sample_smart_ae_handle_sig);
// #endif
// if (sample_ipc_server_init(sample_smart_ae_ipc_msg_proc) != HI_SUCCESS) {
// printf("sample_ipc_server_init failed!!!\n");
// }
// ret = sample_smart_ae_init_aidetect(argc, argv);
// if (ret != HI_SUCCESS) {
// sample_smart_ae_usage(argv[0]);
// sample_ipc_server_deinit();
// return ret;
// }
// ret = sample_smart_ae_start();
// sample_ipc_server_deinit();
// return ret;
// }
2.4 添加代码并编译
如果你不想看具体分析和文件分析,可以直接跳至文件最后从百度网盘下载本人整理的源代码!
添加在include文件夹下添加smart_ae.h,内容如下:
#ifndef INCLUDE_SMART_AE_H_
#define INCLUDE_SMART_AE_H_
#ifdef __cplusplus
extern "C"
{
#endif
#include "sample_comm.h"
extern hi_s32 sample_smart_ae_start_video_route(int img_width, int img_height);
extern hi_void sample_smart_ae_stop_video_route();
#ifdef __cplusplus
}
#endif
#endif // INCLUDE_SMART_AE_H_
添加在include文件夹下添加camera.h,内容如下(封装smart_ae为camera类):
#ifndef INCLUDE_CAMERA_H_
#define INCLUDE_CAMERA_H_
#include <string>
#include <iostream>
namespace zytracker{
namespace vio_plugin{
enum class IOSourceType{
kLocalFile = 0,
kCamera
};
class Camera{
public:
Camera(){};
~Camera(){};
int Init(IOSourceType source_type, std::string source_path, int img_width, int img_height);
void* LoadYUV(int img_width, int img_height);
int DeInit();
private:
IOSourceType source_type_ = IOSourceType::kLocalFile;
std::string source_path_ = "";
int img_width_ = 0;
int img_height_ = 0;
bool is_running_ = false;
};
}//namespace vio_plugin
}//namespace zytracker
在src/文件夹内添加camera.cpp,通过接口读取frame, 返回值yuv_ptr即为从摄像头读取的NV21图像。
#include "smart_ae.h"
#include "camera.h"
namespace zytracker{
namespace vio_plugin{
int Camera::Init(IOSourceType source_type, std::string source_path, int img_width, int img_height){
source_path_ = source_path;
img_width_ = img_width;
img_height_ = img_height;
source_type_ = source_type;
hi_s32 result = sample_smart_ae_start_video_route(img_width_, img_height_);
if(result != HI_SUCCESS){
printf("start video route failed\n");
return -1;
}
return 0;
}
void* Camera::LoadYUV(int img_width, int img_height){
if(img_width != img_width_ || img_height != img_height_){
printf("img_width or img_height is not equal to init width or height\n");
return nullptr;
}
hi_s32 ret = HI_SUCCESS;
hi_video_frame_info src_frame;
hi_u32 size = 0, size_c = 0;
hi_mpi_isp_get_vd_time_out(0, OT_ISP_VD_FE_START, 500); /* 500ms */
ret = hi_mpi_vpss_get_chn_frame(0, 1, &src_frame, 500); /* 500ms */
if (ret != HI_SUCCESS) {
printf("hi_mpi_vpss_get_chn_frame fail, ret = 0x%x\n", ret);
}
size = src_frame.video_frame.stride[0] * src_frame.video_frame.height;
size_c = src_frame.video_frame.stride[1] * src_frame.video_frame.height / 2; /* YUV420, 4/2 */
int height = src_frame.video_frame.height;
int width = src_frame.video_frame.width;
int stride_y = src_frame.video_frame.stride[0];
src_frame.video_frame.virt_addr[0] = (char *)hi_mpi_sys_mmap(src_frame.video_frame.phys_addr[0], height*stride_y);
void* yuv_ptr = malloc(img_width_*img_height_*3/2);
memcpy(yuv_ptr, src_frame.video_frame.virt_addr[0], height*stride_y);
int stride_c = src_frame.video_frame.stride[1];
src_frame.video_frame.virt_addr[1] = (char*)hi_mpi_sys_mmap(src_frame.video_frame.phys_addr[1], stride_c*height/2);
memcpy(yuv_ptr+height*width, src_frame.video_frame.virt_addr[1], stride_c*height/2);
hi_mpi_sys_munmap(src_frame.video_frame.virt_addr[0], size);
hi_mpi_sys_munmap(src_frame.video_frame.virt_addr[1], size_c);
if (hi_mpi_vpss_release_chn_frame(0, 1, &src_frame) != HI_SUCCESS) {
printf("hi_mpi_vpss_release_chn_frame fail.\n");
}
return yuv_ptr;
}
int Camera::DeInit(){
sample_smart_ae_stop_video_route();
return 0;
}
}//namespace vio_plugin
}//namespace zytracker
添加main.cpp文件,调用接口读取并保存数据:
#include "smart_ae.h"
#include "camera.h"
using zytracker::vio_plugin::Camera;
using zytracker::vio_plugin::IOSourceType;
void writeYuvToFile(const char* filename, void* yuvData, size_t dataSize) {
FILE* file = fopen(filename, "wb");
if (!file) {
perror("Failed to open file");
return;
}
fwrite(yuvData, 1, dataSize, file);
fclose(file);
}
int main(int argc, char* argv[]){
Camera camera;
camera.Init(IOSourceType::kLocalFile, "", 1024, 576);
void* yuv_ptr = camera.LoadYUV(1024, 576);
writeYuvToFile("ouput.yuv", yuv_ptr, 1024*576*3/2);
free(yuv_ptr);
camera.DeInit();
return 0;
}
整理后文件目录如下:
<Docker> [root@07276ee5fc5e]:/workspace/tmp/smart_ae$ tree . -L 1
.
|-- CMakeLists.txt
|-- build.sh
|-- common
|-- include
|-- lib
|-- main.cpp
`-- src
添加CMakeLists.txt进行编译即可编译可执行文件进行运行读取图像。需要注意的是add_definitions内的内容,需要根据自己情况进行替换,比如摄像头的sensor_type(非常重要)。
cmake_minimum_required(VERSION 3.10)
project(smart_ae)
# Compile options
add_compile_options(
-fstack-protector-all
-D_FORTIFY_SOURCE=2
-Wno-sign-compare
)
set(CMAKE_C_FLAGS_DEBUG "-fPIC -O0 -g -Wall")
set(CMAKE_CXX_FLAGS_DEBUG "-fPIC -O0 -g -Wall -std=c++14")
set(CMAKE_C_FLAGS_RELEASE "-fPIC -O2 -Wall -s")
set(CMAKE_CXX_FLAGS_RELEASE "-fPIC -O2 -Wall -s -std=c++14")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro,-z,now,-z,noexecstack -fPIE -pie")
set(CMAKE_SKIP_RPATH TRUE)
add_definitions(
-DSC4336P_MIPI_4M_30FPS_10BIT_SELECT=y
-DSENSOR0_TYPE=SC4336P_MIPI_4M_30FPS_10BIT
-DSENSOR1_TYPE=SC4336P_MIPI_4M_30FPS_10BIT
-Dhi3516cv610
-DOT_XXXX
-DISP_V2
-DVER_X=1
-DVER_Y=0
-DVER_Z=0
-DVER_P=0
-DVER_B=10
-DUSER_BIT_32
-DKERNEL_BIT_32
-DOT_RELEASE
-DBOARD_TYPE=DMEB_QFN
-DOT_ACODEC_TYPE_INNER
-DOT_VQE_USE_STATIC_MODULE_REGISTER
)
include_directories(
${PROJECT_SOURCE_DIR}/common/
${PROJECT_SOURCE_DIR}/include/
)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mcpu=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=softfp")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=softfp")
set(AE_LIB_PATH ${PROJECT_SOURCE_DIR}/lib)
link_directories(
${AE_LIB_PATH}
)
set(AE_LIB hi_mpi_cipher hi_mpi_smartae
hi_mpi hi_mpi_sysmem hi_mpi_sysbind
hi_mpi_ae hi_mpi_isp ot_mpi_isp
hi_mpi_awb dehaze extend_stats
drc ldci bnr calcflicker ir_auto acs
sns_sc4336p hi_mpi_km bnr
hi_mpi_ive hi_ivs_md
)
set(AE_SRC
${PROJECT_SOURCE_DIR}/common/sample_comm_vi.c
${PROJECT_SOURCE_DIR}/common/sample_comm_vpss.c
${PROJECT_SOURCE_DIR}/common/sample_comm_sys.c
${PROJECT_SOURCE_DIR}/common/sample_comm_isp.c
${PROJECT_SOURCE_DIR}/common/sample_comm_venc.c
)
add_executable(main main.cpp src/sample_smart_ae.c src/camera.cpp ${AE_SRC})
target_link_libraries(main ${AE_LIB} stdc++ m pthread securec dl protobuf-c)
set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/)
install(TARGETS main DESTINATION .)
用7yuv打开保存的yuv文件看到图像,说明代码可以正常运行
总结
本文详细介绍了基于海思Hi3516CV610芯片的摄像头数据读取开发全流程,通过Docker快速搭建开发环境,提取并整理海思SDK中的关键依赖文件和示例代码,解决了官方文档不全导致的开发难题。重点对sample_smart_ae.c进行改造,封装出可设置图像尺寸的C++接口类,实现了YUV数据的稳定读取和保存。项目提供了完整的CMake工程配置,包含交叉编译选项、库文件链接和预定义宏设置,可直接编译使用。最终通过7yuv工具验证了YUV数据的正确性,为后续视频处理开发奠定了基础。文中分享的代码架构设计、编译参数优化和问题排查经验,对Hi3516平台开发者具有重要参考价值,可显著降低同类项目的开发门槛。完整工程文件已提供百度网盘下载。
附录 下载链接
本项目,代码文件百度网盘下载地址
链接: https://pan.baidu.com/s/17W62l8NK6VRw_E4jXTddDg 提取码: s834