通过多线程分别获取高分辨率和低分辨率的H264码流

目录

[一.RV1126 VI采集摄像头数据并同时获取高分辨率码流和低分辨率码流流程](#一.RV1126 VI采集摄像头数据并同时获取高分辨率码流和低分辨率码流流程)

​编辑

1.1初始化VI模块:

1.2初始化RGA模块:

[1.3初始化高分辨率VENC编码器、 低分辨率VENC编码器:](#1.3初始化高分辨率VENC编码器、 低分辨率VENC编码器:)

[1.4 VI绑定高分辨率VENC编码器,VI绑定RGA模块,伪代码如下:](#1.4 VI绑定高分辨率VENC编码器,VI绑定RGA模块,伪代码如下:)

[1.5 创建多线程获取高分辨率编码数据:](#1.5 创建多线程获取高分辨率编码数据:)

[1.6 创建多线程获取低分辨率数据并传输到编码器:](#1.6 创建多线程获取低分辨率数据并传输到编码器:)

1.7.创建多线程获取低分辨率编码数据:

三.运行的效果:


一.RV1126 VI采集摄像头数据并同时获取高分辨率码流和低分辨率码流流程

RV1126利用多线程同时获取高分辨率编码文件、低分辨率编码文件,一般要分为上图8个步骤:VI模块初始化RGA图像模块初始化、高分辨率编码器的初始化、低分辨率编码器的初始化、VI绑定高分辨率VENC编码器、创建多线程获取高分辨率编码数据、创建多线程获取低分辨率数据并传输到编码器、创建多线程获取低分辨率编码数据。

1.1初始化VI模块:

VI模块的初始化实际上就是对VI_CHN_ATTR_S的参数进行设置、然后调用RK_MPI_VI_SetChnAttr 设置VI模块并使能****RK_MPI_VI_EnableChn,****伪代码如下:

VI_CHN_ATTR_S vi_chn_attr;

。。。。。。。。。。。。。。。( 这里是设置VI的属性****)****

ret = RK_MPI_VI_SetChnAttr(CAMERA_ID, 0, &vi_chn_attr);

ret |= RK_MPI_VI_EnableChn(CAMERA_ID, 0);

1.2初始化RGA模块:

RGA主要的作用是缩放VI模块的分辨率,比方说:VI模块的分辨率1920 * 1080,经过RGA缩放后分辨率为1280 * 720,并利用RK_MPI_RGA_CreateChn创建RGA模块,伪代码如下:

RGA_ATTR_S rga_attr;

rga_attr.stImgIn.u32Width = 1920;

rga_attr.stImgIn.u32Height = 1080;

。。。。。。。。。

rga_attr.stImgOut.u32Width = 1280;

rga_attr.stImgOut.u32Height = 720;

。。。。。。。。。。。

RK_MPI_RGA_CreateChn(RGA_CHN_ID, &rga_attr);

这段代码的核心是输入图像(输入分辨率是原分辨率和VI模块一致)和输出分辨率(输出分辨率是自己设置的分辨率)的设置。比方说输入的分辨率是 : 1920*1080,那stImgIn.u32Width = 1920 、stImgIn.u32Height = 1080,输出图像成:1280 * 720,stImgOut.u32Width = 1280、stImgOut.u32Height = 720。

1.3初始化高分辨率VENC编码器、 低分辨率VENC编码器:

高分辨率编码器的初始化

VENC_CHN_ATTR_S high_venc_chn_attr;

high_venc_chn_attr.stVencAttr.u32PicWidth = 1920;

high_venc_chn_attr.stVencAttr.u32PicHeight = 1080;

high_venc_chn_attr.stVencAttr.u32VirWidth = 1920;

high_venc_chn_attr.stVencAttr.u32VirHeight = 1080;

................................

RK_MPI_VENC_CreateChn( HIGH_ VENC_CHN, & high_venc_chn_attr );

低分辨率编码器的初始化

VENC_CHN_ATTR_S low_venc_chn_attr;

low_venc_chn_attr.stVencAttr.u32PicWidth = 1280;

low_venc_chn_attr.stVencAttr.u32PicHeight = 720;

low_venc_chn_attr.stVencAttr.u32VirWidth = 1280;

low_venc_chn_attr.stVencAttr.u32VirHeight = 720;

................................

RK_MPI_VENC_CreateChn( HIGH_ VENC_CHN, & low_venc_chn_attr );

高分辨率和低分辨率最核心的设置就是分辨率的设置,高分辨率u32PicWidth = 1920、u32PicHeight = 1920,低分辨率u32PicWidth = 1280、u32PicHeight = 720

1.4 VI绑定高分辨率VENC编码器,VI绑定RGA模块,伪代码如下:

//VI模块绑定RGA模块

MPP_CHN_S vi_chn_s;

vi_chn_s.enModId = RK_ID_VI;

vi_chn_s.s32ChnId = CAMERA_CHN;

MPP_CHN_S rga_chn_s;

rga_chn_s.enModId = RK_ID_RGA;

rga_chn_s.s32ChnId = RGA_CHN_ID;

RK_MPI_SYS_Bind(&vi_chn_s, &rga_chn_s);

//VI模块绑定高分辨率VENC模块

MPP_CHN_S high_venc_chn_s;

high_venc_chn_s_s.enModId = RK_ID_VENC;

high_venc_chn_s_s.s32ChnId = HIGH_VENC_ID;

RK_MPI_SYS_Bind(&vi_chn_s, &high_venc_chn_s);

1.5 创建多线程获取高分辨率编码数据:

开启一个线程去采集每一帧高分辨率的VENC模块数据,使用的API是****RK_MPI_SYS_GetMediaBuffer,****模块ID是RK_ID_VENC,通道号ID是高分辨率VENC创建的ID号:

while(1)

{

.........................

mb = RK_MPI_SYS_GetMediaBuffer (RK_ID_VENC, HIGH_VENC_ID, -1);

fwrite( RK_MPI_MB_GetPtr (mb), RK_MPI_MB_Get Size(mb), 1,high_venc_id );

.......................

}

1.6 创建多线程获取低分辨率数据并传输到编码器:

开启一个线程去采集每一帧RGA低分辨率的数据,使用的API是RK_MPI_SYS_GetMediaBuffer, 模块ID是RK_ID_RGA,通道号ID是RGA的通道ID,采集完每一帧RGA数据则使用RK_MPI_SYS_SendMediaBuffer 传输到低分辨率编码器****。**** 这个API伪代码如下

while (1)

{

.........................................

mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_RGA, 0, -1);

RK_MPI_SYS_SendMediaBuffer(RK_ID_VENC, LOW_VENC_ID , mb);

................................

}

下面我们来看看RK_MPI_SYS_SendMediaBuffer 的具体实现****:****

RK_S32 RK_MPI_SYS_SendMediaBuffer(MOD_ID_E enModID, RK_S32 s32ChnID, MEDIA_BUFFER buffer);

enModID: 开发者需要传输的目的模块的ID号,比方说VI模块的数据传输到VENC模块,那么目的模块就是VENC,ID号就是RK_ID_VENC;比方说VI模块的数据传输到RGA模块,那么目的模块就是RGA,ID号就是RK_ID_RGA。

****s32ChnID:****目的模块的通道号

****buffer:****缓冲区数据MediaBuffer

1.7.创建多线程获取低分辨率编码数据:

开启一个线程去采集每一帧低分辨率的编码数据,使用的API是RK_MPI_SYS_GetMediaBuffer, 模块ID是RK_ID_VENC,通道号ID是低分辨率VENC创建的ID号****。**** 这个API伪代码如下

while (1)

{

.........................................

mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, LOW_VENC_ID, -1);

fwrite( RK_MPI_MB_GetPtr (mb), RK_MPI_MB_Get Size(mb), 1, low_venc_file );

................................

}

二.代码实战

cpp 复制代码
#include <assert.h>
#include <fcntl.h>
#include <getopt.h>
#include <pthread.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

// #include "common/sample_common.h"
#include "rkmedia_api.h"

#define PIPE_ID 0
#define VI_CHN_ID 0

#define RGA_CHN_ID 0

#define HIGH_VENC_CHN 0
#define LOW_VENC_CHN 1

//创建多线程获取高分辨率的编码码流
void *get_high_venc_thread(void *args)
{
    pthread_detach(pthread_self());
    FILE *high_venc_file = fopen("test_high_venc.h264", "w+");
    MEDIA_BUFFER mb;

    while (1)
    {
        //获取每一帧高分辨率编码码流数据
        mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, HIGH_VENC_CHN, -1);
        if (!mb)
        {
            printf("Get High Venc break.....\n");
        }

        printf("Get High Venc Success.....\n");
        fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, high_venc_file);
        RK_MPI_MB_ReleaseBuffer(mb);
    }

    return NULL;
}

//创建多线程获取RGA码流并发送到低分辨率编码器
void *rga_handle_thread(void *args)
{
    pthread_detach(pthread_self());
    MEDIA_BUFFER mb;

    while (1)
    {
         //获取每一帧RGA码流数据
        mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_RGA, RGA_CHN_ID, -1);
        if (!mb)
        {
            printf("Get RGA break.....\n");
        }

        printf("Get RGA Success.....\n");
        //发送每一帧RGA数据到低分辨率编码器
        RK_MPI_SYS_SendMediaBuffer(RK_ID_VENC, LOW_VENC_CHN, mb);
        RK_MPI_MB_ReleaseBuffer(mb);
    }

    return NULL;
}

