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 <assert.h>
#include <fcntl.h>
#include <getopt.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#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;
}