rkmedia的视频采集、编码、推流与保存以及相关API和例程解析

rkmedia的视频采集、编码、推流与保存以及相关API和例程解析

目录

rkmedia简介

rkmedia视频采集、编码、推流相关函数简介

RK_MPI_SYS_Init()

RK_MPI_VI_SetChnAttr()

RK_MPI_VI_EnableChn()

RK_MPI_VENC_CreateChn()

RK_MPI_SYS_RegisterOutCb()

RK_MPI_SYS_Bind()

RK_MPI_SYS_UnBind()

RK_MPI_VENC_DestroyChn()

RK_MPI_VI_DisableChn()

示例代码与流程

代码编写流程

源码展示

rkmedia简介

RKMedia是由瑞芯微提供的专门用于音视频处理的系统。它提供了丰富的功能,包括音视频采集、编解码、加密解密等操作。在音视频传输中,编解码是必不可少的环节,它可以减小文件大小,节省带宽。RKMedia支持多种视频编码方式,如H264,并且在H264编码中,将视频数据帧分为关键帧(I帧)、单向参考帧(P帧)和双向参考帧(B帧)进行压缩。压缩方式可以是软压缩(自己编写算法运行在CPU上)或硬压缩(使用CPU内部的专用编码器)。解压方式也可以是软解压或硬解压。RKMedia主要使用硬编码和硬解码的方式来处理音视频数据。

RKMedia的作用包括采集音频数据(PCM格式)、对音频进行编码(AAC格式)、采集视频数据(YUV或NV12格式)、对视频进行编码(YUV转H264格式)等。此外,RKMedia还可以进行视频裁剪、检测摄像头是否有遮挡、视频合成、视频分解、视频录制等操作。在RKMedia中,还有一些专用的名词,如VI(视频采集通道)、VENC(视频编码)、VDEC(视频解码)、AI(音频采集)、AO(音频输出)、AENC(音频编码)、ADEC(音频解码)和MD(移动侦测)。通过RKMedia,开发者可以方便地进行音视频处理和应用开发。

rkmedia视频采集、编码、推流相关函数简介

RK_MPI_SYS_Init()

功能

用于初始化RKMedia系统,必须在使用其他RKMedia函数之前调用

头文件

rkmedia_api.h

库文件

libeasymedia.so

原型

RK_S32 RK_MPI_SYS_Init(void);

返回值

成功 0

失败 错误码

RK_MPI_VI_SetChnAttr()

功能

用于设置VI(视频采集)通道的属性,包括分辨率、帧率、像素格式等

头文件

rkmedia_api.h

库文件

libeasymedia.so

原型

RK_MPI_VI_SetChnAttr(VI_PIPE ViPipe, VI_CHN ViChn, const VI_CHN_ATTR_S *pstChnAttr);

参数

ViPipe VI 管道号。 一般写0

ViChn VI 通道号。取值范围:[0, VI_MAX_CHN_NUM)] 一般写0

pstChnAttr VI 通道属性结构体指针

返回值

成功 0

失败 错误码

typedef struct rkVI_CHN_ATTR_S

{

const RK_CHAR *pcVideoNode; 摄像头的节点

RK_U32 u32Width;

RK_U32 u32Height; 视频的高

IMAGE_TYPE_E enPixFmt; 采集的视频的格式

RK_U32 u32BufCnt; // VI capture video buffer cnt.缓冲帧的大小 3-5

// VI capture video buffer type.

VI_CHN_BUF_TYPE enBufType; 视频数据的存储方式 内存映射

VI_CHN_WORK_MODE enWorkMode; 工作模式 常规

} VI_CHN_ATTR_S;

RK_MPI_VI_EnableChn()

功能

用于使能VI通道,启动视频采集

头文件

rkmedia_api.h

库文件:

libeasymedia.so

原型

RK_S32 RK_MPI_VI_EnableChn(VI_PIPE ViPipe, VI_CHN ViChn);

参数

ViPipe VI 管道号

