跳到主要内容

RKMPI 实例使用指南

1 简介

利用 RKMPI 库实现摄像头图像捕获、预处理、硬件编码,结合 opencv-mobile 进行图像处理,可以将 RKNN 推理结果标注在图像上后作为作为流媒体服务器进行 rtsp 推流,在 局域网 下的 PC 可以使用 VLC 软件拉取并观察图像。

实例包括 RKMPI 库基本使用、opencv-mobile 对数据帧处理两种方式、RTSP 推流结合 RKNN 推理两种方式。主要实现效果:

  • 基于 retinaface 人脸检测网络摄像头

  • 基于 yolov5 物体检测网络摄像头

2 平台支持

DEMOCPU系统摄像头
luckfox_pico_rtsp_opencvRV1103(需修改分辨率)、RV1106buildrootsc3336
luckfox_pico_rtsp_opencv_captureRV1103、RV1106buildrootsc3336
luckfox_pico_rtsp_retinafaceRV1103、RV1106buildrootsc3336
luckfox_pico_rtsp_retinaface_osdRV1103、RV1106buildrootsc3336
luckfox_pico_rtsp_yolov5RV1106buildrootsc3336
  • RV1103Luckfox Pico Luckfox Pico Mini A Luckfpx Pico Miini B Luckfox Pico Plus
  • RV1106Luckfox Pico Pro Luckfox Pico Max Luckfox Pico Ultra Luckfox Pico Ultra W

3 环境搭建

  1. 确认 Luckfox-pico Sdk 的正确安装,确认交叉编译链的环境变量设置正确

    arm-rockchip830-linux-uclibcgnueabihf-gdb --version
    • 显示交叉编译链的版本信息说明,说明环境变量设置正确。
  2. 使用编译好的 buildroot 镜像或从网盘中获取镜像。确保摄像头驱动、 rknpu 驱动和 rockit 驱动、 rkisp 驱动等成功被加载。 在 Luckfox-pico 板端上执行 lsmod 验证。

    # lsmod
    Module Size Used by Tainted: G
    rockit 154528 0
    rknpu 24552 0
    mpp_vcodec 375759 1 rockit
    rga3 86680 1 rockit
    sc3336 9940 1
    os04a10 15198 0
    phy_rockchip_csi2_dphy 7674 0
    phy_rockchip_csi2_dphy_hw 8161 0
    video_rkisp 148435 1 rockit
    video_rkcif 130130 0
    rk_dvbm 5813 2 mpp_vcodec,video_rkisp
    • 注意: 要确保镜像中的根文件系统有足够的空闲空间,推荐使用运行在 SD 卡上的系统。

4 RKMPI 库使用

4.1 系统概览

Rockchip 提供的媒体处理接口( Rockchip Media Process Interface,简称 RKMPI ),可支持多媒体应用软件快速开发。该平台整合了 Rockchip 芯片的硬件资源,对应用软件屏蔽了芯片相关的复杂底层处理,使用户在软件层面调用相应接口就可以完成媒体处理开发,如输入视频捕获、H264 / H265 / JPEG 编码、视频图像处理等。

系统框架

  • 应用层:基于 RKMPI 及其他驱动,由用户开发的应用软件系统。
  • RKMPI 层:基于适配层( 已有的 Rockchip 芯片适配封装接口 ),完成媒体处理功能。
  • 操作系统适配层:基于 Rockchip 芯片已有的硬件模块对外提供的驱动接口。
  • 操作系统层:基于 Linux 系统。
  • 硬件层:由 Rockchip 芯片加上必要的外围器件构成。

IPC(网络摄像机)应用框架

  • VI 模块捕获视频图像,做剪切、缩放等处理,可以输出多路不同分辩率的图像数据。
  • VPSS 模块接收 VI 模块传输的图像,可对图像进行裁剪、缩放、旋转、像素格式转换等处理。
  • VENC 模块可以直接接收 VI 模块捕获的图像或 VPSS 处理后输出的图像数据,可叠加用户通过 RGN 模块设置的 OSD 图像,然后按不同协议进行编码并输出相应码流。
  • 各个模块通道之间进行绑定,处理后的数据流会直接传输到绑定的下一个多媒体模块。

4.2 内存管理

RKMPI 库的各个组件主要使用内存缓冲池来进行内存管理,内存缓冲池通过内存块进行内存分配和回收,使内存资源在各个媒体处理模块中合理使用。内存缓冲池与其他应用内存相隔离,保证了 RKMPI 库的稳定运行,但是应用数据无法直接对内存缓冲池内的内存进行操作。

