以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。
前言
“将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");elseprintf("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;//写spsprintf("Write sps =================\n"); video = MP4AddH264VideoTrack(hMP4File, 90000, 90000 / 30, 1280, 720,pData[4+1], //sps[1] AVCProfileIndicationpData[4+2], //sps[2] profile_compatpData[4+3], //sps[3] AVCLevelIndication3); // 4 bytes length before each NAL unitMP4SetVideoProfileLevel(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;}}