第七季3:分析“将H.264码流打包成MP4格式的文件”的源码

本文主要介绍了如何在朱有鹏嵌入式课程中,通过Sample_VENC_720P_CLASSIC函数修改,将H.264编码的码流转换为MP4格式,涉及SAMPLE_COMM_VENC_MP4函数的详细分析。内容包括函数调用关系、文件操作及关键帧处理。
摘要由CSDN通过智能技术生成

以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。

前言

“将H.264MP4码流打包成MP4格式文件”的实验,见博文第七季2:MP4v2库的移植与播放实战

接下来我们将对相关的源码进行分析。建立SI工程,导入sample_mp4_ar0130目录、mpp目录下的ko目录、lib目录、component目录、inlcude目录(该目录包含分析时用到的ortp、mp4等库文件)等文件。

一、Sample的函数调用关系

这里的源码是在第6季的sample_venc.c文件基础上编写的,因此有很多无关的代码没有删去。

修改后的sample_venc.c文件中的main函数的内容如下:

int main(int argc, char *argv[])
{
	HI_S32 s32Ret;
	MPP_VERSION_S mppVersion;

	HI_MPI_SYS_GetVersion(&mppVersion);//获取MPP的版本号

	printf("MPP Ver  %s\n",mppVersion.aVersion);
	
	RtspServer_init();//可以注释掉这个函数,MP4格式打包与这个函数没有关系。
    s32Ret = SAMPLE_VENC_720P_CLASSIC();//关注这个函数!

	return HI_FAILURE;

	if (HI_SUCCESS == s32Ret)
	    	printf("program exit normally!\n");
	else
	    	printf("program exit abnormally!\n");
	while(1)
	{usleep(1000);}
	return s32Ret;
}

其中SAMPLE_VENC_720P_CLASSIC函数流程和第二季的步骤基本相同。不同之处,在于“step6保存码流”这个步骤之前的代码中更改了通道数目;另外更改了“step6保存码流”这个步骤的代码。

修改后的“step6保存码流”这个步骤的函数调用关系如下:

SAMPLE_COMM_VENC_StartGetStream         //位于sample_comm_venc.c文件
    SAMPLE_COMM_VENC_GetVencStreamProc  //位于sample_comm_venc.c文件
        //忽略部分调用(和第二季的一样)
        SAMPLE_COMM_VENC_MP4            //位于sample_comm_venc.c文件

第二季是将编码方式为H.264的码流保存为H264裸流文件,而这里是将编码方式为H.264的码流保存为MP4格式的文件,都是保存为文件,只是格式不一样,因此按道理是在第二季保存文件时(即 step 2.5)多加一个步骤,即上面的 SAMPLE_COMM_VENC_MP4 函数,该函数负责将一帧数据打包成MP4格式。接下来我们重点分析SAMPLE_COMM_VENC_MP4 函数。

三、分析SAMPLE_COMM_VENC_MP4函数

1、函数的整体分析

HI_S32 SAMPLE_COMM_VENC_MP4(VENC_STREAM_S *stStream)
{
	static int nRecordFlag = 0x00;
	static int recording = 0x1;
	static int spsflag = 0;
	static int ppsflag = 0;
	static MP4TrackId video = 0;
	static MP4FileHandle hMP4File = NULL;

	static char recordfish = 0x1;
	
	int j = 0;
	int len = 0;
	char *pData = NULL;
	char isSyncSample = 0;
	
	if(recordfish == 0x00){
		return 0;
	}

	if(hMP4File == NULL){
		hMP4File = MP4CreateEx("/usr/mmc/test.mp4",0, 1, 1, 0, 0, 0, 0);//文件存储路径
		if (hMP4File == MP4_INVALID_FILE_HANDLE)	{
			printf("open file fialed.\n");
			return -1;
		}
	
		MP4SetTimeScale(hMP4File, 90000);
	}
	
	if(recording && stStream->u32Seq > 30){	//丢弃前30帧,也可以不丢弃
		if(stStream->u32PackCount >= 3){	//从I帧开始编码,保证文件开始就能播放
			nRecordFlag = 1;
		}
	
		if(nRecordFlag){
			for(j = 0;j < stStream->u32PackCount;j++){
				len 	= stStream->pstPack[j].u32Len - stStream->pstPack[j].u32Offset;
				pData	= (stStream->pstPack[j].pu8Addr + stStream->pstPack[j].u32Offset);						
	
				if(stStream->pstPack[j].DataType.enH264EType == H264E_NALU_SPS){
					if(spsflag == 0x00){
						spsflag = 0x1;
						//写sps
						printf("Write sps =================\n");	
	
	
						video = MP4AddH264VideoTrack(hMP4File, 90000, 90000 / 30, 1280, 720,
																pData[4+1], //sps[1] AVCProfileIndication
																pData[4+2], //sps[2] profile_compat
																pData[4+3], //sps[3] AVCLevelIndication
																3); // 4 bytes length before each NAL unit
							MP4SetVideoProfileLevel(hMP4File, 0x7F);
							MP4AddH264SequenceParameterSet(hMP4File, video, pData+4, len-4);										
					}
					
					continue;
				}
				
				if(stStream->pstPack[j].DataType.enH264EType == H264E_NALU_PPS){
					if(ppsflag == 0x00){
						ppsflag = 0x1;
						//写pps									
						printf("Write pps -------------------\n");										
						MP4AddH264PictureParameterSet(hMP4File, video, pData+4, len-4);
					}
					
					continue;
				}
	
				isSyncSample = (stStream->pstPack[j].DataType.enH264EType == H264E_NALU_ISLICE)	?  (1) : (0);
				pData[0] = (len - 4) >> 24;
				pData[1] = (len - 4) >> 16;
				pData[2] = (len - 4) >> 8;
				pData[3] = len - 4; 							
	
				printf("Write date type = %d  isSyncSample = %d\n",stStream->pstPack[j].DataType.enH264EType,isSyncSample);								
	
				MP4WriteSample(hMP4File, video, pData, len , MP4_INVALID_DURATION, 0, isSyncSample);
			}					
		}
	}
	
	if((recording && stStream->u32Seq > 900)){//控制文件时长。900帧/30帧每秒=30秒
		recording = 0x00;
		printf("Close mp4 file\n");						
		MP4Close(hMP4File, 0);
		hMP4File = NULL;
		video = 0;
		recordfish = 0x00;
	}

}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天糊土

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值