内存缓冲池运行方式

  • 内部分配 私有模式下,内存缓冲池由媒体处理模块的通道自行申请,所申请的大小、个数由通道内部根据所设参数申请。
  • 外部分配 共有模式下,内存缓冲池可以由用户自己创建,设置内存缓冲块的大小、属性与数据,传递给指定模块使用。

4.3 视频输入 VI

视频输入组件( VI )实现了将 MIPI Rx( 包含 MIPI 接口、LVDS 接口 )等接口接收视频数据的功能。 VI 组件将接收到的数据存入到指定的内存区域,实现视频数据的采集。

VI 内部数据处理

VI 从软件上划分未输入设备( dev )、输入管道( pipe )、输入通道( channel )三个层级。

  • VI 设备( dev ):支持若干种时序输入,负责对时序进行解析。
  • VI 管道( pipe ):绑定在设备后端,负责设备解析后的数据再处理,主要用于载入 ISP 算法。
  • VI 通道( channel ):视频输入最后一级获取通道。

VI 基础使用

  1. 在进行 VI 模块初始化前请先运行 ISP 算法实现自动曝光控制、自动增益控制、自动白平衡、色彩校正等操作,保证捕获图像的质量。
    // CamID:       摄像头 ID
    // hdr_mode: HDR 模式
    // multi_sensor:多路摄像头
    // iq_dir: 摄像头 ISP 算法配置文件 iqfile 所在目录
    SAMPLE_COMM_ISP_Init(CamId, hdr_mode, multi_sensor, iq_dir);
  2. 运行 ISP 算法
    SAMPLE_COMM_ISP_Run(CamId);
  3. 启动 VI 设备
    RK_MPI_VI_EnableDev(devID);
  4. 绑定 VI 设备与 VI 管道
    RK_MPI_VI_SetDevBindPipe(devID, &stBindPipe);
  5. 设置 VI 通道属性
    RK_MPI_VI_SetChnAttr(PipeID, ChnID, &Chn_attr);
  • 注意: VI 通道的输出分辨率参考摄像头模组支持分辨率比率,避免出现图像畸变的情况。
  1. 启动 VI 通道
    RK_MPI_VI_EnableChn(PipeID, ChnID);
  2. 绑定其他多媒体模块
    MPP_CHN_S viChn;
    viChn.enModId = RK_ID_VI;
    viChn.s32DevID = DevID;
    viChn.s32ChnID = ChnID;
    RK_MPI_SYS_Bind(&viChn, &otherChn);
  3. 与其他多媒体取消绑定
    RK_MPI_SYS_UnBind(&viChn,&otherChn);
  4. 关闭 VI 通道与设备
    RK_MPI_VI_DisableChn(PipeID,ChnID);
    RK_MPI_VI_DisableDev(DevID);

4.4 视频处理子系统 VPSS

视频处理子系统( VPSS )支持对输入图像镜像缩放,固定角度旋转,像素格式转换、裁剪等处理。

VPSS 上下文关系

VPSS 可与 VI 和 VENC 等模块进行绑定,前者为 VPSS 的输入源,后者为 VPSS 的接收者。用户可通过 VPSS 接口对组进行管理,每个组仅可与一个输入源绑定。

  • VPSS 组( group ):控制输入,各个组分时复用硬件设备,硬件依次处理各个组提交的任务。
  • VPSS 通道( channel ):控制输出,每个通道具有缩放、裁剪等功能,通道处理后的图像发送给 VPSS 的接收者。

VPSS 基础使用

  1. 创建 VPSS 组

    RK_MPI_VPSS_CreateGrp(GrpID, &VpssGrpAttr);
  2. 设置 VPSS 通道属性

    RK_MPI_VPSS_SetChnAttr(GrpID, ChnID, &VpssChnAttr);
    • 注意: VPSS 组负责管理输入,设置属性时注意设置 enPixelFormat 属性与绑定的输入源一致,u32MaxWu32MaxH 大于输入源的捕获图像大小。
  3. 启动 VPSS 通道

    RK_MPI_VPSS_EnableChn(GrpID, ChnID);
  4. 启动 VPSS 组

    RK_MPI_VPSS_StartGrp(GrpID);
  5. 输出 VPSS 数据

    • 绑定其它多媒体模块
      MPP_CHN_S vpssChn;
      vpssChn.enModId = RK_ID_VI;
      vpssChn.s32DevID = DevID;
      vpssChn.s32ChnID = ChnID;
      RK_MPI_SYS_Bind(&vpssChn, &otherChn);
    • 直接获取帧数据
      RK_MPI_VPSS_GetChnFrame(GrpID, ChnID, &VpssFrame,-1);
      void *data = RK_MPI_MB_Handle2VirAddr(VpssFrame.stVFrame.pMbBlk);
  6. 停止并关闭 VPSS 组

    RK_MPI_VPSS_StopGrp(GrpID);
    RK_MPI_VPSS_DestroyGrp(GrpID);

