欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 八卦 > RV1126单目摄像头取流,实现双路输出(一路H.264编码推流,一路给算法)

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

2025/5/5 12:58:35 来源:https://blog.csdn.net/w1820020635/article/details/147686992  浏览:    关键词: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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词