ViChn VI 通道号。取值范围:[0, VI_MAX_CHN_NUM

返回值

成功 0

失败 错误码

RK_MPI_VENC_CreateChn()

功能

用于创建编码通道,配置编码器的参数,如编码格式、码率、GOP大小等

头文件

rkmedia_api.h

库文件

libeasymedia.so

原型

RK_S32 RK_MPI_VENC_CreateChn(VENC_CHN VeChn, VENC_CHN_ATTR_S *stVencChnAttr);

参数

VeChn 编码通道号

stVencChnAttr 编码通道属性指针

返回值

成功 0

失败 错误码

编码属性结构体

typedef struct rkVENC_CHN_ATTR_S {

VENC_ATTR_S stVencAttr; // 编码器的属性

VENC_RC_ATTR_S stRcAttr; // 码率的控制属性

VENC_GOP_ATTR_S stGopAttr; // gop属性

} VENC_CHN_ATTR_S;

编码器的属性结构体

typedef struct rkVENC_ATTR_S {

CODEC_TYPE_E enType; // 编码的协议类型 RK_CODEC_TYPE_H264

IMAGE_TYPE_E imageType; // 输入的图像的类型 与vi通道保持一致 IMAGE_TYPE_NV12,

RK_U32 u32VirWidth; // 虚拟的宽

// width, often set vir_width=(width+15)&(~15)

RK_U32 u32VirHeight; // 虚拟的高

// than height, often set vir_height=(height+15)&

(~15)

RK_U32 u32Profile; // 编码的等级 固定为77

// H.264: 66: baseline; 77:MP; 100:HP;

// H.265: default:Main;

// Jpege/MJpege: default:Baseline

RK_BOOL bByFrame; // 保留参数

RK_U32 u32PicWidth; // 真实的宽

RK_U32 u32PicHeight; // 真实的高

VENC_ROTATION_E enRotation; //旋转的角度

union {

VENC_ATTR_H264_S stAttrH264e; // attributes of H264e

VENC_ATTR_H265_S stAttrH265e; // attributes of H265e

VENC_ATTR_MJPEG_S stAttrMjpege; // attributes of Mjpeg

VENC_ATTR_JPEG_S stAttrJpege; // attributes of jpeg

};

} VENC_ATTR_S;

码率控制器的属性结构体

typedef struct rkVENC_RC_ATTR_S {

/* RW; the type of rc*/

VENC_RC_MODE_E enRcMode;//工作模式 VENC_RC_MODE_H264CBR

union {

VENC_H264_CBR_S stH264Cbr;

VENC_H264_VBR_S stH264Vbr;

VENC_H264_AVBR_S stH264Avbr;

VENC_MJPEG_CBR_S stMjpegCbr;

VENC_MJPEG_VBR_S stMjpegVbr;

VENC_H265_CBR_S stH265Cbr;

VENC_H265_VBR_S stH265Vbr;

VENC_H265_AVBR_S stH265Avbr;

};

} VENC_RC_ATTR_S;

typedef struct rkVENC_H264_CBR_S {

RK_U32 u32Gop; 30

RK_U32 u32SrcFrameRateNum; 30

RK_U32 u32SrcFrameRateDen; 1

RK_FR32 fr32DstFrameRateNum; 30

RK_FR32 fr32DstFrameRateDen; 1

RK_U32 u32BitRate; // RW; Range:[2000, 98000000]; average bitrate分辨率1920*1080

} VENC_H264_CBR_S;

GOP 属性结构体。该结构体可不初始化

typedef struct rkVENC_GOP_ATTR_S {

VENC_GOP_MODE_E enGopMode; //模式

RK_U32 u32GopSize; //一个gop组多少帧

RK_S32 s32IPQpDelta; //

RK_U32 u32BgInterval;

RK_S32 s32ViQpDelta;

} VENC_GOP_ATTR_S;

RK_MPI_SYS_RegisterOutCb()

功能

用于注册VENC(视频编码)通道的回调函数,当编码完成后,会通过回调函数返回编码后的数据

头文件

rkmedia_api.h

库文件

libeasymedia.so

原型

RK_S32 RK_MPI_SYS_RegisterOutCb(const MPP_CHN_S *pstChn, OutCbFunc cb);

参数

pstChn 通道的描述结构体

cb 回调函数

返回值

成功 0

失败 非零

通道描述结构体

typedef struct rkMPP_CHN_S {

MOD_ID_E enModId; 模块号 RK_ID_VENC

RK_S32 s32DevId; 设备号 0

RK_S32 s32ChnId; 通道号 0

} MPP_CHN_S;

回调函数

typedef void (*OutCbFunc)(MEDIA_BUFFER mb);

RK_MPI_SYS_Bind()

功能

用于将VI通道和VENC通道进行绑定,建立数据流传输的连接

头文件

rkmedia_api.h

库文件

libeasymedia.so

原型

RK_S32 RK_MPI_SYS_Bind(const MPP_CHN_S *pstSrcChn,const MPP_CHN_S *pstDestChn);

参数

pstSrcChn 源通道指针 vi通道

pstDestChn 目的通道指针 venc通道

返回值

成功 0

失败 错误码

typedef struct rkMPP_CHN_S {

MOD_ID_E enModId; 模块号

RK_S32 s32DevId; 设备号

RK_S32 s32ChnId; 通道号

} MPP_CHN_S;

RK_MPI_SYS_UnBind()

功能

用于解除VI通道和VENC通道的绑定,断开数据流传输的连接

头文件

rkmedia_api.h

库文件

libeasymedia.so

原型

RK_MPI_SYS_UnBind(const MPP_CHN_S *pstSrcChn,const MPP_CHN_S *pstDestChn)

参数

pstSrcChn 源通道指针。

pstDestChn 目的通道指针。

返回值

成功 0

失败 非零

RK_MPI_VENC_DestroyChn()

功能

用于销毁VENC通道,释放相关资源

头文件

rkmedia_api.h

库文件

libeasymedia.so

原型

RK_S32 RK_MPI_VENC_DestroyChn(VENC_CHN VeChn);

参数

VeChn 编码通道号。取值范围:[0, VENC_MAX_CHN_NUM)]