4.5 视频编码 VENC

视频编码组件( VENC )支持多路实时编码且每路编码独立,支持在编码的同时调度 Region 模块对编码图像内容进行叠加和遮挡。主要支持的编码格式有 H264 、 H265 、 JPEG 、 MJPEG 。

VENC 内部数据处理

VENC 从数据帧中获取图像,进行图像处理、 RGN 模块处理后传入编码通道,结合数据帧的时间戳等信息进行编码。

  • VENC 通道( channel ):管理图像的输入与编码属性,使用 RK_MPI_VENC_StartRecvFrame 接口来启动视频编码接收输入图像数据。

VENC 基础使用

  1. 创建并设置 VENC 通道
    RK_MPI_VENC_CreateChn(chnId, &stAttr);
    • VENC 通道需要设置 enTypeu32Profile 参数来设置编码类型
      // 设置编码类型为 H264
      stAttr.stVencAttr.enType = RK_VIDEO_ID_AVC;
      stAttr.stVencAttr.u32Profile = H264E_PROFILE_MAIN;
    • VENC 需要根据设置的编码类型设置相应的结构体参数
      // 设置 H264 编码属性 
      stAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
      stAttr.stRcAttr.stH264Cbr.u32BitRate = BitRate;
      stAttr.stRcAttr.stH264Cbr.u32Gop = Gop;
  2. 开始接收帧数据
    RK_MPI_VENC_StartRecvFrame(ChnId, &stRecvParam);
  3. 从 VENC 输出中获取帧数据
    RK_MPI_VENC_GetStream(ChnId, &stFrame, -1);
  4. 获取 VENC 帧内存块虚拟地址
    void *Data = RK_MPI_MB_Handle2VirAddr(stFrame.pstPack->pMbBlk);
  5. 停止接收帧数据
    RK_MPI_VENC_StopRecvFrame(ChnId);
  6. 关闭 VENC 通道
    RK_MPI_VENC_DestroyChn(ChnId);

5 RKRTSP 库使用

RKRTSP 是 RockChip 提供的一套仅供测试使用的 rtsp 服务器软件接口,仅作为 RKMPI 库的功能验证使用,可以通过软件接口快速实现 rtsp 推流功能。

5.1 RKRTSP 基础使用

  1. 创建 rtsp 实例

    g_rtsplive = create_rtsp_demo(port);
  2. 创建 rtsp 接口

    g_rtsp_session = rtsp_new_session(g_rtsplive, "/live/0");
  3. 设置 rtsp 传输属性

    rtsp_set_video(g_rtsp_session, RTSP_CODEC_ID_VIDEO_H264, NULL, 0);
    • 注意: 目前 RKRTSP 库软件接口暂不支持 MJPEG 推流。
  4. 同步 rtsp 时间戳

    rtsp_sync_video_ts(g_rtsp_session, rtsp_get_reltime(), rtsp_get_ntptime());
  5. 更新视频流设置并驱动事件

    rtsp_tx_video(g_rtsp_session, (uint8_t *)pData, PackLen, PTS);
    rtsp_do_event(g_rtsplive);
  6. 删除 rtsp 实例

    rtsp_del_demo(g_rtsplive);

6 opencv-mobile 标注推流帧数实例

6.1 图像帧获取

RKMPI 库内部内存使用内存缓冲进行管理,在应用层想要实现对捕获图像进行处理再进行 rtsp 推流有两种方式。

  • 使用 VI 组件进行捕获

    将捕获的图像经过 VPSS 转换像素格式后获取帧数据,转换为 cv::Mat 类型后使用 opencv-mobile 进行处理,最后拷贝回帧数据内存缓冲块虚拟地址,使用 RK_MPI_VENC_SendFrame 将 VPSS 帧数据传输到 VENC 组件进行编码。

  • 使用 opencv-mobile 进行捕获

    创建内存缓冲池,从内存缓冲池中获取内存缓冲块存放图像数据,使用 opencv-mobile 捕获图像进行处理,转换像素格式拷贝入内存缓冲块,最后调用 RK_MPI_VENC_SendFrame 将存放内存缓冲块地址、时间戳、图像大小等信息的 VIDEO_FRAME_INFO_S 结构体数据传输到 VENC 组件进行编码。

6.2 基于 VI 图像捕获 rtsp 推流实例

运行效果