//创建多线程获取低分辨率编码码流
void *get_low_venc_thread(void *args)
{
    pthread_detach(pthread_self());
    MEDIA_BUFFER mb;
    FILE * low_venc_file = fopen("test_low_venc.h264", "w+");

    while (1)
    {
        //获取每一帧低分辨率编码码流
        mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, LOW_VENC_CHN, -1);
        if (!mb)
        {
            printf("Get LOW VENC break.....\n");
        }

        printf("Get LOW VENC Success.....\n");
        fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, low_venc_file);
        RK_MPI_MB_ReleaseBuffer(mb);
    }

    return NULL;
}

int main(int argc, char *argv[])
{

    int ret;
    RK_MPI_SYS_Init();

    // VI Init......
    VI_CHN_ATTR_S vi_chn_attr;
    vi_chn_attr.pcVideoNode = "rkispp_scale0";//设置视频设备节点路径
    vi_chn_attr.u32Width = 1920;//设置分辨率的宽度
    vi_chn_attr.u32Height = 1080;//设置分辨率的高度
    vi_chn_attr.enPixFmt = IMAGE_TYPE_NV12;//设置图像类型
    vi_chn_attr.enBufType = VI_CHN_BUF_TYPE_MMAP;//设置VI获取类型
    vi_chn_attr.u32BufCnt = 3;//设置缓冲数量
    vi_chn_attr.enWorkMode = VI_WORK_MODE_NORMAL;//设置VI工作类型
    ret = RK_MPI_VI_SetChnAttr(PIPE_ID, VI_CHN_ID, &vi_chn_attr);
    if (ret)
    {
        printf("VI_CHN_ATTR Set Failed.....\n");
        return -1;
    }
    else
    {
        printf("VI_CHN_ATTR Set Success.....\n");
    }

    ret = RK_MPI_VI_EnableChn(PIPE_ID, VI_CHN_ID);
    if (ret)
    {
        printf("VI_CHN_ATTR Enable Failed.....\n");
        return -1;
    }
    else
    {
        printf("VI_CHN_ATTR Enable Success.....\n");
    }

    // RGA
    RGA_ATTR_S rga_info;
    /**Image Input ..............*/
    rga_info.stImgIn.u32Width = 1920;//设置RGA输入分辨率宽度
    rga_info.stImgIn.u32Height = 1080;//设置RGA输入分辨率高度
    rga_info.stImgIn.u32HorStride = 1920;//设置RGA输入分辨率虚宽
    rga_info.stImgIn.u32VirStride = 1080;//设置RGA输入分辨率虚高
    rga_info.stImgIn.imgType = IMAGE_TYPE_NV12;//设置ImageType图像类型
    rga_info.stImgIn.u32X = 0;//设置X坐标
    rga_info.stImgIn.u32Y = 0;//设置Y坐标

    /**Image Output......................*/
    rga_info.stImgOut.u32Width = 1280;//设置RGA输出分辨率宽度
    rga_info.stImgOut.u32Height = 720;//设置RGA输出分辨率高度
    rga_info.stImgOut.u32HorStride = 1280;//设置RGA输出分辨率虚宽
    rga_info.stImgOut.u32VirStride = 720;//设置RGA输出分辨率虚高
    rga_info.stImgOut.imgType = IMAGE_TYPE_NV12;//设置输出ImageType图像类型
    rga_info.stImgOut.u32X = 0;//设置X坐标
    rga_info.stImgOut.u32Y = 0;//设置Y坐标

    // RGA Public Parameter
    rga_info.u16BufPoolCnt = 3;//缓冲池计数
    rga_info.u16Rotaion = 0;//
    rga_info.enFlip = RGA_FLIP_H;//水平翻转
    rga_info.bEnBufPool = RK_TRUE;
    ret = RK_MPI_RGA_CreateChn(RGA_CHN_ID, &rga_info);
    if (ret)
    {
        printf("RGA Set Failed.....\n");
    }
    else
    {
        printf("RGA Set Success.....\n");
    }

    // High Venc Parameter
    VENC_CHN_ATTR_S high_venc_attr;
    high_venc_attr.stVencAttr.enType = RK_CODEC_TYPE_H264;//设置编码器类型
    high_venc_attr.stVencAttr.imageType = IMAGE_TYPE_NV12;//设置编码图像类型
    high_venc_attr.stVencAttr.u32PicWidth = 1920;//设置编码分辨率宽度
    high_venc_attr.stVencAttr.u32PicHeight = 1080;//设置编码分辨率高度
    high_venc_attr.stVencAttr.u32VirWidth = 1920;//设置编码分辨率虚宽
    high_venc_attr.stVencAttr.u32VirHeight = 1080;//设置编码分辨率虚高
    high_venc_attr.stVencAttr.u32Profile = 66;//设置编码等级
    high_venc_attr.stVencAttr.enRotation = VENC_ROTATION_0;//设置编码的旋转角度

    //********* 设置码率控制属性  *******************//
    high_venc_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;//设置H264的CBR码率控制模式
    high_venc_attr.stRcAttr.stH264Cbr.u32Gop = 25;//设置GOP关键帧间隔
    high_venc_attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 25;//设置源帧率分子
    high_venc_attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1;//设置源帧率分母
    high_venc_attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 25;//设置目标帧率分子
    high_venc_attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1;//设置目标帧率分母
    high_venc_attr.stRcAttr.stH264Cbr.u32BitRate = 8388608;//设置码率大小
    ret = RK_MPI_VENC_CreateChn(HIGH_VENC_CHN, &high_venc_attr);
    if (ret)
    {
        printf("Set High Venc Attr Failed.....\n");
    }
    else
    {
        printf("Set High Venc Attr Success.....\n");
    }

    // Low Venc Parameter
    VENC_CHN_ATTR_S low_venc_attr;
    low_venc_attr.stVencAttr.enType = RK_CODEC_TYPE_H264;//设置编码器类型
    low_venc_attr.stVencAttr.imageType = IMAGE_TYPE_NV12;//设置编码图像类型
    low_venc_attr.stVencAttr.u32PicWidth = 1280;//设置编码分辨率宽度
    low_venc_attr.stVencAttr.u32PicHeight = 720;//设置编码分辨率高度
    low_venc_attr.stVencAttr.u32VirWidth = 1280;//设置编码分辨率虚宽
    low_venc_attr.stVencAttr.u32VirHeight = 720;//设置编码分辨率虚高
    low_venc_attr.stVencAttr.u32Profile = 66;//设置编码等级
    low_venc_attr.stVencAttr.enRotation = VENC_ROTATION_0;//设置编码的旋转角度

     //********* 设置码率控制属性  *******************//
    low_venc_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;//设置H264的CBR码率控制模式
    low_venc_attr.stRcAttr.stH264Cbr.u32Gop = 25;//设置GOP关键帧间隔
    low_venc_attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 25;//设置源帧率分子
    low_venc_attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1;//设置源帧率分母
    low_venc_attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 25;//设置目标帧率分子
    low_venc_attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1;//设置目标帧率分母
    low_venc_attr.stRcAttr.stH264Cbr.u32BitRate = 8388608;//设置码率大小
    ret = RK_MPI_VENC_CreateChn(LOW_VENC_CHN, &low_venc_attr);
    if (ret)
    {
        printf("Set Low Venc Attr Failed.....\n");
    }
    else
    {
        printf("Set Low Venc Attr Success.....\n");
    }

    MPP_CHN_S vi_chn_s;
    vi_chn_s.enModId = RK_ID_VI;
    vi_chn_s.s32ChnId = VI_CHN_ID;

    MPP_CHN_S high_chn_s;
    high_chn_s.enModId = RK_ID_VENC;
    high_chn_s.s32ChnId = HIGH_VENC_CHN;

    //VI绑定高分辨率VENC模块
    ret = RK_MPI_SYS_Bind(&vi_chn_s, &high_chn_s);
    if (ret)
    {
        printf("VI Bind High Venc Failed.....\n");
        return -1;
    }
    else
    {
        printf("VI Bind High Venc Success.....\n");
    }

    MPP_CHN_S rga_chn_s;
    rga_chn_s.enModId = RK_ID_RGA;
    rga_chn_s.s32ChnId = RGA_CHN_ID;

    //VI绑定RGA模块
    ret = RK_MPI_SYS_Bind(&vi_chn_s, &rga_chn_s);
    if (ret)
    {
        printf("VI Bind RGA Failed.....\n");
        return -1;
    }
    else
    {
        printf("VI Bind RGA Success.....\n");
    }

    pthread_t high_venc_pid;
    pthread_t rga_pid;
    pthread_t low_venc_pid;

    pthread_create(&high_venc_pid, NULL, get_high_venc_thread, NULL); //创建多线程获取高分辨率的编码码流 
    pthread_create(&rga_pid, NULL, rga_handle_thread, NULL);//创建多线程获取RGA码流并发送到低分辨率编码器
    pthread_create(&low_venc_pid, NULL, get_low_venc_thread, NULL);//创建多线程获取低分辨率编码码流

    while (1)
    {
        sleep(1);
    }

    RK_MPI_SYS_UnBind(&vi_chn_s, &high_chn_s);
    RK_MPI_SYS_UnBind(&vi_chn_s, &rga_chn_s);
    RK_MPI_RGA_DestroyChn(RGA_CHN_ID);
    RK_MPI_VENC_DestroyChn(HIGH_VENC_CHN);
    RK_MPI_VENC_DestroyChn(LOW_VENC_CHN);
    RK_MPI_VI_DisableChn(PIPE_ID, VI_CHN_ID);

    return 0;
}

