pcm+H264封装MP4代码
2014-11-17 13:06
267 查看
昵称:海鱼 日期:2014年11月10日—2014年11月17日
1.本周已完成任务:对添加osd代码进行了解
2.本周未完成任务:改善添加的osd代码
3.下周计划:改善osd代码
4.关键技术点说明:
因为osd代码没有完成,所以就把前面的代码拿出来了
为了音视频同步,我在裸音频流中每写320个字节,加入了8字节的时间戳,每个I,p帧H264的视频流中加了时间戳。
程序读出各时间戳比较,因为是随便写的DEMO,前提是视频全程都有,音频从一段时间后开始,所以我只是将视频的第一个时间戳拿出来,作为起始时间,然后音频的第一个时间戳大于它就写入空数据,直到小于它时再开始写入音频。
前面一个微博写的G711A+H264封装成MP4的有个严重的bug,因为写入音视频是一起的,哪个读完数据就停止,然而写音频和视频的时间不同。比如我录制的pcm音频2048个字节大概播放0.128秒,H264视频每个I,P帧是40ms,这样会导致音频插入了20s而视频就只插入了7s左右,导致播放中视频就卡住了,音频还在播。
所以这次改成了先把视频全部写进MP4文件后再写音频,靠起始时间戳和每次写入加上对应的时间来计算当前时间,这样可以将音视频更好的加入,也能保持音视频同步。
1.本周已完成任务:对添加osd代码进行了解
2.本周未完成任务:改善添加的osd代码
3.下周计划:改善osd代码
4.关键技术点说明:
因为osd代码没有完成,所以就把前面的代码拿出来了
为了音视频同步,我在裸音频流中每写320个字节,加入了8字节的时间戳,每个I,p帧H264的视频流中加了时间戳。
程序读出各时间戳比较,因为是随便写的DEMO,前提是视频全程都有,音频从一段时间后开始,所以我只是将视频的第一个时间戳拿出来,作为起始时间,然后音频的第一个时间戳大于它就写入空数据,直到小于它时再开始写入音频。
前面一个微博写的G711A+H264封装成MP4的有个严重的bug,因为写入音视频是一起的,哪个读完数据就停止,然而写音频和视频的时间不同。比如我录制的pcm音频2048个字节大概播放0.128秒,H264视频每个I,P帧是40ms,这样会导致音频插入了20s而视频就只插入了7s左右,导致播放中视频就卡住了,音频还在播。
所以这次改成了先把视频全部写进MP4文件后再写音频,靠起始时间戳和每次写入加上对应的时间来计算当前时间,这样可以将音视频更好的加入,也能保持音视频同步。
#include <fcntl.h> #include <stdio.h> #include <ctype.h> #include <errno.h> #include <stdlib.h> #include <string.h> #include <limits.h> #include <unistd.h> #include <signal.h> #include <dirent.h> #include <pthread.h> #include <asm/types.h> #include <arpa/inet.h> #include <sys/vfs.h> #include <sys/time.h> #include <sys/wait.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/utsname.h> #include <netdb.h> #include <net/if.h> #include <netinet/in.h> #include <net/route.h> #include <net/if_arp.h> #include <linux/fs.h> #include <linux/sockios.h> #include <linux/netlink.h> #include <linux/rtnetlink.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/ether.h> #include <netinet/ip_icmp.h> #include "faac.h" #include "mp4v2.h" #define JOSEPH_G711A_LOCATION "../av_file/test1.pcm" #define JOSEPH_H264_LOCALTION "../av_file/test1.h264" #define JOSEPH_MP4_FILE "test.mp4" #define MP4_DETAILS_ALL 0xFFFFFFFF #define NALU_SPS 0 #define NALU_PPS 1 #define NALU_I 2 #define NALU_P 3 #define NALU_SET 4 typedef unsigned int uint32_t; typedef unsigned char uint8_t; typedef struct Joseph_Acc_Config { FILE* fpIn; //打开的音频文件 faacEncHandle hEncoder; //音频文件描述符 unsigned long nSampleRate; //音频采样数 unsigned int nChannels; //音频声道数 unsigned int nPCMBitSize; //音频采样精度 unsigned long nInputSamples; //每次调用编码时所应接收的原始数据长度 unsigned long nMaxOutputBytes; //每次调用编码时生成的AAC数据的最大长度 unsigned char* pbPCMBuffer; //pcm数据 unsigned char* pbAACBuffer; //aac数据 unsigned long long int timestamp; unsigned long long int mtimestamp; }JOSEPH_ACC_CONFIG; typedef struct Joseph_Mp4_Config { FILE* fpInVideo; //打开的视频文件 MP4FileHandle hFile; //mp4文件描述符 MP4TrackId video; //视频轨道标志符 MP4TrackId audio; //音频轨道标志符 int m_vFrameDur; //帧间隔时间 unsigned int timeScale; //视频每秒的ticks数,如90000 unsigned int fps; //视频帧率 unsigned short width; //视频宽 unsigned short height; //视频高 unsigned long long int timestamp; unsigned long long int mtimestamp; }JOSEPH_MP4_CONFIG; int joseph_get_h264_frame(JOSEPH_MP4_CONFIG* joseph_mp4_config) { joseph_mp4_config->fpInVideo = fopen(JOSEPH_H264_LOCALTION, "rb"); return 0; } /***********************************************************mp4 encode***********************************************/ JOSEPH_MP4_CONFIG* InitMp4Encoder(JOSEPH_ACC_CONFIG* joseph_aac_config) { JOSEPH_MP4_CONFIG *joseph_mp4_config = NULL; joseph_mp4_config =(JOSEPH_MP4_CONFIG *)malloc(sizeof(JOSEPH_MP4_CONFIG)); joseph_mp4_config->m_vFrameDur = 0; joseph_mp4_config->video = MP4_INVALID_TRACK_ID; joseph_mp4_config->audio = MP4_INVALID_TRACK_ID; joseph_mp4_config->hFile = NULL; joseph_mp4_config->timeScale = 90000; joseph_mp4_config->fps = 25; joseph_mp4_config->width = 640; joseph_mp4_config->height = 480; /*file handle*/ joseph_mp4_config->hFile = MP4Create(JOSEPH_MP4_FILE, 0); if(joseph_mp4_config->hFile == MP4_INVALID_FILE_HANDLE) { printf("open file fialed.\n"); return NULL; } MP4SetTimeScale(joseph_mp4_config->hFile, joseph_mp4_config->timeScale); //timeScale /*audio track*/ joseph_mp4_config->audio = MP4AddAudioTrack(joseph_mp4_config->hFile, joseph_aac_config->nSampleRate, \ joseph_aac_config->nInputSamples, MP4_MPEG4_AUDIO_TYPE); if(joseph_mp4_config->audio == MP4_INVALID_TRACK_ID) { printf("add audio track failed.\n"); return NULL; } MP4SetAudioProfileLevel(joseph_mp4_config->hFile, 0x2); unsigned char aacConfig[2] = {0x15, 0x88}; // 0001 0101 1000 1000 MP4SetTrackESConfiguration(joseph_mp4_config->hFile, joseph_mp4_config->audio, aacConfig, 2); return joseph_mp4_config; } //------------------------------------------------------------------------------------------------- Mp4Encode说明 // 【h264编码出的NALU规律】 // 第一帧 SPS【0 0 0 1 0x67】 PPS【0 0 0 1 0x68】 SEI【0 0 0 1 0x6】 IDR【0 0 0 1 0x65】 // p帧 P【0 0 0 1 0x61】 // I帧 SPS【0 0 0 1 0x67】 PPS【0 0 0 1 0x68】 IDR【0 0 0 1 0x65】 // 【mp4v2封装函数MP4WriteSample】 // 此函数接收I/P nalu,该nalu需要用4字节的数据大小头替换原有的起始头,并且数据大小为big-endian格式 //------------------------------------------------------------------------------------------------- int Mp4VEncode(JOSEPH_MP4_CONFIG* joseph_mp4_config, unsigned char* naluData, int naluSize) { int index = -1; if(naluData[0]==0 && naluData[1]==0 && naluData[2]==0 && naluData[3]==1 && naluData[4]==0x67) { index = NALU_SPS; // printf("%s[%d]====NALU_SPS\n",__FUNCTION__,__LINE__); } if(index!=NALU_SPS && joseph_mp4_config->video == MP4_INVALID_TRACK_ID) { return -1; } if(naluData[0]==0 && naluData[1]==0 && naluData[2]==0 && naluData[3]==1 && naluData[4]==0x68) { index = NALU_PPS; // printf("%s[%d]====NALU_PPS\n",__FUNCTION__,__LINE__); } if(naluData[0]==0 && naluData[1]==0 && naluData[2]==0 && naluData[3]==1 && naluData[4]==0x65) { index = NALU_I; // printf("%s[%d]====NALU_I\n",__FUNCTION__,__LINE__); } if(naluData[0]==0 && naluData[1]==0 && naluData[2]==0 && naluData[3]==1 && naluData[4]==0x61) { index = NALU_P; // printf("%s[%d]====NALU_P\n",__FUNCTION__,__LINE__); } if(naluData[0]==0 && naluData[1]==0 && naluData[2]==0 && naluData[3]==1 && naluData[4]==0x6) { index = NALU_SET; // printf("%s[%d]====NALU_SET\n",__FUNCTION__,__LINE__); } switch(index) { case NALU_SPS: if(joseph_mp4_config->video == MP4_INVALID_TRACK_ID) { joseph_mp4_config->video = MP4AddH264VideoTrack (joseph_mp4_config->hFile, joseph_mp4_config->timeScale, //timeScale (joseph_mp4_config->timeScale / joseph_mp4_config->fps), //sampleDuration timeScale/fps joseph_mp4_config->width, // width joseph_mp4_config->height, // height naluData[5], // sps[1] AVCProfileIndication naluData[6], // sps[2] profile_compat naluData[7], // sps[3] AVCLevelIndication 3); // 4 bytes length before each NAL unit if(joseph_mp4_config->video == MP4_INVALID_TRACK_ID) { printf("add video track failed.\n"); return -1; } //MP4SetVideoProfileLevel(joseph_mp4_config->hFile, 1); // Simple Profile @ Level 3 mp4编码标准 MP4SetVideoProfileLevel(joseph_mp4_config->hFile, 0x7F); // Simple Profile @ Level 3 } MP4AddH264SequenceParameterSet(joseph_mp4_config->hFile, joseph_mp4_config->video, naluData+4, naluSize-4); break; case NALU_PPS: MP4AddH264PictureParameterSet(joseph_mp4_config->hFile, joseph_mp4_config->video, naluData+4, naluSize-4); break; case NALU_SET: MP4AddH264PictureParameterSet(joseph_mp4_config->hFile, joseph_mp4_config->video, naluData+4, naluSize-4); break; case NALU_I: { unsigned char* IFrameData = (unsigned char*)malloc((naluSize)* sizeof(unsigned char)); IFrameData[0] = (naluSize-4) >>24; IFrameData[1] = (naluSize-4) >>16; IFrameData[2] = (naluSize-4) >>8; IFrameData[3] = (naluSize-4) &0xff; memcpy(IFrameData+4, naluData+4, naluSize-4); //if(!MP4WriteSample(joseph_mp4_config->hFile, joseph_mp4_config->video, IFrameData, naluSize+1, m_vFrameDur/8000*90000, 0, 1)) if(!MP4WriteSample(joseph_mp4_config->hFile, joseph_mp4_config->video, IFrameData, naluSize, MP4_INVALID_DURATION, 0, 1)) { return -1; } //joseph_mp4_config->m_vFrameDur = 0; free(IFrameData); IFrameData = NULL; break; } case NALU_P: { naluData[0] = (naluSize-4) >>24; naluData[1] = (naluSize-4) >>16; naluData[2] = (naluSize-4) >>8; naluData[3] = (naluSize-4) &0xff; //if(!MP4WriteSample(joseph_mp4_config->hFile, joseph_mp4_config->video, naluData, naluSize, m_vFrameDur/8000*90000, 0, 1)) if(!MP4WriteSample(joseph_mp4_config->hFile, joseph_mp4_config->video, naluData, naluSize, MP4_INVALID_DURATION, 0, 1)) { return -1; } //joseph_mp4_config->m_vFrameDur = 0; break; } default: break; } return 0; } int Mp4AEncode(JOSEPH_MP4_CONFIG* joseph_mp4_config, unsigned char* aacData, int aacSize) { if(joseph_mp4_config->video == MP4_INVALID_TRACK_ID) { return -1; } MP4WriteSample(joseph_mp4_config->hFile, joseph_mp4_config->audio, aacData, aacSize , MP4_INVALID_DURATION, 0, 1); //joseph_mp4_config->m_vFrameDur += 1024; return 0; } void CloseMp4Encoder(JOSEPH_MP4_CONFIG* joseph_mp4_config) { if(joseph_mp4_config->hFile) { MP4Close(joseph_mp4_config->hFile, 0); joseph_mp4_config->hFile = NULL; } return ; } /*************************************************aac encode******************************************/ JOSEPH_ACC_CONFIG* InitAccEncoder(void) { JOSEPH_ACC_CONFIG* joseph_aac_config = NULL; faacEncConfigurationPtr pConfiguration; int nRet = 0; int nPCMBufferSize = 0; joseph_aac_config = (JOSEPH_ACC_CONFIG*)malloc(sizeof(JOSEPH_ACC_CONFIG)); joseph_aac_config->nSampleRate = 8000; joseph_aac_config->nChannels = 1; joseph_aac_config->nPCMBitSize = 16; joseph_aac_config->nInputSamples = 0; joseph_aac_config->nMaxOutputBytes = 0; joseph_aac_config->fpIn = fopen(JOSEPH_G711A_LOCATION, "rb"); //open FAAC engine joseph_aac_config->hEncoder = faacEncOpen(joseph_aac_config->nSampleRate, joseph_aac_config->nChannels, \ &joseph_aac_config->nInputSamples, &joseph_aac_config->nMaxOutputBytes); if(joseph_aac_config->hEncoder == NULL) { printf("failed to call faacEncOpen()\n"); return NULL; } nPCMBufferSize = (joseph_aac_config->nInputSamples*(joseph_aac_config->nPCMBitSize/8)); joseph_aac_config->pbPCMBuffer=(unsigned char*)malloc(nPCMBufferSize*sizeof(unsigned char)); memset(joseph_aac_config->pbPCMBuffer, 0, nPCMBufferSize); joseph_aac_config->pbAACBuffer=(unsigned char*)malloc(joseph_aac_config->nMaxOutputBytes*sizeof(unsigned char)); memset(joseph_aac_config->pbAACBuffer, 0, joseph_aac_config->nMaxOutputBytes); //GET current encoding configuration pConfiguration = faacEncGetCurrentConfiguration(joseph_aac_config->hEncoder); #if 1 pConfiguration->inputFormat = FAAC_INPUT_16BIT; pConfiguration->outputFormat = 0; pConfiguration->aacObjectType = LOW; #else pConfiguration->inputFormat = FAAC_INPUT_16BIT; /*0 - raw; 1 - ADTS*/ pConfiguration->outputFormat = 0; pConfiguration->useTns = 0; pConfiguration->allowMidside = 1; pConfiguration->shortctl = SHORTCTL_NORMAL; pConfiguration->aacObjectType = LOW; pConfiguration->mpegVersion = MPEG2; #endif //set encoding configuretion nRet = faacEncSetConfiguration(joseph_aac_config->hEncoder, pConfiguration); return joseph_aac_config; } void CloseAccEncoder(JOSEPH_ACC_CONFIG* joseph_aac_config) { if(joseph_aac_config->hEncoder) { faacEncClose(joseph_aac_config->hEncoder); joseph_aac_config->hEncoder = NULL; } return ; } /*********************************************main**************************************************/ int main(int argc, char* argv[]) { JOSEPH_ACC_CONFIG *joseph_aac_config = NULL; JOSEPH_MP4_CONFIG *joseph_mp4_config = NULL; int nRet = 0; int nTmp = 0; int nCount = 0; int nStatus = 0; int PCMSize = 320; int nPCMRead = 0; int nH264Read = 0; int H264Read_one = 0; unsigned long long int nVideoSize = 0; int nPCMBufferSize = 0; int i = 0,j,k; unsigned char nVideoBuffer[1048576] = {0}; unsigned char VideoBuffer[1036] = {0}; unsigned char mVideoBuffer[1024] = {0}; unsigned char *pbPCMTmpBuffer = NULL; int timeout=0; pbPCMTmpBuffer = (unsigned char *)malloc(PCMSize *sizeof(unsigned char)); memset(pbPCMTmpBuffer, 0, PCMSize); // joseph_mp4_config->kk = 0; /*init aac */ if((joseph_aac_config=InitAccEncoder()) == NULL) { printf("init aac failed\n"); return -1; } /*init mp4*/ if((joseph_mp4_config=InitMp4Encoder(joseph_aac_config)) == NULL) { printf("init mp4 failed\n"); return -1; } nPCMBufferSize = (joseph_aac_config->nInputSamples*(joseph_aac_config->nPCMBitSize/8)); /*get video frame*/ nVideoSize = joseph_get_h264_frame(joseph_mp4_config); if(nVideoSize < 0) { printf("read g711a frame failed\n"); return -1; } nH264Read = fread(&VideoBuffer, 1, 1024, joseph_mp4_config->fpInVideo); nStatus = 0; while(nH264Read>0) { H264Read_one = 1; while(H264Read_one>0) { if(nH264Read<12) { k = nH264Read; nH264Read = fread(&mVideoBuffer, 1, 1024, joseph_mp4_config->fpInVideo); memcpy(VideoBuffer+k,mVideoBuffer,nH264Read); nH264Read += k; } for(j=5;j<nH264Read;j++) { if((VideoBuffer[j]==0x00)&&(VideoBuffer[j+1]==0x00)&&(VideoBuffer[j+2]==0x00)&&(VideoBuffer[j+3]==0x01)&&(j+4<nH264Read)) { memcpy(nVideoBuffer+nVideoSize,VideoBuffer,j); H264Read_one = 0; nVideoSize += j; if((VideoBuffer[j+4]==0x61)||(VideoBuffer[j+4]==0x67)) { joseph_mp4_config->timestamp = 0; for(k=1;k<=8;k++) { joseph_mp4_config->timestamp = joseph_mp4_config->timestamp<<8; joseph_mp4_config->timestamp += VideoBuffer[j-k]; } if(nStatus == 0) { joseph_mp4_config->mtimestamp = joseph_mp4_config->timestamp; nStatus = 1; } nVideoSize -= 8; memcpy(nVideoBuffer,nVideoBuffer,nVideoSize); } memcpy(VideoBuffer,VideoBuffer+j,nH264Read-j); nH264Read -= j; break; } if(nH264Read == (j+1)) { memcpy(nVideoBuffer+nVideoSize,VideoBuffer,nH264Read-12); nVideoSize += nH264Read-12; memcpy(VideoBuffer,VideoBuffer+nH264Read-12,12); nH264Read = 0; nH264Read = fread(&mVideoBuffer, 1, 1024, joseph_mp4_config->fpInVideo); memcpy(VideoBuffer+12,mVideoBuffer,nH264Read); nH264Read += 12; if(nH264Read <1036) { memcpy(nVideoBuffer+nVideoSize,VideoBuffer,nH264Read-8); nVideoSize += nH264Read-8; joseph_mp4_config->timestamp = 0; for(k=1;k<=8;k++) { joseph_mp4_config->timestamp = joseph_mp4_config->timestamp<<8; joseph_mp4_config->timestamp += VideoBuffer[nH264Read-k]; } nH264Read = 0; H264Read_one = 0; } } } } if(nVideoSize < 0) { printf("read g711a frame failed\n"); break; } /*write video frame to mp4*/ if((Mp4VEncode(joseph_mp4_config, nVideoBuffer, nVideoSize)) < 0) { printf("write video frame failed\n"); break; } nVideoSize = 0; memset(nVideoBuffer, 0, nVideoSize); } nPCMRead = 1; k = 0; while(nPCMRead>0) { nStatus = 0; nPCMRead = fread(pbPCMTmpBuffer, 1, 160, joseph_aac_config->fpIn); if(timeout == 0) timeout = 1; else { joseph_aac_config->timestamp = 0; fread(&joseph_aac_config->timestamp, 8, 1, joseph_aac_config->fpIn); timeout = 0; while((k == 0)&&(joseph_aac_config->timestamp > joseph_mp4_config->mtimestamp)) { nRet = 160; if((Mp4AEncode(joseph_mp4_config, joseph_aac_config->pbAACBuffer, nRet)) < 0) { printf("write audio frame failed\n"); break; } joseph_mp4_config->mtimestamp += 128000; } k = 1; } /*pcm to aac*/ if((nPCMBufferSize - nCount) < nPCMRead) { nStatus = 1; memcpy((joseph_aac_config->pbPCMBuffer + nCount), pbPCMTmpBuffer, (nPCMBufferSize - nCount)); joseph_aac_config->nInputSamples = (nPCMBufferSize / (joseph_aac_config->nPCMBitSize / 8)); nRet = faacEncEncode(joseph_aac_config->hEncoder, (int*) (joseph_aac_config->pbPCMBuffer), \ joseph_aac_config->nInputSamples, joseph_aac_config->pbAACBuffer, joseph_aac_config->nMaxOutputBytes); /*write audio frame to mp4*/ if((Mp4AEncode(joseph_mp4_config, joseph_aac_config->pbAACBuffer, nRet)) < 0) { printf("write audio frame failed\n"); break; } memset(joseph_aac_config->pbPCMBuffer,0,1024); /*Treatment of redundant frames*/ nTmp = (nPCMRead - (nPCMBufferSize - nCount)); memset(joseph_aac_config->pbPCMBuffer, 0, nPCMBufferSize); memcpy(joseph_aac_config->pbPCMBuffer, (pbPCMTmpBuffer + (nPCMBufferSize-nCount)), nTmp); nCount = 0; nCount += nTmp; } if( nStatus == 0) { memcpy(joseph_aac_config->pbPCMBuffer + nCount, pbPCMTmpBuffer, nPCMRead); nCount += nPCMRead; } if(nPCMRead < 160) { joseph_aac_config->nInputSamples = (nCount / (joseph_aac_config->nPCMBitSize / 8)); nRet = faacEncEncode(joseph_aac_config->hEncoder, (int*) (joseph_aac_config->pbPCMBuffer), \ joseph_aac_config->nInputSamples, joseph_aac_config->pbAACBuffer, joseph_aac_config->nMaxOutputBytes); /*write audio frame to mp4*/ if((Mp4AEncode(joseph_mp4_config, joseph_aac_config->pbAACBuffer, nRet)) < 0) { printf("write audio frame failed\n"); break; } break; } memset(pbPCMTmpBuffer, 0, 160); } /*Close FAAC engine*/ CloseAccEncoder(joseph_aac_config); CloseMp4Encoder(joseph_mp4_config); /*close file fp*/ fclose(joseph_aac_config->fpIn); /*free the source of malloc*/ //audio part free(joseph_aac_config->pbPCMBuffer); joseph_aac_config->pbPCMBuffer = NULL; free(joseph_aac_config->pbAACBuffer); joseph_aac_config->pbAACBuffer = NULL; free(pbPCMTmpBuffer); pbPCMTmpBuffer = NULL; free(joseph_aac_config); joseph_aac_config = NULL; //video part free(joseph_mp4_config); joseph_mp4_config = NULL; // printf("%s:[%d] The sys changover succeed !\n",__FUNCTION__,__LINE__); return 0; }
相关文章推荐
- 周报 关于G711a+H264封装成MP4(代码是转自http://blog.csdn.net/skdkjzz/article/details/40393891)
- 音频录制的噪声抑制与PCM+H264封装MP4
- H264 编码封装成 MP4 格式 视频流 RTP 封包
- 关于对H264码流的PS的封装的相关代码实现
- linux下利用mp4v2库将h264和aac文件封装成MP4
- MP4V2封装的类库,可将H264和AAC直接打包到MP4容器中,堪称经典
- H264编码的MP4文件封装RTP包发送给VLC播放器播放
- 成功在MP4封装的H264视频中提取能播放的裸流
- 6.使用DirecrShow采集摄像头视音频并实时进行H264和AAC编码后封装成MP4
- 关于对H264码流的TS的封装的相关代码实现
- 使用mp4v2将aac音频h264视频数据封装成mp4开发心得
- 关于对H264码流的TS的封装的相关代码实现
- 成功在MP4封装的H264视频中提取能播放的裸流
- 关于对H264码流的PS的封装的相关代码实现
- 关于对H264码流的PS的封装的相关代码实现
- 使用FFMPEG从MP4封装中提取视频流到H264文件
- mpeg4ip的Mp4v2库移植到android系统上,实现h264封装到mp4的容器内
- 转supermanwg的 mpeg4ip的Mp4v2库移植到android系统上,实现h264封装到mp4的容器内
- mpeg4ip的Mp4v2库移植到android系统上,实现h264封装到mp4的容器内
- ffmpeg将yuv封装为mp4测试代码