当前位置: 首页 > ai >正文

RV1126单目摄像头取流,实现双路输出(一路H.264编码推流,一路给算法)

RV1126单目摄像头取流,实现双路输出

    • 0 前言
    • 1 环境搭建与准备​
      • 1.1 硬件环境​
      • 1.2 软件环境​
      • 1.2.1 设备树配置
      • 1.2.2 SDK完整编译
    • 2 实现思路与关键代码
      • 2.1 实现思路
        • 2.1.1 视频流节点
        • 2.1.2 视频编码
      • 2.2 关键代码
        • 2.2.1 Cmake配置
        • 2.2.2 初始化配置
        • 2.2.3 推流回调
        • 2.2.4 取流推送给算法
    • 3 运行效果
      • 3.1 ffmpeg拉流实时播放
      • 3.2 算法端实时处理
    • 4 参考资料

0 前言

在机器人项目开发过程中,经常会需要从单目摄像头(算法爱叫它sensor)获取视频流,并将其进行双路输出,一路进行H.264编码后实时推流用于远程监控等场景,另一路直接输出给算法进行图像分析、目标检测等处理。本文将详细介绍如何在RV1126上实现这一功能。

1 环境搭建与准备​

1.1 硬件环境​

首先,确保我们的硬件平台是基于RV1126的(这里我使用AIO-1126-JD4开发板),并且正确连接好单目摄像头OS04A10(通常是MIPI接口)。RV1126支持两组MIPI CSI,也就是可以两路摄像头同时输入(后期可扩展为双目)。AIO-1126-JD4开发板集成了一个百兆以太网口、一个千兆以太网口和WiFi蓝牙一体的SIP模组AP6236,方便后续的H.264编码推流。
详细可官网了解:AIO-1126-JD4
在这里插入图片描述
在这里插入图片描述

1.2 软件环境​

1.2.1 设备树配置

使能csi_dphy0节点,并配置好os04a10摄像头的支持。
在这里插入图片描述
在这里插入图片描述

1.2.2 SDK完整编译

参考《Ubuntu20.04/22.04下Docker方案实现多平台SDK编译》第2章将SDK完整的编译一遍,这样,我们就可得到交叉编译工具链及依赖环境rv1126_rv1109_linux_release_20211022/buildroot/output/firefly_rv1126_rv1109/host
在这里插入图片描述

2 实现思路与关键代码

2.1 实现思路

2.1.1 视频流节点

RV1126平台的ISPP可同时提供4种分辨率视频流,用户层可以看到ISPP驱动提供的4个视频节点。rkispp_m_bypass不支持缩放,分辨率仅能保持sensor最大分辨率。rkispp_scale0分辨率超过2K之后,需要使用NV16格式。
在这里插入图片描述
我们接入摄像头后,在板子上可以通过以下指令看到rkispp_scale0rkispp_scale1视频节点,可以用这两个视频节点实现双路取流。

grep '' /sys/class/video4linux/video*/name

在这里插入图片描述
rkispp_scale0节点的视频流用来传送给算法,rkispp_scale1节点的视频流用来编码成H.264后推流出去。有小伙伴有疑问,为什么我不用ROS直接发布出去?我只能说视频流和图片帧发布不是同一个概念,也不是一个量级的,编码推流的传输效率会高些。

2.1.2 视频编码

RV1126的视频编码模块(VENC)支持多路实时编码,且每路编码独立,编码协议和编码profile可以不同。支持视频编码同时,调度Region模块对编码图像内容进行叠加和遮挡。支持H264/H265/MJPEG/JPEG编码。
在这里插入图片描述

2.2 关键代码

2.2.1 Cmake配置

cmake指定系统根目录,并配置查找路径和交叉编译工具gcc、g++等。

set(TARGET_SYSROOT "/home/wzl/workspace/firefly/rv1126_rv1109_linux_release_20211022/buildroot/output/firefly_rv1126_rv1109/host/arm-buildroot-linux-gnueabihf/sysroot")set(CMAKE_SYSROOT "${TARGET_SYSROOT}")
set(CMAKE_FIND_ROOT_PATH ${TARGET_SYSROOT} ${NATIVE_SYSROOT})
...
set(TOOLCHAIN_ROOT "/home/wzl/workspace/firefly/rv1126_rv1109_linux_release_20211022/buildroot/output/firefly_rv1126_rv1109/host")
set(CMAKE_C_COMPILER "${TOOLCHAIN_ROOT}/bin/arm-linux-gnueabihf-gcc")
set(CMAKE_CXX_COMPILER "${TOOLCHAIN_ROOT}/bin/arm-linux-gnueabihf-g++")
set(CMAKE_ASM_COMPILER "${TOOLCHAIN_ROOT}/bin/arm-linux-gnueabihf-gcc")
set(CMAKE_AR "${TOOLCHAIN_ROOT}/bin/arm-linux-gnueabihf-ar" CACHE FILEPATH "Archiver")
set(CMAKE_RANLIB "${TOOLCHAIN_ROOT}/bin/arm-linux-gnueabihf-ranlib")