使用 Luckfox-pico pro 的 VI 组件捕获 2304 × 1296 图像,使用 opencv-mobile 标注帧率后编码为 H264 进行 rtsp 推流,可以达到 23 帧左右。

注意: 使用 Luckfox-pico mini / plus 进行测试时可以因为内存限制无法正常运行,请尝试降低捕获分辨率。

实现流程

  1. RKRTSP、 RKMPI、 RKAIQ 初始化

    // RKAIQ Init
    SAMPLE_COMM_ISP_Init(CamID, hdr_mode, multi_sensor, iq_dir);
    SAMPLE_COMM_ISP_Run(CamID);

    // RTSP Init
    g_rtsplive = create_rtsp_demo(554);
    g_rtsp_session = rtsp_new_session(g_rtsplive, "/live/0");
    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());

    // RKMPI Init
    RK_MPI_SYS_Init();
  2. VI、 VPSS、 VENC 组件初始化

    // VI Init
    vi_dev_init();
    vi_chn_init(0, width, height);

    // VPSS Init
    vpss_init(0, width, height);

    // VENC Init
    RK_CODEC_ID_E enCodecType = RK_VIDEO_ID_AVC;
    venc_init(0, width, height, enCodecType);
    • VI 通道的像素格式要与 VPSS 组的像素格式一致
      vi_chn_attr.enPixelFormat = RK_FMT_YUV420SP; // VI 通道属性

      stGrpVpssAttr.enPixelFormat = RK_FMT_YUV420SP; // VPSS 组属性
    • 为了方便将数据传入 RKNN 模型中进行推理,使用 VPSS 组件将像素格式转换为 RGB888
      stVpssChnAttr.enPixelFormat = RK_FMT_RGB888;
    • VENC 设置进行 H264 编码的相关属性
      stAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
      stAttr.stRcAttr.stH264Cbr.u32BitRate = BitRate;
      stAttr.stRcAttr.stH264Cbr.u32Gop = GOP;
      • 注意: 例程中使用的 GOP 为 1 , 设置所有数据帧都为 I 帧,在 PC 端使用 VLC 软件设置缓存时间不能低于 500 ms,可以尝试修改进行优化。
  3. 绑定 VI 通道到 VPSS 上

    stSrcChn.enModId = RK_ID_VI;
    stSrcChn.s32DevId = 0;
    stSrcChn.s32ChnId = 0;

    stvpssChn.enModId = RK_ID_VPSS;
    stvpssChn.s32DevId = 0;
    stvpssChn.s32ChnId = 0;
    s32Ret = RK_MPI_SYS_Bind(&stSrcChn, &stvpssChn);
  4. 获取 VPSS 数据帧和对应内存缓冲块虚拟地址

    RK_MPI_VPSS_GetChnFrame(DevId, ChnId, &stVpssFrame, -1);
    void *data = RK_MPI_MB_Handle2VirAddr(stVpssFrame.stVFrame.pMbBlk);
  5. 使用 opencv-mobile 标注帧率

    cv::Mat frame(height,width,CV_8UC3,data);   
    cv::putText(frame,fps_text,
    cv::Point(40, 40),
    cv::FONT_HERSHEY_SIMPLEX,1,
    cv::Scalar(0,255,0),2);
  6. 发送数据到 VENC 中编码为 H264 格式

    //为了保证数据的完整性使用阻塞发送。
    RK_MPI_VENC_SendFrame(vpssChn, &stVpssFrame,-1);
  7. 进行 rtsp 推流

    RK_MPI_VENC_GetStream(vencChn, &stFrame, -1);
    RK_MPI_MB_Handle2VirAddr(stFrame.pstPack->pMbBlk);
    rtsp_tx_video(g_rtsp_session, (uint8_t *)pData, stFrame.pstPack->u32Len,
    stFrame.pstPack->u64PTS);
    rtsp_do_event(g_rtsplive);
  8. 资源释放

    RK_MPI_SYS_UnBind(&stSrcChn, &stvpssChn);

    RK_MPI_VI_DisableChn(DevId, ChnId);
    RK_MPI_VI_DisableDev(DevId);

    RK_MPI_VPSS_StopGrp(GrpId);
    RK_MPI_VPSS_DestroyGrp(Grpid);

    RK_MPI_VENC_StopRecvFrame(ChnId);
    RK_MPI_VENC_DestroyChn(ChnId);
    // RKRTSP
    rtsp_del_demo(g_rtsplive);
    // RKIRQ
    SAMPLE_COMM_ISP_Stop(CamId);
    // RKMPI
    RK_MPI_SYS_Exit();

