av_register_all();
avformat_alloc_output_context2():初始化输出文件。
avio_open():打开输出文件。
avformat_write_header():写入文件头。
avformat_new_stream() 向媒体文件添加新流。
av_interleaved_write_frame():写入一个AVPacket到输出文件。
av_write_trailer():写入文件尾。
参考雷神的 最简单的基于FFmpeg的封装格式处理
和
ffmpeg h264文件和裸流 封装mp4
实现代码如下:
#include "libavformat/avformat.h"
static AVFormatContext* g_OutFmt_Ctx;
static int vi = -1;
static int STREAM_FRAME_RATE = 30;
static HI_BOOL b_First_IDR_Find = HI_FALSE;
static int ptsInc = 0;
/* Add an output stream */
static int HI_PDT_Add_Stream(AVFormatContext *poutFmtCtx)
{
AVOutputFormat *pOutFmt = NULL;
AVCodecContext *PAVCodecCtx = NULL;
AVStream *pAVStream = NULL;
AVCodec *pcodec = NULL;
pOutFmt = poutFmtCtx->oformat;
/* find the encoder */
pcodec = avcodec_find_encoder(pOutFmt->video_codec);
if (NULL == pcodec)
{
printf("could not find encoder for '%s' \n", avcodec_get_name(pOutFmt->video_codec));
return -1;
}
pAVStream = avformat_new_stream(poutFmtCtx, pcodec);
if (NULL == pAVStream)
{
printf("could not allocate stream \n");
return -1;
}
pAVStream->id = poutFmtCtx->nb_streams-1;
PAVCodecCtx = pAVStream->codec;
vi = pAVStream->index;
switch ((pcodec)->type)
{
case AVMEDIA_TYPE_AUDIO:
printf("AVMEDIA_TYPE_AUDIO\n");
PAVCodecCtx->sample_fmt = (pcodec)->sample_fmts ? (pcodec)->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;
PAVCodecCtx->bit_rate = 64000;
PAVCodecCtx->sample_rate = 44100;
PAVCodecCtx->channels = 2;
break;
case AVMEDIA_TYPE_VIDEO:
printf("AVMEDIA_TYPE_VIDEO\n");
PAVCodecCtx->codec_id = AV_CODEC_ID_H264;
PAVCodecCtx->bit_rate = 0;
PAVCodecCtx->width = 1920;
PAVCodecCtx->height = 1080;
PAVCodecCtx->time_base.den = STREAM_FRAME_RATE*1000;
PAVCodecCtx->time_base.num = 1000;
PAVCodecCtx->gop_size = 1;
PAVCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
if (PAVCodecCtx->codec_id == AV_CODEC_ID_MPEG2VIDEO)
{
PAVCodecCtx->max_b_frames = 2;
}
if (PAVCodecCtx->codec_id == AV_CODEC_ID_MPEG1VIDEO)
{
PAVCodecCtx->mb_decision = 2;
}
break;
default:
break;
}
if (poutFmtCtx->oformat->flags & AVFMT_GLOBALHEADER)
{
PAVCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
return HI_SUCCESS;
}
初始化,写文件头
int HI_PDT_CreateMp4(VENC_CHN VeChn)
{
int ret = 0; // 成功返回0,失败返回1
char pszFileName[256] = {0};
AVOutputFormat *pOutFmt = NULL;
sprintf(pszFileName,"tttt_%d.mp4",VeChn);
av_register_all();
avformat_alloc_output_context2(&g_OutFmt_Ctx, NULL, NULL, pszFileName);
if (NULL == g_OutFmt_Ctx)
{ //try default
printf("Could not deduce output format from file extension: using mp4. \n");
avformat_alloc_output_context2(&g_OutFmt_Ctx, NULL, "mp4", pszFileName);
if (NULL == g_OutFmt_Ctx)
{
MLOGE("avformat_alloc_output_context2 failed \n");
return -1;
}
}
pOutFmt = g_OutFmt_Ctx->oformat;
if (pOutFmt->video_codec == AV_CODEC_ID_NONE)
{
MLOGE("add_stream ID =%d \n",pOutFmt->video_codec);
goto exit_outFmt_failed;
}
ret = HI_PDT_Add_Stream(g_OutFmt_Ctx);
if(ret <0)
{
MLOGD(" HI_PDT_Add_Stream Failed \n");
goto exit_outFmt_failed;
}
MLOGD("==========Output Information==========\n");
av_dump_format(g_OutFmt_Ctx, 0, pszFileName, 1);
/* open the output file, if needed */
if (!(pOutFmt->flags & AVFMT_NOFILE))
{
ret = avio_open(&g_OutFmt_Ctx->pb, pszFileName, AVIO_FLAG_WRITE);
if (ret < 0)
{
printf("could not open %s\n", pszFileName);
goto exit_avio_open_failed;
}
}
/* Write the stream header, if any */
ret = avformat_write_header(g_OutFmt_Ctx, NULL);
if (ret < 0)
{
printf("Error occurred when opening output file \n");
goto exit_writeheader_failed;
}
b_First_IDR_Find = 0;
return HI_SUCCESS;
exit_writeheader_failed:
exit_avio_open_failed:
if (g_OutFmt_Ctx && !(g_OutFmt_Ctx->flags & AVFMT_NOFILE))
avio_close(g_OutFmt_Ctx->pb);
exit_outFmt_failed:
if(NULL != g_OutFmt_Ctx)
avformat_free_context(g_OutFmt_Ctx);
return -1;
}
写文件尾
void HI_PDT_CloseMp4(HI_VOID)
{
if (g_OutFmt_Ctx)
av_write_trailer(g_OutFmt_Ctx);
if (g_OutFmt_Ctx && !(g_OutFmt_Ctx->oformat->flags & AVFMT_NOFILE))
avio_close(g_OutFmt_Ctx->pb);
if (g_OutFmt_Ctx)
{
avformat_free_context(g_OutFmt_Ctx);
g_OutFmt_Ctx = NULL;
}
vi = -1;
b_First_IDR_Find = 0;
}
其中会从第一个非P帧开始写入。
void HI_PDT_WriteVideo(HI_HANDLE VencHdl, VENC_STREAM_S *pstStream,
PAYLOAD_TYPE_E enType)
{
unsigned int i=0;
unsigned char* pPackVirtAddr = NULL;
unsigned int u32PackLen = 0;
unsigned int u32PackOffset = 0;
H264E_NALU_TYPE_E enH264EType;
H265E_NALU_TYPE_E enH265EType;
int ret = 0;
AVStream *pst = NULL;
AVPacket pkt;
int isIDR =0;
if(vi<0)
{
printf("vi less than 0 \n");
return;
}
if(NULL == pstStream)
{
return;
}
pst = g_OutFmt_Ctx->streams[vi];
for (i = 0 ; i < pstStream->u32PackCount; i++)
{
pPackVirtAddr = pstStream->pstPack[i].pu8Addr ;
u32PackLen = pstStream->pstPack[i].u32Len;
u32PackOffset = pstStream->pstPack[i].u32Offset;
isIDR = 0;
av_init_packet(&pkt);
if(PT_H264 == enType)
{
enH264EType = pstStream->pstPack[i].DataType.enH264EType;
isIDR = HI_PDT_CheckKeyFrame_H264(enH264EType);
}
else if (PT_H265 == enType)
{
enH265EType = pstStream->pstPack[i].DataType.enH265EType;
isIDR = HI_PDT_CheckKeyFrame_H265(enH265EType);
}
else
{
printf("unsupport stream type! \n");
return ;
}
pkt.flags |= (isIDR==1) ? AV_PKT_FLAG_KEY : 0;
pkt.stream_index = pst->index;
pkt.data = (unsigned char*)(pPackVirtAddr + u32PackOffset);
pkt.size = u32PackLen - u32PackOffset;
if(b_First_IDR_Find ==0)
{
if (0 == ( pkt.flags & AV_PKT_FLAG_KEY ))
continue;
b_First_IDR_Find = 1;
}
pkt.pts = av_rescale_q((ptsInc++), pst->codec->time_base,pst->time_base);
pkt.dts=av_rescale_q_rnd(pkt.dts, pst->time_base,pst->time_base,(enum AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
pkt.duration = av_rescale_q(pkt.duration,pst->time_base, pst->time_base);
pkt.pos = -1;
ret = av_interleaved_write_frame(g_OutFmt_Ctx, &pkt);
if (ret < 0)
{
printf("cannot write frame");
}
}
return;
}
static int HI_PDT_CheckKeyFrame_H264(H264E_NALU_TYPE_E enH264EType)
{
HI_BOOL isIDR = 0;
switch(enH264EType)
{
case H264E_NALU_IDRSLICE:
case H264E_NALU_ISLICE:
case H264E_NALU_SPS:
case H264E_NALU_PPS:
isIDR = 1;
break;
default:
break;
}
return isIDR;
}
static int HI_PDT_CheckKeyFrame_H265(H265E_NALU_TYPE_E enH265EType)
{
HI_BOOL isIDR = 0;
switch(enH265EType)
{
case H265E_NALU_IDRSLICE:
case H265E_NALU_ISLICE:
case H265E_NALU_SPS:
case H265E_NALU_PPS:
case H265E_NALU_VPS:
isIDR = 1;
break;
default:
break;
}
return isIDR;
}
第一步:HI_PDT_CreateMp4(VencChn[0]);
第二步:
海思平台通过: HI_MPI_VENC_GetStream() API 获取到stream 的数据
然后调用 WriteVideo() 写入数据。
结束录像时,调用 HI_PDT_CloseMp4();