另外,还需链接RK媒体库easymedia和推流库rtsp

target_link_libraries(${PROJECT_NAME}easymediartsp...)
2.2.2 初始化配置

VI[0]用来取流传给算法,VI[1]用来编码后推流。

int Camera::Init() {RK_U32 u32Width = 640;RK_U32 u32Height = 480;int frameCnt = 30;RK_CHAR *pDeviceName = "rkispp_scale0";RK_CHAR *pDeviceName_rtsp = "rkispp_scale1";// 初始化rtsp推流g_rtsplive = create_rtsp_demo(554);g_rtsp_session = rtsp_new_session(g_rtsplive, "/live/main_stream");rtsp_set_video(g_rtsp_session, RTSP_CODEC_ID_VIDEO_H264, NULL, 0);rtsp_sync_video_ts(g_rtsp_session, rtsp_get_reltime(), rtsp_get_ntptime());RK_MPI_SYS_Init();VI_CHN_ATTR_S vi_chn_attr;vi_chn_attr.pcVideoNode = pDeviceName;vi_chn_attr.u32BufCnt = 3;vi_chn_attr.u32Width = u32Width;vi_chn_attr.u32Height = u32Height;vi_chn_attr.enPixFmt = IMAGE_TYPE_RGB888;vi_chn_attr.enWorkMode = VI_WORK_MODE_NORMAL;vi_chn_attr.enBufType = VI_CHN_BUF_TYPE_MMAP;int ret = RK_MPI_VI_SetChnAttr(s32CamId, 0, &vi_chn_attr);ret |= RK_MPI_VI_EnableChn(s32CamId, 0);if (ret) {printf("ERROR: create VI[0] error! ret=%d\n", ret);return -1;}vi_chn_attr.pcVideoNode = pDeviceName_rtsp;vi_chn_attr.enPixFmt = IMAGE_TYPE_NV12;ret = RK_MPI_VI_SetChnAttr(s32CamId, 1, &vi_chn_attr);ret |= RK_MPI_VI_EnableChn(s32CamId, 1);if (ret) {printf("ERROR: create VI[1] error! ret=%d\n", ret);return -1;}VENC_CHN_ATTR_S venc_chn_attr;memset(&venc_chn_attr, 0, sizeof(venc_chn_attr));venc_chn_attr.stVencAttr.enType = RK_CODEC_TYPE_H264;venc_chn_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;venc_chn_attr.stRcAttr.stH264Cbr.u32Gop = 30;venc_chn_attr.stRcAttr.stH264Cbr.u32BitRate = u32Width * u32Height;// frame rate: in 30/1, out 30/1.venc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1;venc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 30;venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1;venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 30;venc_chn_attr.stVencAttr.imageType = IMAGE_TYPE_NV12;venc_chn_attr.stVencAttr.u32PicWidth = u32Width;venc_chn_attr.stVencAttr.u32PicHeight = u32Height;venc_chn_attr.stVencAttr.u32VirWidth = u32Width;venc_chn_attr.stVencAttr.u32VirHeight = u32Height;venc_chn_attr.stVencAttr.u32Profile = 77;ret = RK_MPI_VENC_CreateChn(1, &venc_chn_attr);if (ret) {printf("ERROR: create VENC[1] error! ret=%d\n", ret);return -1;}//注册编码通道的回调函数MPP_CHN_S pstChn;pstChn.enModId = RK_ID_VENC;//模块号pstChn.s32DevId = 0;//设备号 第一个摄像头pstChn.s32ChnId = 1;//通道号 第一个通道ret = RK_MPI_SYS_RegisterOutCb(&pstChn, video_packet_cb);if(ret != 0){printf("注册编码通道的回调函数失败\n");return -1;}//绑定venc通道pstSrcChn.enModId = RK_ID_VI;pstSrcChn.s32DevId = 0;pstSrcChn.s32ChnId = 1;pstDestChn.enModId = RK_ID_VENC;pstDestChn.s32DevId = 0;pstDestChn.s32ChnId = 1;ret = RK_MPI_SYS_Bind(&pstSrcChn,&pstDestChn);if(ret != 0){printf("绑定venc通道失败\n");return -1;}p_get_image_thread = std::make_shared<std::thread>(&Camera::getImageWork, this);p_get_image_thread->detach();ret = RK_MPI_VI_StartStream(s32CamId, 0);if (ret) {printf("Start VI[0] failed! ret=%d\n", ret);return -1;}return 0;
}
2.2.3 推流回调
void video_packet_cb(MEDIA_BUFFER mb) {static RK_S32 packet_cnt = 0;printf("#Get packet-%d, size %zu\n", packet_cnt, RK_MPI_MB_GetSize(mb));//推流if (g_rtsplive && g_rtsp_session) {rtsp_tx_video(g_rtsp_session, (const uint8_t *)RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb),RK_MPI_MB_GetTimestamp(mb));rtsp_do_event(g_rtsplive);}// fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, fp);RK_MPI_MB_ReleaseBuffer(mb);	//释放帧数据packet_cnt++;
}
2.2.4 取流推送给算法
void Camera::getImageWork() {int frame_id = 0;MEDIA_BUFFER mb = NULL;while (!quit) {mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VI, s32CamId, -1);if (!mb) {printf("RK_MPI_SYS_GetMediaBuffer get null buffer!\n");break;}MB_IMAGE_INFO_S stImageInfo = {0};int ret = RK_MPI_MB_GetImageInfo(mb, &stImageInfo);if (ret) {printf("Warn: Get image info failed! ret = %d\n", ret);printf("Get Frame:ptr:%p, fd:%d, size:%zu, mode:%d, channel:%d, ""timestamp:%lld, ImgInfo:<wxh %dx%d, fmt 0x%x>\n",RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetFD(mb), RK_MPI_MB_GetSize(mb),RK_MPI_MB_GetModeID(mb), RK_MPI_MB_GetChannelID(mb),RK_MPI_MB_GetTimestamp(mb), stImageInfo.u32Width,stImageInfo.u32Height, stImageInfo.enImgType);}double timestamp = api::GetSteadyClockS();cv::Mat image(480, 640, CV_8UC3, RK_MPI_MB_GetPtr(mb));this->trackImageSig(image, timestamp);RK_MPI_MB_ReleaseBuffer(mb);}
}