返回值

成功 0

失败 错误码

RK_MPI_VI_DisableChn()

功能

用于失能VI通道,停止视频采集

头文件

rkmedia_api.h

库文件

libeasymedia.so

原型

RK_S32 RK_MPI_VI_DisableChn(VI_PIPE ViPipe, VI_CHN ViChn);

参数

ViPipe VI 管道号

ViChn VI 通道号。取值范围:[0, VI_MAX_CHN_NUM)]

返回值

成功 0

失败 错误码

示例代码与流程

代码编写流程

初始化RKMedia系统和推流服务器。设置摄像头属性,包括分辨率、帧率等。创建VI(视频采集)通道,并使能该通道。创建VENC(视频编码)通道,配置编码器的参数。注册VENC通道的回调函数,用于处理编码后的视频帧数据。绑定VI通道和VENC通道,建立数据流传输的连接。循环采集视频帧数据,并进行推流和保存到文件的操作。解除VI通道和VENC通道的绑定。销毁VENC通道。失能VI通道。关闭文件和释放相关资源。

源码展示

该代码的主要功能是通过RKMedia实现摄像头视频采集、编码、推流并将视频保存到本地文件

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include "common/sample_common.h"

#include "librtsp/rtsp_demo.h"

#include "rkmedia_api.h"

#include "rkmedia_venc.h"

FILE *fp = NULL;

RK_BOOL bMultictx = RK_FALSE;

RK_CHAR *pIqfilesPath = "/oem/etc/iqfiles"; //配置文件

int quit = 0;

rtsp_demo_handle g_rtsplive = NULL;

static rtsp_session_handle g_rtsp_session;

void video_packet_cb(MEDIA_BUFFER mb) {

static RK_S32 packet_cnt = 0;

if (quit)

return;

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, 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++;

}

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

argc = argc;

argv = argv;

int ret = 0;

RK_U32 u32Width = 1920; //视频采集的宽度 像素点

RK_U32 u32Height = 1080; //视频采集的高度 像素点

//设置摄像头属性

RK_CHAR *pDeviceName = "rkispp_scale0"; //摄像头

rk_aiq_working_mode_t hdr_mode = RK_AIQ_WORKING_MODE_NORMAL;

int fps = 30; //每秒采集到视频帧的个数

SAMPLE_COMM_ISP_Init(0, hdr_mode, bMultictx, pIqfilesPath);

SAMPLE_COMM_ISP_Run(0);

SAMPLE_COMM_ISP_SetFrameRate(0, fps);

fp = fopen("test.h264", "w");