6.3 基于 opencv-mobile 图像捕获 rtsp 推流例程

运行效果

在 Luckfox-pico pro 上使用 opencv-mobile 捕获图像标注帧率后编码为 H264 进行 rtsp 推流,可以达到 9 帧左右。

注意: 使用 opencv-mobile 捕获摄像头图像的方式较为简单,但是失去了 VI 组件和 VPSS 组件的硬件加速,在性能上有明显损失,推荐使用 VI 来实现图像捕获。

实现流程

  1. 创建内存缓存池,分配内存缓存块

    MB_POOL src_Pool = RK_MPI_MB_CreatePool(&PoolCfg);
    MB_BLK src_Blk = RK_MPI_MB_GetMB(src_Pool, width * height * 3, RK_TRUE);
    • 创建内存缓冲池的设置的内存块数量不能小于捕获图像像素的数量
      PoolCfg.u64MBSize = width * height * channel ;
      PoolCfg.u32MBCnt = num;
      PoolCfg.enAllocType = MB_ALLOC_TYPE_DMA;
  2. RKRTSP、 RKMPI初始化

    // RTSP Init
    g_rtsplive = create_rtsp_demo(554);
    g_rtsp_session = rtsp_new_session(g_rtsplive, "/live/0");
    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());

    // RKMPI Init
    RK_MPI_SYS_Init();
  3. VENC 组件初始化

    // VENC Init
    RK_CODEC_ID_E enCodecType = RK_VIDEO_ID_AVC;
    venc_init(0, width, height, enCodecType);
    • VENC 设置进行 H264 编码的相关属性
      stAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
      stAttr.stRcAttr.stH264Cbr.u32BitRate = BitRate;
      stAttr.stRcAttr.stH264Cbr.u32Gop = GOP;
      • 注意: 例程中使用的 GOP 为 1 , 设置所有数据帧都为 I 帧,在 PC 端使用 VLC 软件设置缓存时间不能低于 500 ms,可以尝试修改进行优化。
  4. 构建 VIDEO_FRAME_INFO_S 数据帧结构体

    VIDEO_FRAME_INFO_S h264_frame;
    h264_frame.stVFrame.u32Width = width;
    h264_frame.stVFrame.u32Height = height;
    h264_frame.stVFrame.u32VirWidth = width;
    h264_frame.stVFrame.u32VirHeight = height;
    h264_frame.stVFrame.enPixelFormat = RK_FMT_RGB888;
    h264_frame.stVFrame.u32FrameFlag = 160;
    h264_frame.stVFrame.pMbBlk = src_Blk;
    unsigned char *data = (unsigned char *)RK_MPI_MB_Handle2VirAddr(src_Blk);
    • 数据帧像素格式要与 VENC 通道像素格式一致。
  5. 使用 opencv-mobile 捕获图像并标注帧率,将图像数据以 RGB888 的格式拷贝到数据帧的内存缓冲块中

    cap >> bgr;
    cv::putText(bgr,fps_text,
    cv::Point(40, 40),
    cv::FONT_HERSHEY_SIMPLEX,1,
    cv::Scalar(0,255,0),2);

    for (int y = 0; y < height; ++y) {
    for (int x = 0; x < width; ++x) {
    cv::Vec3b pixel = bgr.at<cv::Vec3b>(y, x);
    data[(y * width + x) * 3 + 0] = pixel[2]; // Red
    data[(y * width + x) * 3 + 1] = pixel[1]; // Green
    data[(y * width + x) * 3 + 2] = pixel[0]; // Blue
    }
    }
  6. 发送数据到 VENC 中编码为 H264 格式

    //为了保证数据的完整性使用阻塞发送。
    RK_MPI_VENC_SendFrame(vpssChn, &h264_Frame,-1);
  7. 进行 rtsp 推流

    RK_MPI_VENC_GetStream(vencChn, &stFrame, -1);
    RK_MPI_MB_Handle2VirAddr(stFrame.pstPack->pMbBlk);
    rtsp_tx_video(g_rtsp_session, (uint8_t *)pData, stFrame.pstPack->u32Len,
    stFrame.pstPack->u64PTS);
    rtsp_do_event(g_rtsplive);
  8. 资源释放

    // Destory MB
    RK_MPI_MB_ReleaseMB(src_Blk);
    // Destory Pool
    RK_MPI_MB_DestroyPool(src_Pool);
    // VENC
    RK_MPI_VENC_StopRecvFrame(ChnId);
    RK_MPI_VENC_DestroyChn(ChnId);
    // RKRTSP
    rtsp_del_demo(g_rtsplive);
    // RKMPI
    RK_MPI_SYS_Exit();

