rv1106抓h264流
从rkipc的rkipc_pipe_0_init我们得知,h265流的设置步骤为
设置vi通道-》使能vi通道-》创建venc通道-》设置h265通道的相关参数 -》接收帧 -》获取h265数据
根据这个步骤写rv1106抓h264流
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include <sys/stat.h>
#include <stdbool.h>
#include <sys/mman.h>
#include <rk_mpi_cal.h>
#include <rk_mpi_ivs.h>
#include <rk_mpi_mb.h>
#include <rk_mpi_mmz.h>
#include <rk_mpi_rgn.h>
#include <rk_mpi_sys.h>
#include <rk_mpi_tde.h>
#include <rk_mpi_venc.h>
#include <rk_mpi_vi.h>
#include <rk_mpi_vpss.h>
#include <rk_aiq_user_api2_acgc.h>
#include <rk_aiq_user_api2_camgroup.h>
#include <rk_aiq_user_api2_imgproc.h>
#include <rk_aiq_user_api2_sysctl.h>
#define JPEG_VENC_CHN 4
#define VIDEO_PIPE_0 0
#define MAX_AIQ_CTX 8
int main(int argc, char *argv[])
{
printf("hello world! \n");
for (int i = 0; i < argc; i++)
{
printf("%d argv: ", i);
printf("%s \n", argv[i]);
}
// 检查参数数量
// if (argc != 2)
// {
// perror("Usage: ./program <filename>");
// exit(1);
// }
int ret = -1;
char g_iq_file_dir_[256];
memcpy(g_iq_file_dir_, "/etc/iqfiles", strlen("/etc/iqfiles"));
printf("g_iq_file_dir_ is %s\n", g_iq_file_dir_);
char main_scene[32];
char sub_scene[32];
static int current_scenario_id = 0;
const char *scenario = "normal";
if (!strcmp(scenario, "normal"))
{
current_scenario_id = 0;
strcpy(sub_scene, "day");
}
else
{
current_scenario_id = 1;
strcpy(sub_scene, "night");
}
rk_aiq_working_mode_t g_WDRMode[MAX_AIQ_CTX];
rk_aiq_working_mode_t WDRMode = RK_AIQ_WORKING_MODE_NORMAL;
// must set HDR_MODE, before init
g_WDRMode[0] = WDRMode;
char hdr_str[16];
snprintf(hdr_str, sizeof(hdr_str), "%d", (int)WDRMode);
setenv("HDR_MODE", hdr_str, 1);
rk_aiq_sys_ctx_t *aiq_ctx;
rk_aiq_static_info_t aiq_static_info;
//通过物理id 获取sensor_name,后面初始化要用
rk_aiq_uapi2_sysctl_enumStaticMetasByPhyId(0, &aiq_static_info);
if (aiq_static_info.sensor_info.phyId == -1)
{
printf("WARN: aiq_static_info.sensor_info.phyId is %d\n",
aiq_static_info.sensor_info.phyId);
}
printf("ID: %d, sensor_name is %s, iqfiles is %s\n", 0,
aiq_static_info.sensor_info.sensor_name, "/etc/iqfiles");
//设置设备缓存数量,init前要完成
rk_aiq_uapi2_sysctl_preInit_devBufCnt(aiq_static_info.sensor_info.sensor_name, "rkraw_rx", 2);
//设置main_scene
if (WDRMode == RK_AIQ_WORKING_MODE_NORMAL)
strcpy(main_scene, "normal");
else
strcpy(main_scene, "hdr");
printf("main_scene is %s, sub_scene is %s\n", main_scene, sub_scene);
printf("rk_aiq_uapi2_sysctl_preInit_scene begin\n");
//init前的系统设置
ret = rk_aiq_uapi2_sysctl_preInit_scene(aiq_static_info.sensor_info.sensor_name, main_scene,
sub_scene);
if (ret < 0)
{
printf("%s: failed to set scene\n", aiq_static_info.sensor_info.sensor_name);
exit(1);
}
//初始化
aiq_ctx =
rk_aiq_uapi2_sysctl_init(aiq_static_info.sensor_info.sensor_name, "/etc/iqfiles", NULL, NULL);
printf("rk_aiq_uapi2_sysctl_init over\n");
if (!aiq_ctx)
{
printf("%s: failed to rk_aiq_uapi2_sysctl_init \n", __func__);
exit(1);
}
//准备好aiq环境
if (rk_aiq_uapi2_sysctl_prepare(aiq_ctx, 0, 0, WDRMode))
{
printf("rk_aiq_uapi2_sysctl_prepare failed !\n");
return -1;
}
printf("*************rk_aiq_uapi2_sysctl_prepare succeed\n");
//启动aiq
if (rk_aiq_uapi2_sysctl_start(aiq_ctx))
{
printf("rk_aiq_uapi2_sysctl_start failed\n");
return -1;
}
printf("*************rk_aiq_uapi2_sysctl_start succeed\n");
//aiq完成
//以下为rkmpi的操作
int dev_id_ = 0;
int pipe_id_ = 0;
VI_CHN_BUF_WRAP_S stViWrap;
//初始化rkmpi系统
printf("*************RK_MPI_SYS_Init \n");
if (RK_MPI_SYS_Init() != RK_SUCCESS)
{
RK_LOGE("rk mpi sys init fail!");
return -1;
}
//查看vi设备的状态,没开的话打开
VI_DEV_ATTR_S stDevAttr;
VI_DEV_BIND_PIPE_S stBindPipe;
memset(&stDevAttr, 0, sizeof(stDevAttr));
memset(&stBindPipe, 0, sizeof(stBindPipe));
// 0. get dev config status
//vi设备是否配置,没配置的话进行配置
printf("*************RK_MPI_VI_GetDevAttr \n");
ret = RK_MPI_VI_GetDevAttr(dev_id_, &stDevAttr);
if (ret == RK_ERR_VI_NOT_CONFIG)
{
// 0-1.config dev
printf("RK_MPI_VI_SetDevAttr not config\n");
ret = RK_MPI_VI_SetDevAttr(dev_id_, &stDevAttr);
if (ret != RK_SUCCESS)
{
printf("RK_MPI_VI_SetDevAttr %x\n", ret);
return -1;
}
else
{
printf("RK_MPI_VI_SetDevAttr set success\n");
}
}
else
{
printf("RK_MPI_VI_SetDevAttr already\n");
}
// 1.get dev enable status
//使能vi设备
printf("*************RK_MPI_VI_GetDevIsEnable \n");
ret = RK_MPI_VI_GetDevIsEnable(dev_id_);
if (ret != RK_SUCCESS)
{
// 1-2.enable dev
ret = RK_MPI_VI_EnableDev(dev_id_);
printf("enable RK_MPI_VI\n");
if (ret != RK_SUCCESS)
{
printf("RK_MPI_VI_EnableDev %x\n", ret);
return -1;
}
// 1-3.bind dev/pipe
stBindPipe.u32Num = pipe_id_;
stBindPipe.PipeId[0] = pipe_id_;
ret = RK_MPI_VI_SetDevBindPipe(dev_id_, &stBindPipe);
printf("set enable RK_MPI_VI\n");
if (ret != RK_SUCCESS)
{
printf("RK_MPI_VI_SetDevBindPipe %x\n", ret);
return -1;
}
printf("set enable RK_MPI_VI success\n");
}
else
{
printf("RK_MPI_VI_EnableDev already\n");
}
//vi通道参数设置
int max_width = 1920;
int max_height = 1080;
int width = 1920;
int height = 1080;
// 2.config channel
VI_CHN_ATTR_S vi_chn_attr;
memset(&vi_chn_attr, 0, sizeof(vi_chn_attr));
vi_chn_attr.stIspOpt.u32BufCount = 2;
vi_chn_attr.stIspOpt.enMemoryType = VI_V4L2_MEMORY_TYPE_DMABUF;
vi_chn_attr.stIspOpt.stMaxSize.u32Width = max_width;
vi_chn_attr.stIspOpt.stMaxSize.u32Height = max_height;
vi_chn_attr.stSize.u32Width = width;
vi_chn_attr.stSize.u32Height = height;
vi_chn_attr.enPixelFormat = RK_FMT_YUV420SP;
vi_chn_attr.u32Depth = 1;
vi_chn_attr.enCompressMode = COMPRESS_MODE_NONE;
printf("*************RK_MPI_VI_SetChnAttr \n");
//设置vi通道0
ret = RK_MPI_VI_SetChnAttr(pipe_id_, VIDEO_PIPE_0, &vi_chn_attr);
if (ret)
{
printf("ERROR: create VI error! ret=%d\n", ret);
exit(1);
}
// 3.enable channel
//使能vi通道0
printf("*************RK_MPI_VI_EnableChn \n");
ret = RK_MPI_VI_EnableChn(pipe_id_, VIDEO_PIPE_0);
if (ret)
{
printf("ERROR: create VI error! ret=%d\n", ret);
exit(1);
}
//h264通道设置
// 设置编码通道结构体的值
VENC_CHN_ATTR_S h264_chn_attr;
memset(&h264_chn_attr, 0, sizeof(h264_chn_attr));
h264_chn_attr.stVencAttr.enType = RK_VIDEO_ID_AVC;
h264_chn_attr.stVencAttr.u32Profile = 100;
h264_chn_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
h264_chn_attr.stRcAttr.stH264Cbr.u32Gop = 30;
h264_chn_attr.stRcAttr.stH264Cbr.u32BitRate = 2048;
h264_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1;
h264_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 25;
h264_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1;
h264_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 25;
h264_chn_attr.stGopAttr.enGopMode = VENC_GOPMODE_NORMALP;
h264_chn_attr.stVencAttr.enPixelFormat = RK_FMT_YUV420SP;
h264_chn_attr.stVencAttr.u32MaxPicWidth = width;
h264_chn_attr.stVencAttr.u32MaxPicHeight = height;
h264_chn_attr.stVencAttr.u32PicWidth = width;
h264_chn_attr.stVencAttr.u32PicHeight = height;
h264_chn_attr.stVencAttr.u32VirWidth = width;
h264_chn_attr.stVencAttr.u32VirHeight = height;
h264_chn_attr.stVencAttr.u32StreamBufCnt = 2;
h264_chn_attr.stVencAttr.u32BufSize = 1036800;
// 创建编码通道
printf("*************RK_MPI_VENC_CreateChn \n");
ret = RK_MPI_VENC_CreateChn(VIDEO_PIPE_0, &h264_chn_attr);
if (ret)
{
printf("ERROR: create VENC error! ret=%d\n", ret);
return -1;
}
//高级设置
ret = RK_MPI_VENC_EnableMotionDeblur(VIDEO_PIPE_0, true);
if (ret)
{
printf("RK_MPI_VENC_EnableMotionDeblur error! ret=%#x\n", ret);
}
VENC_RC_PARAM_S venc_rc_param;
RK_MPI_VENC_GetRcParam(VIDEO_PIPE_0, &venc_rc_param);
venc_rc_param.stParamH264.u32MinQp = 15;
venc_rc_param.stParamH264.u32FrmMinIQp = 26;
venc_rc_param.stParamH264.u32FrmMinQp = 28;
venc_rc_param.stParamH264.u32FrmMaxIQp = 51;
venc_rc_param.stParamH264.u32FrmMaxQp = 51;
RK_MPI_VENC_SetRcParam(VIDEO_PIPE_0, &venc_rc_param);
VENC_H264_TRANS_S pstH264Trans;
RK_MPI_VENC_GetH264Trans(VIDEO_PIPE_0, &pstH264Trans);
pstH264Trans.bScalingListValid = 0;
RK_MPI_VENC_SetH264Trans(VIDEO_PIPE_0, &pstH264Trans);
VENC_CHN_REF_BUF_SHARE_S stVencChnRefBufShare;
memset(&stVencChnRefBufShare, 0, sizeof(VENC_CHN_REF_BUF_SHARE_S));
stVencChnRefBufShare.bEnable = 1;
RK_MPI_VENC_SetChnRefBufShareAttr(VIDEO_PIPE_0, &stVencChnRefBufShare);
RK_MPI_VENC_SetChnRotation(VIDEO_PIPE_0, ROTATION_0);
VENC_RECV_PIC_PARAM_S stRecvParam;
memset(&stRecvParam, 0, sizeof(VENC_RECV_PIC_PARAM_S));
stRecvParam.s32RecvPicNum = -1;
RK_MPI_VENC_StartRecvFrame(VIDEO_PIPE_0, &stRecvParam);
// 绑定VI到VENC
// bind
MPP_CHN_S vi_chn, venc_chn;
vi_chn.enModId = RK_ID_VI;
vi_chn.s32DevId = 0;
vi_chn.s32ChnId = VIDEO_PIPE_0;
venc_chn.enModId = RK_ID_VENC;
venc_chn.s32DevId = 0;
venc_chn.s32ChnId = VIDEO_PIPE_0;
RK_MPI_SYS_Bind(&vi_chn, &venc_chn);
VENC_STREAM_S stFrame;
stFrame.pstPack = malloc(sizeof(VENC_PACK_S));
void *h264_data;
size_t h264_size;
int cap_count = 0;
char file_name[128] = {0};
time_t t ;
struct tm tm;
while (1)
{
//获取流的数据
if(RK_MPI_VENC_GetStream(VIDEO_PIPE_0, &stFrame, 1000) == RK_SUCCESS)
{
void *h264_data = RK_MPI_MB_Handle2VirAddr(stFrame.pstPack->pMbBlk);
h264_size = stFrame.pstPack->u32Len;
snprintf(file_name, 128, "/userdata/h264/h264%06d.h264", cap_count);
//打开文件
FILE *fp = fopen(file_name, "wb");
if (fp == NULL)
{
printf("fp is NULL\n");
}
else
{
//写入文件
fwrite(h264_data, 1, stFrame.pstPack->u32Len, fp);
}
//关闭文件
fflush(fp);
fclose(fp);
// printf("h264_size: %d \n", h264_size);
cap_count++;
//释放frame
ret = RK_MPI_VENC_ReleaseStream(VIDEO_PIPE_0, &stFrame);
if (ret != RK_SUCCESS) {
printf("RK_MPI_VENC_ReleaseStream fail %x\n", ret);
}
} else
{
printf("RK_MPI_VENC_GetStream error \n");
}
}
}
测试
在rv1106上运行程序,得到每帧的文件,用ffmpeg对i帧进行转码,例如我这里h264000060.h264为i帧
$ ffmpeg -i ./h264000060.h264 -f mp4 output.mp4
用浏览器打开这个mp4
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>HTML5 Video 自动循环播放示例</title>
</head>
<body>
<video id="myVideo" width="1920" height="1080" controls>
<source src="output.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
<script>
const video = document.getElementById('myVideo');
video.addEventListener('ended', function () {
video.currentTime = 0;
video.play();
});
</script>
</body>
</html>
发现能正常显示。