三.运行的效果:

分别用ffplay播放器分别播出高分辨率H264文件(test_high_venc.h264),低分辨率(test_rga_venc.h264)

ffplay test_high_venc.h264

ffplay test_rga_venc.h264

相关推荐
彬sir哥11 分钟前
水仙花数(华为OD)
java·c语言·javascript·c++·python·算法
clownAdam15 分钟前
通俗易懂的分类算法之K近邻详解
人工智能·算法·分类·数据挖掘·knn·k邻近
江沉晚呤时16 分钟前
深入解析 .NET Core 的应用启动流程
linux·c#·asp.net·.netcore
别NULL25 分钟前
机试题——通讯录合并
c++·算法
SimonRiley_26 分钟前
解决 Dell PowerEdge T630 增加第三方 PCIe 设备后制冷系统异常
linux·服务器
啊迷诺斯30 分钟前
虚拟机ip设置
linux·运维·服务器
大G哥43 分钟前
jenkins集成docker发布java项目
java·运维·开发语言·docker·jenkins
Chandler241 小时前
LeetCode 热题 100 链表章节
算法·leetcode·链表
Hamm1 小时前
巧妙使用位运算来解决真实开发中的权限控制场景
java·后端·算法
Biomamba生信基地1 小时前
Biomamba劝学
linux·服务器·生信