7 RKNN 推理图像 rtsp 推流实例

为了最大化提高 rtsp 推流的帧率,实例使用 VI 组件来实现摄像头图像捕获。在 RKNN 推理结果的标注上,采用两种方式来实现:

  • 将标注好的图像上传到 VENC 组件进行编码传输
  • 在图像上传到 VENC 组件后再使用 RGN 模块以打 OSD 的形式标注结果

7.1 基于 opencv-mobile 绘制 RKNN 推理结果

运行效果

使用 Luckfox-pico pro 的 VI 组件捕获 720 × 480 分辩率图像,经过 VPSS 组件像素格式转换,opencv-mobile 缩放后进行 RKNN 模型推理,将获取到的结果标注在原数据帧上传输到 VENC 组件进行编码传输。

注意: 受内存限制,Luckfox-pico mini / plus 无法运行 yolov5 物体检测例程。

  • 使用 retinaface 人脸检测模型推流帧率可达到 11 帧左右。

  • 使用 yolov5 物体检测模型推流帧率可达到 7 帧左右。

实现流程

  1. RKRTSP、 RKMPI、 RKAIQ 和 RKNN 初始化

    // RKNN Init
    init_rknn_model(model_path, &rknn_app_ctx);

    // RKAIQ Init
    SAMPLE_COMM_ISP_Init(CamID, hdr_mode, multi_sensor, iq_dir);
    SAMPLE_COMM_ISP_Run(CamID);

    // RTSP Init
    g_rtsplive = create_rtsp_demo(554);
    g_rtsp_session = rtsp_new_session(g_rtsplive, "/live/0");
    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());

    // RKMPI Init
    RK_MPI_SYS_Init();
  2. VI、 VPSS、 VENC 组件初始化

    // VI Init
    vi_dev_init();
    vi_chn_init(0, width, height);

    // VPSS Init
    vpss_init(0, width, height);

    // VENC Init
    RK_CODEC_ID_E enCodecType = RK_VIDEO_ID_AVC;
    venc_init(0, width, height, enCodecType);
    • VI 通道的像素格式要与 VPSS 组的像素格式一致
      vi_chn_attr.enPixelFormat = RK_FMT_YUV420SP; // VI 通道属性

      stGrpVpssAttr.enPixelFormat = RK_FMT_YUV420SP; // VPSS 组属性
    • 为了方便将数据传入 RKNN 模型中进行推理,使用 VPSS 组件将像素格式转换为 RGB888。
      stVpssChnAttr.enPixelFormat = RK_FMT_RGB888;
    • VENC 设置进行 H264 编码的相关属性
      stAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
      stAttr.stRcAttr.stH264Cbr.u32BitRate = BitRate;
      stAttr.stRcAttr.stH264Cbr.u32Gop = GOP;
      • 注意: 例程中使用的 GOP 为 1 , 设置所有数据帧都为 I 帧,在 PC 端使用 VLC 软件设置缓存时间不能低于 500 ms,可以尝试修改进行优化。
  3. 绑定 VI 通道到 VPSS 上

    stSrcChn.enModId = RK_ID_VI;
    stSrcChn.s32DevId = 0;
    stSrcChn.s32ChnId = 0;

    stvpssChn.enModId = RK_ID_VPSS;
    stvpssChn.s32DevId = 0;
    stvpssChn.s32ChnId = 0;
    s32Ret = RK_MPI_SYS_Bind(&stSrcChn, &stvpssChn);
  4. 获取 VPSS 数据帧和对应内存缓冲块虚拟地址

    RK_MPI_VPSS_GetChnFrame(DevId, ChnId, &stVpssFrame, -1);
    void *data = RK_MPI_MB_Handle2VirAddr(stVpssFrame.stVFrame.pMbBlk);
  5. opencv-mobile 读取 VPSS 数据帧后,将图像处理为模型推理的需求大小

    cv::Mat frame(height,width,CV_8UC3, data);          
    cv::Mat model_input;
    cv::resize(frame, model_input, cv::Size(model_width,model_height), 0, 0, cv::INTER_LINEAR);
    • 注意: 如果使用对原图像比例要求较高的特征值获取模型,不能简单对图像进行缩放,需要使用缩放并填充黑边的操作来处理图像。
  6. 获取推理结果标注在 VPSS 数据帧图像上。

    memcpy(rknn_app_ctx.input_mems[0]->virt_addr, model_input.data, 640*640*3);
    inference_rknn_model(&rknn_app_ctx, &od_results);
  7. 发送数据到 VENC 中编码为 H264 格式

    //为了保证数据的完整性使用阻塞发送
    RK_MPI_VENC_SendFrame(vpssChn, &stVpssFrame,-1);
  8. 进行 rtsp 推流

    RK_MPI_VENC_GetStream(vencChn, &stFrame, -1);
    RK_MPI_MB_Handle2VirAddr(stFrame.pstPack->pMbBlk);
    rtsp_tx_video(g_rtsp_session, (uint8_t *)pData, stFrame.pstPack->u32Len,
    stFrame.pstPack->u64PTS);
    rtsp_do_event(g_rtsplive);
  9. 资源释放

    RK_MPI_SYS_UnBind(&stSrcChn, &stvpssChn);

    RK_MPI_VI_DisableChn(DevId, ChnId);
    RK_MPI_VI_DisableDev(DevId);

    RK_MPI_VPSS_StopGrp(GrpId);
    RK_MPI_VPSS_DestroyGrp(Grpid);

    RK_MPI_VENC_StopRecvFrame(ChnId);
    RK_MPI_VENC_DestroyChn(ChnId);
    // RKRTSP
    rtsp_del_demo(g_rtsplive);
    // RKIRQ
    SAMPLE_COMM_ISP_Stop(CamId);
    // RKMPI
    RK_MPI_SYS_Exit();
    // RKNN
    release_rknn_model(&rknn_app_ctx);

