H264视频编码成MP4文件

http://blog.csdn.net/firehood_/article/details/8813587


        最近需要将H264视频编码成MP4格式。研究了一下,一种方法是采用ffmpeg库,可以先将H264文件解码,再编码生成MP4文件,但这种方式效率较低,10M的视频可能需要几秒钟才能完成。另一种方式根据MP4文件协议直接将H264包封装成MP4格式,由于是直接基于MP4的封装,因而效率很高。H264可以很方便的封装成FLV文件,但MP4格式格式相对比较复杂,封装起来会比较麻烦。由于没时间研究MP4协议,在Google Code上找到一个开源的MP4编解码库Mp4v2(https://code.google.com/p/mp4v2/),通过Mp4v2可以很方便的将H264编码成MP4格式文件。为了方便使用,基于该库封装了一个MP4Encoder类,MP4Encoder封装的接口如下。目前仅支持将H264文件或数据帧编码成MP4文件。

[cpp]  view plain  copy
  1. class MP4Encoder  
  2. {  
  3. public:  
  4.     MP4Encoder(void);  
  5.     ~MP4Encoder(void);  
  6. public:  
  7.     // open or creat a mp4 file.  
  8.     MP4FileHandle CreateMP4File(const char *fileName,int width,int height,int timeScale = 90000,int frameRate = 25);  
  9.     // wirte 264 metadata in mp4 file.  
  10.     bool Write264Metadata(MP4FileHandle hMp4File,LPMP4ENC_Metadata lpMetadata);  
  11.     // wirte 264 data, data can contain  multiple frame.  
  12.     int WriteH264Data(MP4FileHandle hMp4File,const unsigned char* pData,int size);   
  13.     // close mp4 file.  
  14.     void CloseMP4File(MP4FileHandle hMp4File);  
  15.     // convert H264 file to mp4 file.  
  16.     // no need to call CreateMP4File and CloseMP4File,it will create/close mp4 file automaticly.  
  17.     bool WriteH264File(const char* pFile264,const char* pFileMp4);  
  18.     // Prase H264 metamata from H264 data frame  
  19.     static bool PraseMetadata(const unsigned char* pData,int size,MP4ENC_Metadata &metadata);  
  20. };   

客户端调用示例代码:

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. #include "MP4Encoder\MP4Encoder.h"  
  3.   
  4. int main(int argc, char** argv)  
  5. {  
  6.     MP4Encoder mp4Encoder;  
  7.     // convert H264 file to mp4 file  
  8.     mp4Encoder.WriteH264File("test.264","test.mp4");  
  9. }  


MP4Encoder完整的代码如下:

[cpp]  view plain  copy
  1. /********************************************************************  
  2. filename:   MP4Encoder.h 
  3. created:    2013-04-16 
  4. author:     firehood  
  5. purpose:    MP4编码器,基于开源库mp4v2实现(https://code.google.com/p/mp4v2/)。 
  6. *********************************************************************/  
  7. #pragma once  
  8. #include "mp4v2\mp4v2.h"  
  9.   
  10. // NALU单元  
  11. typedef struct _MP4ENC_NaluUnit  
  12. {  
  13.     int type;  
  14.     int size;  
  15.     unsigned char *data;  
  16. }MP4ENC_NaluUnit;  
  17.   
  18. typedef struct _MP4ENC_Metadata  
  19. {  
  20.     // video, must be h264 type  
  21.     unsigned int    nSpsLen;  
  22.     unsigned char   Sps[1024];  
  23.     unsigned int    nPpsLen;  
  24.     unsigned char   Pps[1024];  
  25.   
  26. } MP4ENC_Metadata,*LPMP4ENC_Metadata;  
  27.   
  28. class MP4Encoder  
  29. {  
  30. public:  
  31.     MP4Encoder(void);  
  32.     ~MP4Encoder(void);  
  33. public:  
  34.     // open or creat a mp4 file.  
  35.     MP4FileHandle CreateMP4File(const char *fileName,int width,int height,int timeScale = 90000,int frameRate = 25);  
  36.     // wirte 264 metadata in mp4 file.  
  37.     bool Write264Metadata(MP4FileHandle hMp4File,LPMP4ENC_Metadata lpMetadata);  
  38.     // wirte 264 data, data can contain  multiple frame.  
  39.     int WriteH264Data(MP4FileHandle hMp4File,const unsigned char* pData,int size);   
  40.     // close mp4 file.  
  41.     void CloseMP4File(MP4FileHandle hMp4File);  
  42.     // convert H264 file to mp4 file.  
  43.     // no need to call CreateMP4File and CloseMP4File,it will create/close mp4 file automaticly.  
  44.     bool WriteH264File(const char* pFile264,const char* pFileMp4);  
  45.     // Prase H264 metamata from H264 data frame  
  46.     static bool PraseMetadata(const unsigned char* pData,int size,MP4ENC_Metadata &metadata);  
  47. private:  
  48.     // read one nalu from H264 data buffer  
  49.     static int ReadOneNaluFromBuf(const unsigned char *buffer,unsigned int nBufferSize,unsigned int offSet,MP4ENC_NaluUnit &nalu);  
  50. private:  
  51.     int m_nWidth;  
  52.     int m_nHeight;  
  53.     int m_nFrameRate;  
  54.     int m_nTimeScale;  
  55.     MP4TrackId m_videoId;  
  56. };   

MP4Encoder.cpp

[cpp]  view plain  copy
  1. /********************************************************************  
  2. filename:   MP4Encoder.cpp 
  3. created:    2013-04-16 
  4. author:     firehood  
  5. purpose:    MP4编码器,基于开源库mp4v2实现(https://code.google.com/p/mp4v2/)。 
  6. *********************************************************************/  
  7. #include "MP4Encoder.h"  
  8. #include <string.h>  
  9.   
  10. #define BUFFER_SIZE  (1024*1024)  
  11.   
  12. MP4Encoder::MP4Encoder(void):  
  13. m_videoId(NULL),  
  14. m_nWidth(0),  
  15. m_nHeight(0),  
  16. m_nTimeScale(0),  
  17. m_nFrameRate(0)  
  18. {  
  19. }  
  20.   
  21. MP4Encoder::~MP4Encoder(void)  
  22. {  
  23. }  
  24.   
  25. MP4FileHandle MP4Encoder::CreateMP4File(const char *pFileName,int width,int height,int timeScale/* = 90000*/,int frameRate/* = 25*/)  
  26. {  
  27.     if(pFileName == NULL)  
  28.     {  
  29.         return false;  
  30.     }  
  31.     // create mp4 file  
  32.     MP4FileHandle hMp4file = MP4Create(pFileName);  
  33.     if (hMp4file == MP4_INVALID_FILE_HANDLE)  
  34.     {  
  35.         printf("ERROR:Open file fialed.\n");  
  36.         return false;  
  37.     }  
  38.     m_nWidth = width;  
  39.     m_nHeight = height;  
  40.     m_nTimeScale = 90000;  
  41.     m_nFrameRate = 25;  
  42.     MP4SetTimeScale(hMp4file, m_nTimeScale);  
  43.     return hMp4file;  
  44. }  
  45.   
  46. bool MP4Encoder::Write264Metadata(MP4FileHandle hMp4File,LPMP4ENC_Metadata lpMetadata)  
  47. {  
  48.     m_videoId = MP4AddH264VideoTrack  
  49.         (hMp4File,   
  50.         m_nTimeScale,   
  51.         m_nTimeScale / m_nFrameRate,   
  52.         m_nWidth, // width  
  53.         m_nHeight,// height  
  54.         lpMetadata->Sps[1], // sps[1] AVCProfileIndication  
  55.         lpMetadata->Sps[2], // sps[2] profile_compat  
  56.         lpMetadata->Sps[3], // sps[3] AVCLevelIndication  
  57.         3);           // 4 bytes length before each NAL unit  
  58.     if (m_videoId == MP4_INVALID_TRACK_ID)  
  59.     {  
  60.         printf("add video track failed.\n");  
  61.         return false;  
  62.     }  
  63.     MP4SetVideoProfileLevel(hMp4File, 0x01); //  Simple Profile @ Level 3  
  64.   
  65.     // write sps  
  66.     MP4AddH264SequenceParameterSet(hMp4File,m_videoId,lpMetadata->Sps,lpMetadata->nSpsLen);  
  67.   
  68.     // write pps  
  69.     MP4AddH264PictureParameterSet(hMp4File,m_videoId,lpMetadata->Pps,lpMetadata->nPpsLen);  
  70.   
  71.     return true;  
  72. }  
  73.   
  74. int MP4Encoder::WriteH264Data(MP4FileHandle hMp4File,const unsigned char* pData,int size)  
  75. {  
  76.     if(hMp4File == NULL)  
  77.     {  
  78.         return -1;  
  79.     }  
  80.     if(pData == NULL)  
  81.     {  
  82.         return -1;  
  83.     }  
  84.     MP4ENC_NaluUnit nalu;  
  85.     int pos = 0, len = 0;  
  86.     while (len = ReadOneNaluFromBuf(pData,size,pos,nalu))  
  87.     {  
  88.         if(nalu.type == 0x07) // sps  
  89.         {  
  90.             // 添加h264 track      
  91.             m_videoId = MP4AddH264VideoTrack  
  92.                 (hMp4File,   
  93.                 m_nTimeScale,   
  94.                 m_nTimeScale / m_nFrameRate,   
  95.                 m_nWidth,     // width  
  96.                 m_nHeight,    // height  
  97.                 nalu.data[1], // sps[1] AVCProfileIndication  
  98.                 nalu.data[2], // sps[2] profile_compat  
  99.                 nalu.data[3], // sps[3] AVCLevelIndication  
  100.                 3);           // 4 bytes length before each NAL unit  
  101.             if (m_videoId == MP4_INVALID_TRACK_ID)  
  102.             {  
  103.                 printf("add video track failed.\n");  
  104.                 return 0;  
  105.             }  
  106.             MP4SetVideoProfileLevel(hMp4File, 1); //  Simple Profile @ Level 3  
  107.   
  108.             MP4AddH264SequenceParameterSet(hMp4File,m_videoId,nalu.data,nalu.size);  
  109.         }  
  110.         else if(nalu.type == 0x08) // pps  
  111.         {  
  112.             MP4AddH264PictureParameterSet(hMp4File,m_videoId,nalu.data,nalu.size);  
  113.         }  
  114.         else  
  115.         {  
  116.             int datalen = nalu.size+4;  
  117.             unsigned char *data = new unsigned char[datalen];  
  118.             // MP4 Nalu前四个字节表示Nalu长度  
  119.             data[0] = nalu.size>>24;  
  120.             data[1] = nalu.size>>16;  
  121.             data[2] = nalu.size>>8;  
  122.             data[3] = nalu.size&0xff;  
  123.             memcpy(data+4,nalu.data,nalu.size);  
  124.             if(!MP4WriteSample(hMp4File, m_videoId, data, datalen,MP4_INVALID_DURATION, 0, 1))  
  125.             {  
  126.                 return 0;  
  127.             }  
  128.             delete[] data;  
  129.         }  
  130.           
  131.         pos += len;  
  132.     }  
  133.     return pos;  
  134. }  
  135.   
  136. int MP4Encoder::ReadOneNaluFromBuf(const unsigned char *buffer,unsigned int nBufferSize,unsigned int offSet,MP4ENC_NaluUnit &nalu)  
  137. {  
  138.     int i = offSet;  
  139.     while(i<nBufferSize)  
  140.     {  
  141.         if(buffer[i++] == 0x00 &&  
  142.             buffer[i++] == 0x00 &&  
  143.             buffer[i++] == 0x00 &&  
  144.             buffer[i++] == 0x01  
  145.             )  
  146.         {  
  147.             int pos = i;  
  148.             while (pos<nBufferSize)  
  149.             {  
  150.                 if(buffer[pos++] == 0x00 &&  
  151.                     buffer[pos++] == 0x00 &&  
  152.                     buffer[pos++] == 0x00 &&  
  153.                     buffer[pos++] == 0x01  
  154.                     )  
  155.                 {  
  156.                     break;  
  157.                 }  
  158.             }  
  159.             if(pos == nBufferSize)  
  160.             {  
  161.                 nalu.size = pos-i;    
  162.             }  
  163.             else  
  164.             {  
  165.                 nalu.size = (pos-4)-i;  
  166.             }  
  167.   
  168.             nalu.type = buffer[i]&0x1f;  
  169.             nalu.data =(unsigned char*)&buffer[i];  
  170.             return (nalu.size+i-offSet);  
  171.         }  
  172.     }  
  173.     return 0;  
  174. }  
  175.   
  176. void MP4Encoder::CloseMP4File(MP4FileHandle hMp4File)  
  177. {  
  178.     if(hMp4File)  
  179.     {  
  180.         MP4Close(hMp4File);  
  181.         hMp4File = NULL;  
  182.     }  
  183. }  
  184.   
  185. bool MP4Encoder::WriteH264File(const char* pFile264,const char* pFileMp4)  
  186. {  
  187.     if(pFile264 == NULL || pFileMp4 == NULL)  
  188.     {  
  189.         return false;  
  190.     }  
  191.   
  192.     MP4FileHandle hMp4File = CreateMP4File(pFileMp4,352,288);  
  193.   
  194.     if(hMp4File == NULL)  
  195.     {  
  196.         printf("ERROR:Create file failed!");  
  197.         return false;  
  198.     }  
  199.   
  200.     FILE *fp = fopen(pFile264, "rb");    
  201.     if(!fp)    
  202.     {    
  203.         printf("ERROR:open file failed!");  
  204.         return false;  
  205.     }    
  206.     fseek(fp, 0, SEEK_SET);  
  207.   
  208.     unsigned char *buffer  = new unsigned char[BUFFER_SIZE];  
  209.     int pos = 0;  
  210.     while(1)  
  211.     {  
  212.         int readlen = fread(buffer+pos, sizeof(unsigned char), BUFFER_SIZE-pos, fp);  
  213.   
  214.   
  215.         if(readlen<=0)  
  216.         {  
  217.             break;  
  218.         }  
  219.   
  220.         readlen += pos;  
  221.   
  222.         int writelen = 0;  
  223.         for(int i = readlen-1; i>=0; i--)  
  224.         {  
  225.                 if(buffer[i--] == 0x01 &&  
  226.                     buffer[i--] == 0x00 &&  
  227.                     buffer[i--] == 0x00 &&  
  228.                     buffer[i--] == 0x00  
  229.                     )  
  230.                 {  
  231.                     writelen = i+5;  
  232.                     break;  
  233.                 }  
  234.         }  
  235.           
  236.         writelen = WriteH264Data(hMp4File,buffer,writelen);  
  237.         if(writelen<=0)  
  238.         {  
  239.             break;  
  240.         }  
  241.         memcpy(buffer,buffer+writelen,readlen-writelen+1);  
  242.         pos = readlen-writelen+1;  
  243.     }  
  244.     fclose(fp);  
  245.   
  246.     delete[] buffer;  
  247.     CloseMP4File(hMp4File);  
  248.   
  249.     return true;  
  250. }  
  251.   
  252. bool MP4Encoder:: PraseMetadata(const unsigned char* pData,int size,MP4ENC_Metadata &metadata)  
  253. {  
  254.     if(pData == NULL || size<4)  
  255.     {  
  256.         return false;  
  257.     }  
  258.     MP4ENC_NaluUnit nalu;  
  259.     int pos = 0;  
  260.     bool bRet1 = false,bRet2 = false;  
  261.     while (int len = ReadOneNaluFromBuf(pData,size,pos,nalu))  
  262.     {  
  263.         if(nalu.type == 0x07)  
  264.         {  
  265.             memcpy(metadata.Sps,nalu.data,nalu.size);  
  266.             metadata.nSpsLen = nalu.size;  
  267.             bRet1 = true;  
  268.         }  
  269.         else if((nalu.type == 0x08))  
  270.         {  
  271.             memcpy(metadata.Pps,nalu.data,nalu.size);  
  272.             metadata.nPpsLen = nalu.size;  
  273.             bRet2 = true;  
  274.         }  
  275.         pos += len;  
  276.     }  
  277.     if(bRet1 && bRet2)  
  278.     {  
  279.         return true;  
  280.     }  
  281.     return false;  
  282. }  


     

7
0
 
 
猜你在找
机器学习之概率与统计推断
机器学习之数学基础
机器学习之凸优化
机器学习之矩阵
响应式布局全新探索
探究Linux的总线、设备、驱动模型
深度学习基础与TensorFlow实践
深度学习之神经网络原理与实战技巧
前端开发在线峰会
TensorFlow实战进阶:手把手教你做图像识别应用
查看评论
8楼  qq_35409547 2016-06-28 15:23发表 [回复]
你好,我想将WriteH264Data中的指针pData里的内容写出来,请问怎么写,在哪儿添加语句?
Re:  linux_day 2017-04-20 11:47发表 [回复]
回复qq_35409547: 你搞好了吗,能读到一个单元的码流吗
7楼  独钓_寒江雪 2016-05-12 11:29发表 [回复]
lz,我转换的MP4文件没有图像显示,问下怎么解决,新手
6楼  tanjibao 2016-04-27 17:35发表 [回复]
楼主,不知道你做过live555MediaServer的流媒体服务中添加MP4格式的转发没?希望求教
5楼  请叫我小清新 2014-10-11 11:26发表 [回复]
为何我调用后使用播放器播放只有两秒就没了?
Re:  linux_day 2017-04-20 13:19发表 [回复]
回复请叫我小清新:代码能用吗,H264的流都写不进去
Re:  hmge 2016-01-23 11:29发表 [回复]
同问啊 我也出现这样问题
4楼  qq529633582 2014-07-15 20:35发表 [回复]
ffmpeg加上-vcodec copy -acodec copy取消编解码过程可以秒转的
3楼  weiwei22844 2014-02-26 10:43发表 [回复]
不错,很好的入门介绍,我已在自己的工程中用上!
2楼  Renuvb 2013-12-02 15:35发表 [回复]
版主你好,我用这个代码进行格式转换时,提示 mp4v2::impl::MP4File::FindTrakAtomIndex: Track id 0 doesn't exist
这个是什么原因呢?
Re:  ybsun2010 2013-12-05 09:38发表 [回复]
回复Renuvb:你好。我也遇到了这个问题。我找到的原因是用x264编码后的h264文件开始有一段x264的版权声明,你用记事本打开该h264文件就能看到了。用其他方式编码的h264就没有版权声明,可以直接转换成功
Re:  齐达内的神话 2014-05-14 22:23发表 [回复]
回复ybsun2010:请问你是如何解决的呢?我也碰到了这个问题,谢谢。
Re:  lcyw 2015-09-28 12:18发表 [回复]
回复齐达内的神话:x264_sei_version_write() 注释掉这个函数
Re:  ybsun2010 2014-05-16 08:41发表 [回复]
回复齐达内的神话:首先,重新编译一下x264库,设置里面的一个参数,就是不要生产版权声明,具体的参数你搜下,我忘了;这样生成的h264文件里面最前面是00 00 00 01 06,紧跟着是sps,你在WriteH264File函数中把 fseek(fp, 0, SEEK_SET); 改为fseek(fp, 5, SEEK_SET);就行了。这样就是从sps开始读了
1楼  ybsun2010 2013-11-14 21:05发表 [回复]
你好,我正在做将h264转换为mp4的项目。请问在vs2010下编译完了mp4v2后怎么加入到你的这个工程中呢?谢谢了




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值