3 运行效果

3.1 ffmpeg拉流实时播放

192.168.0.4是RV1126板子的IP地址。

ffplay "rtsp://192.168.0.4/live/main_stream"

在这里插入图片描述

3.2 算法端实时处理

这里移植了ORB_SLAM3,感兴趣的小伙伴可以关注我后期的博客更新。
在这里插入图片描述
(缺一张SLAM配图,后期补上)

4 参考资料

[1] Rockchip_Developer_Guide_Linux_RKMedia_CN.pdf

http://www.xdnf.cn/news/3735.html

相关文章:

  • 【React】 Hooks useTransition 解析与性能优化实践
  • 套接字+Socket连接
  • Y1模拟一 补题报告
  • function包装器的意义
  • Milvus(13):自定义分析器、过滤器
  • Dubbo(94)如何在金融系统中应用Dubbo?
  • validator - Go 结构体验证库
  • 每天五分钟深度学习框架PyTorch:基于Dataset封装自定义数据集
  • 深入理解Java垃圾回收机制
  • NV228NV254固态美光颗粒NV255NV263
  • 2025年01月03日美蜥(杭州普瑞兼职)一面
  • 【C++ Qt】输入类控件(上) LineEdit、QTextEdit
  • 升级 CUDA Toolkit 12.9 与 cuDNN 9.9.0 后验证指南:功能与虚拟环境检测
  • 黑马点评day01(基于Redis)
  • C++学习:六个月从基础到就业——C++11/14:右值引用与移动语义
  • Webug4.0靶场通关笔记14- 第18关 文件上传之Nginx解析缺陷
  • Linux线程深度解析:从基础到实践
  • 码蹄集——偶数位、四边形坐标
  • 南京优质的公司有哪些?
  • 小程序 IView WeappUI组件库(简单增删改查)
  • 变更需求代价:影响分析
  • Java面试大纲(以及常见面试问答)
  • 19、权限控制:分院帽系统——React 19 RBAC实现
  • iview自定义下拉树菜单
  • 【C++】 —— 笔试刷题day_25
  • C++多态(下)
  • 【办公类-99-04】20250504闵豆统计表excle转PDF,合并PDF、添加中文字体页眉+边框下划线
  • 观察者模式(Observer Pattern)详解
  • 计算机系统结构 第二章 :缓存优化
  • vector的两种实现