7.2 基于 OSD 绘制 RKNN 推理结果

运行效果

使用 Luckfox-pico pro 的 VI 组件捕获 720 × 480 分辩率图像,设置输出两个通道,一个通道与 VPSS 组绑定,一个通道与 VENC 通道绑定,开启两个线程,一个线程负责 rtsp 推流,一个线程运行 RKNN 模型推理,将推理结果以 OSD 的形式显示 VENC 码流上。

实现流程

  1. RKRTSP、 RKMPI、 RKAIQ 和 RKNN 初始化

    // RKNN Init
    init_rknn_model(model_path, &rknn_app_ctx);

    // RKAIQ Init
    SAMPLE_COMM_ISP_Init(CamID, hdr_mode, multi_sensor, iq_dir);
    SAMPLE_COMM_ISP_Run(CamID);

    // RTSP Init
    g_rtsplive = create_rtsp_demo(554);
    g_rtsp_session = rtsp_new_session(g_rtsplive, "/live/0");
    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());

    // RKMPI Init
    RK_MPI_SYS_Init();
  2. VI、 VPSS、 VENC 组件初始化

    // VI Init
    vi_dev_init();
    vi_chn_init(0, width, height);

    // VPSS Init
    vpss_init(0, width, height);

    // VENC Init
    RK_CODEC_ID_E enCodecType = RK_VIDEO_ID_AVC;
    venc_init(0, width, height, enCodecType);
    • VI 通道的像素格式要与绑定的接收者一致

      vi_chn_attr.enPixelFormat = RK_FMT_YUV420SP;              // VI 通道属性

      stGrpVpssAttr.enPixelFormat = RK_FMT_YUV420SP; // VPSS 组属性

      stChnVencAttr.stVencAttr.enPixelFormat = RK_FMT_YUV420SP; // VENC 通道属性
    • 为了方便将数据传入 RKNN 模型中进行推理,使用 VPSS 组件将像素格式转换为 RGB888

      stVpssChnAttr.enPixelFormat = RK_FMT_RGB888;
    • VENC 设置进行 H264 编码的相关属性

      stAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
      stAttr.stRcAttr.stH264Cbr.u32BitRate = BitRate;
      stAttr.stRcAttr.stH264Cbr.u32Gop = GOP;
      • 注意: 例程中使用的 GOP 为 1 , 设置所有数据帧都为 I 帧,在 PC 端使用 VLC 软件设置缓存时间不能低于 500 ms,可以尝试修改进行优化。
  3. 通道绑定

    • VI 通道 0 与 VENC 通道绑定

      stSrcChn.enModId = RK_ID_VI;
      stSrcChn.s32DevId = 0;
      stSrcChn.s32ChnId = 0;

      stvencChn.enModId = RK_ID_VENC;
      stvencChn.s32DevId = 0;
      stvencChn.s32ChnId = 0;
      s32Ret = RK_MPI_SYS_Bind(&stSrcChn, &stvencChn);
    • VI 通道 1 与 VPSS 组绑定

      stSrcChn.enModId = RK_ID_VI;
      stSrcChn.s32DevId = 0;
      stSrcChn.s32ChnId = 1;

      stvpssChn.enModId = RK_ID_VPSS;
      stvpssChn.s32DevId = 0;
      stvpssChn.s32ChnId = 0;
      s32Ret = RK_MPI_SYS_Bind(&stSrcChn, &stvpssChn);
  4. RKNN 推理线程

    • 获取 VPSS 数据帧和对应内存缓冲块虚拟地址
      RK_MPI_VPSS_GetChnFrame(DevId, ChnId, &stVpssFrame, -1);
      void *data = RK_MPI_MB_Handle2VirAddr(stVpssFrame.stVFrame.pMbBlk);
    • opencv-mobile 读取 VPSS 数据帧图像后,将图像处理为模型推理的需求大小
      cv::Mat frame(height,width,CV_8UC3, data);          
      cv::Mat model_input;
      cv::resize(frame, model_input, cv::Size(model_width,model_height), 0, 0, cv::INTER_LINEAR);
      • 注意: 如果使用对原图像比例要求较高的特征值获取模型,不能简单对图像进行缩放,需要使用缩放并填充黑边的操作来处理图像。
    • 获取推理结果并将结果以 OSD 的形式显示在 VENC 码流上
      memcpy(rknn_app_ctx.input_mems[0]->virt_addr, model_input.data, 640*640*3);
      inference_rknn_model(&rknn_app_ctx, &od_results);
      • 注意: RGN 模块将 OSD 显示在码流上使用的是覆盖 ( overlay ) 的形式,对内存占用较高,例程中只实现了对一张人脸进行标注。
  5. 推流线程进行 rtsp 推流

    RK_MPI_VENC_GetStream(vencChn, &stFrame, -1);
    RK_MPI_MB_Handle2VirAddr(stFrame.pstPack->pMbBlk);
    rtsp_tx_video(g_rtsp_session, (uint8_t *)pData, stFrame.pstPack->u32Len,
    stFrame.pstPack->u64PTS);
    rtsp_do_event(g_rtsplive);
  6. 资源释放

    RK_MPI_SYS_UnBind(&stSrcChn0, &stvencChn);
    RK_MPI_SYS_UnBind(&stSrcChn1, &stvpssChn);

    RK_MPI_VI_DisableChn(DevId, ChnId0);
    RK_MPI_VI_DisableChn(DevId, ChnId1);
    RK_MPI_VI_DisableDev(DevId);

    RK_MPI_VPSS_StopGrp(GrpId);
    RK_MPI_VPSS_DestroyGrp(Grpid);

    RK_MPI_VENC_StopRecvFrame(ChnId);
    RK_MPI_VENC_DestroyChn(ChnId);
    // RKRTSP
    rtsp_del_demo(g_rtsplive);
    // RKIRQ
    SAMPLE_COMM_ISP_Stop(CamId);
    // RKMPI
    RK_MPI_SYS_Exit();
    // RKNN
    release_rknn_model(&rknn_app_ctx);

