hisi3559A平台VENC获取H264裸流封装成mp4

主要函数说明:

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;
    }



MP4创建函数:

初始化,写文件头

  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;
    }

MP4关闭函数:

写文件尾

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();

备注:代码中含有一些是海思平台特有的变量定义

你可能感兴趣的:(海思平台(hisi))