目录
一、名词解释
mmap --- 内存映射
一般用于无法直接操作的物理内存
就将物理内存映射为一个虚拟内存
操作虚拟内存就相当于操作物理内存
CBR --- 固定码率 --- 最简单
CBR 控制模式一般用于网络流媒体视频编码。CBR 的优点在于压缩速度非常快,并且码率很平稳不会出现码率跳变的情况
VBR --- 可变码率
VBR 的优点是它的尽可能保证整个图像编码质量,利用 VBR 编码的图像很少会出现马赛克、画面丢失的情况。但缺点同样也很明显,使用 VBR 编码出来的图像它的体积是不固定的。
AVBR --- 混合码率
AVBR 全称叫适配式可变码率控制方式,它的很多思想跟 VBR 基本上是一致的。但它比 VBR 强大的一点是这种控制方式能够自动检测当前编码场景是静止画面还是运动画面。若当前画面是静止画面则会主动降低码率、若当前画面是运动画面则会把码率主动提升。AVBR 适用于当前编码视频静止画面和运动画面频繁出现的场景,所以 AVBR 码率控制方式经常用在大型体育比赛上面。
简单点理解 CBR --- I 帧间隔固定
二、程序编译&执行
修改 SDK 路径
目前的目标:根据官方的例程,去完善自己的程序
//用来设置摄像头参数的 --- 必要的
rk_aiq_working_mode_t hdr_mode = RK_AIQ_WORKING_MODE_NORMAL;
int fps = 30;
SAMPLE_COMM_ISP_Init(s32CamId, hdr_mode, bMultictx, pIqfilesPath);
SAMPLE_COMM_ISP_Run(s32CamId);
SAMPLE_COMM_ISP_SetFrameRate(s32CamId, fps);
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();
RK_MPI_VI_SetChnAttr(s32CamId, 0, &vi_chn_attr);
RK_MPI_VI_EnableChn(s32CamId, 0);
RK_MPI_VENC_CreateChn(0, &venc_chn_attr);
RK_MPI_SYS_RegisterOutCb(&stEncChn, video_packet_cb);
RK_MPI_SYS_Bind(&stSrcChn, &stDestChn);
rtsp_del_demo(g_rtsplive);
RK_MPI_SYS_UnBind(&stSrcChn, &stDestChn);
RK_MPI_VENC_DestroyChn(0);
RK_MPI_VI_DisableChn(s32CamId, 0);
SAMPLE_COMM_ISP_Stop(s32CamId);
代码
#include "main.h"
#include <time.h>
//0.变量定义
RK_S32 s32CamId = 0; //摄像头ID号
RK_BOOL bMultictx = RK_FALSE;
RK_CHAR *pIqfilesPath = "/oem/etc/iqfiles"; //ISP配置文件
rtsp_demo_handle g_rtsplive = NULL; //rtsp服务器的核心指针
rtsp_session_handle g_rtsp_session; //rtsp服务器配置
RK_U32 Width = 1280;
RK_U32 Height = 720;
int end_flag;
int time_count = 30 * 30;//30s的帧数,1s30帧
FILE *g_output_file;
int g_filesave; // 添加标志位,确保只保存一次文件
char filename[100] = {0};
// 输出回调函数
void myoutcbfunc(MEDIA_BUFFER mb)
{
if(time_count)
{
printf("time_count = %d\n", time_count);
time_count--;
}
else
{
RK_MPI_MB_ReleaseBuffer(mb);//释放
end_flag = 1;
return;
}
/*
回调函数中 --- 最核心的就是
获取数据内容、获取数据大小
*/
//1.一个是推流
RK_MPI_MB_GetPtr(mb);
RK_MPI_MB_GetSize(mb);
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);
//2.本地保存
fwrite(RK_MPI_MB_GetPtr(mb), 1, RK_MPI_MB_GetSize(mb), g_output_file);
RK_MPI_MB_ReleaseBuffer(mb);//释放
}
int main(void)
{
// time_t tm = time(0);
// struct tm *lm = localtime(&tm);
// sprintf(filename,"%04d%02d%02d%02d%02d%02d.h264",lm->tm_year+1900,lm->tm_mon+1,lm->tm_mday,lm->tm_hour,lm->tm_min,lm->tm_sec);
g_output_file = fopen("./9203.h264", "w");
/*1.ISP的初始化 --- 必要内容
没有参考文档,只能复制例程
*/
rk_aiq_working_mode_t hdr_mode = RK_AIQ_WORKING_MODE_NORMAL;
int fps = 30;
SAMPLE_COMM_ISP_Init(s32CamId, hdr_mode, bMultictx, pIqfilesPath);
SAMPLE_COMM_ISP_Run(s32CamId);
SAMPLE_COMM_ISP_SetFrameRate(s32CamId, fps);
/*2.rtsp的初始化 --- 可有可无
没有参考文档,只能复制例程
*/
g_rtsplive = create_rtsp_demo(554); //推流的端口号
//rtsp://192.168.66.88/live/main_stream --- 原本的
//rtsp://192.168.66.88/xyd/9203 --- 可改
g_rtsp_session = rtsp_new_session(g_rtsplive, "/live/main_stream"); //推流的路径 --- 可以自己修改
/*
重要内容 --- rtsp_set_video --- RTSP_CODEC_ID_VIDEO_H264
如果是音频需要换为 rtsp_set_audio --- RTSP_CODEC_ID_AUDIO_XXXX
*/
rtsp_set_video(g_rtsp_session, RTSP_CODEC_ID_VIDEO_H264, NULL, 0);
/*
重要内容 --- rtsp_sync_video_ts
如果是音频需要换为 rtsp_sync_audio_ts
*/
rtsp_sync_video_ts(g_rtsp_session, rtsp_get_reltime(), rtsp_get_ntptime());
/*3.RkMedia相关配置 --- 必要内容
有相关API文档进行说明
*/
RK_MPI_SYS_Init();
//对于VI视频输入来说,管道号和通道号 填0即可
VI_CHN_ATTR_S vi_pstChnAttr = {0};
vi_pstChnAttr.enBufType = VI_CHN_BUF_TYPE_MMAP;
vi_pstChnAttr.enPixFmt = IMAGE_TYPE_NV12;//输入视频的原始格式 --- 这里如果使用YUV色彩格式有问题 --- 最中使用的是NV12原因是例程使用的NV12
vi_pstChnAttr.enWorkMode = VI_WORK_MODE_NORMAL;//VI通道工作模式
vi_pstChnAttr.pcVideoNode = "rkispp_scale0";//节点路径
vi_pstChnAttr.u32BufCnt = 3;//v4l2摄像头开发方式定义的缓冲区个数3~5
vi_pstChnAttr.u32Height = Height;//高
vi_pstChnAttr.u32Width = Width;//宽
RK_MPI_VI_SetChnAttr(0, 0, &vi_pstChnAttr);
RK_MPI_VI_EnableChn(0, 0);
/*
重要 --- venc通道,最终的整合需要2个编码器通道
*/
VENC_CHN_ATTR_S venc_stVencChnAttr = {0};
venc_stVencChnAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;//I帧间隔固定,比如固定间隔30帧
venc_stVencChnAttr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1;
venc_stVencChnAttr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 30;
venc_stVencChnAttr.stRcAttr.stH264Cbr.u32BitRate = Width * Height;
venc_stVencChnAttr.stRcAttr.stH264Cbr.u32Gop = 30;//I帧间隔
venc_stVencChnAttr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1;//分母
venc_stVencChnAttr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 30;//分子
//编码器属性
venc_stVencChnAttr.stVencAttr.enType = RK_CODEC_TYPE_H264;
venc_stVencChnAttr.stVencAttr.imageType = IMAGE_TYPE_NV12;//输入图像格式
venc_stVencChnAttr.stVencAttr.u32PicHeight = Height;
venc_stVencChnAttr.stVencAttr.u32PicWidth = Width;
//H264编码可以达到300倍以上
venc_stVencChnAttr.stVencAttr.u32Profile = 77;//编码等级
venc_stVencChnAttr.stVencAttr.u32VirHeight = Height;
venc_stVencChnAttr.stVencAttr.u32VirWidth = Width;
RK_MPI_VENC_CreateChn(0, &venc_stVencChnAttr);
/*
当前绑定的是VI0和VENC0通道
后续绑定的是VI0和VENC1通道
*/
MPP_CHN_S v_pstSrcChn = {0};
MPP_CHN_S v_pstDestChn = {0};
v_pstSrcChn.enModId = RK_ID_VI;//视频输入通道
v_pstSrcChn.s32ChnId = 0;//VI通道
v_pstSrcChn.s32DevId = 0;
v_pstDestChn.enModId = RK_ID_VENC;//视频编码通道
v_pstDestChn.s32ChnId = 0;//VENC通道
v_pstDestChn.s32DevId = 0;
RK_MPI_SYS_Bind(&v_pstSrcChn, &v_pstDestChn);//建立VI和VENC的联系
RK_MPI_SYS_RegisterOutCb(&v_pstDestChn, myoutcbfunc);
while(!end_flag)
{
sleep(1);
}
/*3.RkMedia相关释放
有相关API文档进行说明
*/
rtsp_del_demo(g_rtsplive);
RK_MPI_SYS_UnBind(&v_pstSrcChn, &v_pstDestChn);
RK_MPI_VENC_DestroyChn(0); //后续这个VENC要改为2个通道
RK_MPI_VI_DisableChn(s32CamId, 0);
SAMPLE_COMM_ISP_Stop(s32CamId);
fclose(g_output_file); // 关闭文件
return 0;
}