8 编译运行

8.1 编译(在PC上执行)

  1. 获取 git 仓库源码

    git clone https://github.com/LuckfoxTECH/luckfox_pico_rkmpi_example.git
  2. 设置环境变量

    export LUCKFOX_SDK_PATH=< luckfox-pico Sdk 地址>

    注意:使用绝对地址。

  3. 执行 ./build.sh 后选择编译的例程

    1) luckfox_pico_rtsp_opencv
    2) luckfox_pico_rtsp_opencv_capture
    3) luckfox_pico_rtsp_retinaface
    4) luckfox_pico_rtsp_retinaface_osd
    5) luckfox_pico_rtsp_yolov5
    Enter your choice [1-5]:

8.2 运行(在 Luckfox Pico 板端执行 )

  1. 编译完成后会在luckfox_pico_rkmpi_example/install文件夹下生成对应的部署文件夹

    luckfox_pico_rtsp_opencv_demo  
    luckfox_pico_rtsp_opencv_capture_demo
    luckfox_pico_rtsp_retinaface_demo
    luckfox_pico_rtsp_retinaface_osd_demo
    luckfox_pico_rtsp_yolov5_demo
  2. 将生成的部署文件夹完整上传到 Luckfox Pico 上 (可使用adb ssh等方式) ,板端进入文件夹运行

    # 在 Luckfox Pico 板端运行,<Demo Target> 是部署文件夹中的可执行程序
    chmod a+x <Demo Target>
    ./<Demo Target>
  3. 使用 VLC 打开网络串流 rtsp://172.32.0.93/live/0(按实际情况修改 IP 地址拉取图像)

注意

  • 在运行demo前请执行 RkLunch-stop.sh 关闭 Luckofox Pico 开机默认开启的后台程序 rkicp ,解除对摄像头的占用。
  • RV1103 的系统资源较少,无法正常运行时请降低视频捕获的分辨率。