if(fp == NULL){

printf("打开录像文件失败\n");

return -1;

}

//初始化推流服务器

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(); //初始化remedia的系统

//设置vi通道的属性

VI_CHN_ATTR_S pstChnAttr;

pstChnAttr.pcVideoNode = pDeviceName; //摄像头的节点

pstChnAttr.u32Width = u32Width; //视频的宽

pstChnAttr.u32Height = u32Height; //视频的高

pstChnAttr.enPixFmt = IMAGE_TYPE_NV12; //采集的视频格式

pstChnAttr.u32BufCnt = 3; //缓冲帧的大小

pstChnAttr.enBufType = VI_CHN_BUF_TYPE_MMAP; //内存映射

pstChnAttr.enWorkMode = VI_WORK_MODE_NORMAL; //工作模式,正常

ret = RK_MPI_VI_SetChnAttr(0, 0, &pstChnAttr);

if(ret != 0){

printf("设置vi通道的属性错误\n");

return -1;

}

//使能vi通道

ret = RK_MPI_VI_EnableChn(0, 0);

if(ret != 0){

printf("使能vi通道失败\n");

return -1;

}

//创建编码通道

VENC_CHN_ATTR_S stVencChnAttr;

stVencChnAttr.stVencAttr.enType = RK_CODEC_TYPE_H264;//编码协议的类型

stVencChnAttr.stVencAttr.imageType = IMAGE_TYPE_NV12;//采集的图像的类型

stVencChnAttr.stVencAttr.u32VirWidth = u32Width;//虚拟的宽

stVencChnAttr.stVencAttr.u32VirHeight = u32Height;//虚拟的高

stVencChnAttr.stVencAttr.u32Profile = 77;//编码的等级

//stVencChnAttr.stVencAttr.bByFrame =

stVencChnAttr.stVencAttr.u32PicWidth = u32Width;//真实的宽

stVencChnAttr.stVencAttr.u32PicHeight = u32Height;//真实的高

stVencChnAttr.stVencAttr.enRotation = 0;//旋转角度

stVencChnAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;//工作模式

stVencChnAttr.stRcAttr.stH264Cbr.u32Gop = 30;//gop组帧数

stVencChnAttr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 30;//帧率

stVencChnAttr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1;

stVencChnAttr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 30;

stVencChnAttr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1;

stVencChnAttr.stRcAttr.stH264Cbr.u32BitRate = u32Width * u32Height;//分辨率

ret = RK_MPI_VENC_CreateChn(0, &stVencChnAttr);

if(ret != 0){

printf("创建编码通道失败\n");

return -1;

}

//注册编码通道的回调函数

MPP_CHN_S pstChn;

pstChn.enModId = RK_ID_VENC;//模块号

pstChn.s32DevId = 0;//设备号 第一个摄像头

pstChn.s32ChnId = 0;//通道号 第一个通道

ret = RK_MPI_SYS_RegisterOutCb(&pstChn, video_packet_cb);

if(ret != 0){

printf("注册编码通道的回调函数失败\n");

return -1;

}

//绑定venc通道

MPP_CHN_S pstSrcChn, pstDestChn;

pstSrcChn.enModId = RK_ID_VI;

pstSrcChn.s32DevId = 0;

pstSrcChn.s32ChnId = 0;

pstDestChn.enModId = RK_ID_VENC;

pstDestChn.s32DevId = 0;

pstDestChn.s32ChnId = 0;

ret = RK_MPI_SYS_Bind(&pstSrcChn,&pstDestChn);

if(ret != 0){

printf("绑定venc通道失败\n");

return -1;

}

while(1){

usleep(60);

break;

}

quit = 1;

fclose(fp);

//解除通道绑定

ret = RK_MPI_SYS_UnBind(&pstSrcChn,&pstDestChn);

if(ret != 0){

printf("解除通道绑定失败\n");

return -1;

}

//销毁编码通道

ret = RK_MPI_VENC_DestroyChn(0);

if(ret != 0){

printf("销毁编码通道失败\n");

return -1;

}

//失能vi通道

ret = RK_MPI_VI_DisableChn(0, 0);

if(ret != 0){

printf("失能vi通道失败\n");

return -1;

}

return 0;

}

相关文章