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 plaincopy
  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 plaincopy
  1. #include <stdio.h>
  2. #include "MP4Encoder\MP4Encoder.h"
  3. int main(int argc, char** argv)
  4. {
  5. MP4Encoder mp4Encoder;
  6. // convert H264 file to mp4 file
  7. mp4Encoder.WriteH264File("test.264","test.mp4");
  8. }

MP4Encoder完整的代码如下:

[cpp] view plaincopy
  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. // NALU单元
  10. typedef struct _MP4ENC_NaluUnit
  11. {
  12. int type;
  13. int size;
  14. unsigned char *data;
  15. }MP4ENC_NaluUnit;
  16. typedef struct _MP4ENC_Metadata
  17. {
  18. // video, must be h264 type
  19. unsigned int    nSpsLen;
  20. unsigned char   Sps[1024];
  21. unsigned int    nPpsLen;
  22. unsigned char   Pps[1024];
  23. } MP4ENC_Metadata,*LPMP4ENC_Metadata;
  24. class MP4Encoder
  25. {
  26. public:
  27. MP4Encoder(void);
  28. ~MP4Encoder(void);
  29. public:
  30. // open or creat a mp4 file.
  31. MP4FileHandle CreateMP4File(const char *fileName,int width,int height,int timeScale = 90000,int frameRate = 25);
  32. // wirte 264 metadata in mp4 file.
  33. bool Write264Metadata(MP4FileHandle hMp4File,LPMP4ENC_Metadata lpMetadata);
  34. // wirte 264 data, data can contain  multiple frame.
  35. int WriteH264Data(MP4FileHandle hMp4File,const unsigned char* pData,int size);
  36. // close mp4 file.
  37. void CloseMP4File(MP4FileHandle hMp4File);
  38. // convert H264 file to mp4 file.
  39. // no need to call CreateMP4File and CloseMP4File,it will create/close mp4 file automaticly.
  40. bool WriteH264File(const char* pFile264,const char* pFileMp4);
  41. // Prase H264 metamata from H264 data frame
  42. static bool PraseMetadata(const unsigned char* pData,int size,MP4ENC_Metadata &metadata);
  43. private:
  44. // read one nalu from H264 data buffer
  45. static int ReadOneNaluFromBuf(const unsigned char *buffer,unsigned int nBufferSize,unsigned int offSet,MP4ENC_NaluUnit &nalu);
  46. private:
  47. int m_nWidth;
  48. int m_nHeight;
  49. int m_nFrameRate;
  50. int m_nTimeScale;
  51. MP4TrackId m_videoId;
  52. };

MP4Encoder.cpp

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

7
0
  • 上一篇H264视频通过RTMP直播
  • 下一篇Apache用户名和密码验证
相关文章推荐
  • • H264视频编码成MP4文件
  • • Android 的视频编码 H263 MP4V H264
  • • 文章【Android 的视频编码 H263 MP4V H264】的代码实现
  • • 视频编码与封装方式详解
  • • mp4v2再学习 -- H264视频编码成MP4文件(转)
  • • H264编码 封装成MP4格式 视频流 RTP封包
  • • 各种音视频编解码学习详解 h264 ,mpeg4 ,aac 等所有音视频格式
  • • 音视频编解码 文件格式 协议内容详解
  • • h264视频编码的MP4格式视频无法在网页中的adobe flash player中播放
  • • Mp4v2实现h264+aac打包成Mp4视频文件

猜你在找
机器学习之概率与统计推断
机器学习之数学基础
机器学习之凸优化
机器学习之矩阵
响应式布局全新探索
探究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后怎么加入到你的这个工程中呢?谢谢了

H264视频编码成MP4文件相关推荐

  1. mp4v2再学习 -- H264视频编码成MP4文件

    一.H264视频编码成MP4文件 参看:H264视频编码成MP4文件 参看:mp4v2在VS2010下的编译与在项目中的使用 最近做项目需要将H264文件封装为mp4文件,从网上找到了MP4V2库,下 ...

  2. 使用mp4v2将aac音频h264视频数据封装成mp4开发心得

    这阵子在捣鼓一个将游戏视频打包成本地可播放文件的模块.开始使用avi作为容器,弄了半天无奈avi对aac的支持实在有限,在播放时音视频时无法完美同步. 关于这点avi文档中有提到: For AAC, ...

  3. 工具---《.264视频 转成 MP4视频》

    <.264视频 转成 MP4视频> 安装了"爱奇艺万能播放器"可以打开.264视频,但是opencv却不能直接读取.264视频,还是需要想办法".264视频 ...

  4. 【Python网络爬虫实战篇】使用selenium+requests爬取下载高清源视频:关于爬取m3u8文件链接解析为ts视频合并成mp4视频的分析实战

    这两天博主在摸鱼时,偶然间接触到了流媒体的概念,一时间来了兴致.再加上之前博主有着七.八年的视频制作经验,深知视频素材获取的不易.因此,打算利用自己所学的python网络爬虫的知识,通过编写代码实现获 ...

  5. 怎么把video文件改成mp4_如何把视频转换成mp4格式?

    要想把视频转换成mp4格式,这就看你原视频是什么格式了.这里推荐一个视频格式转换类型比较齐全的一款. 烁光视频转换器烁光视频转换器​www.ivideotools.com 专业视频格式转换器,支持视频 ...

  6. 怎么把video文件改成mp4_腾讯视频文件怎么转换格式_如何把腾讯视频转换成mp4格式-win7之家...

    视频软件不一样,视频的格式也会不一样,比如qlv视频格式是腾讯视频的独有格式,想要观看qlv格式的视频文件,就只能下载腾讯视频播放器,但是这样显得有点麻烦,其实我们也可以对腾讯视频文件转换格式,比如转 ...

  7. mov格式的视频转换成mp4,教你3种快速方法来处理

    怎么把mov格式的视频转换成mp4?有这样疑问的朋友,我相信不在少数.为什么这么说呢?因为现在是短视频时代,人们接触视频的机会非常多,当然也会遇到mov格式.MOV格式:可以用QuickTime,包括 ...

  8. b站电脑客户端_如何将B站的flv格式的视频转换成mp4格式

    经常看到B站有精彩的视频片段,于是想把这些视频下载保存到电脑,但是发现没有下载按钮,是不是很悲催.有些时候想从优酷.土豆网这些视频网站下载视频,结果却提示要先下载视频客户端才能继续下载视频,运气差的话 ...

  9. 如何快速不借用转换工具将FLV格式视频转换成MP4

    FLV流媒体格式是一种新的视频格式,全称为Flash Video.它的出现有效地解决了视频文件导入Flash后,使导出的SWF文件体积庞大,所以就引出了FLV格式,主要特点就是体积小 适合作为流媒体在 ...

最新文章

  1. 报错android.view.InflateException: Binary XML file line #11: Attempt to invoke virtual method 'boolean
  2. java子类对象不能调用父类protected方法和域的原因。
  3. C++ Primer 5th笔记(chap 15 OOP)继承中的类作用域
  4. 从Q3财报看百度营销成长
  5. win7 64 iis7+access ADODB.Connection 错误 '800a0e7a'
  6. win和linux创建共享文件夹,在Windows和Linux下(Debian、Ubuntu)创建共享文件夹
  7. python计算n到n+100之间所有奇数的和_Python基本操作题
  8. 《VMware vSphere设计(原书第2版)》——1.1 什么是设计
  9. okhttp3 请求html页面,OkHttp3源码详解(二) 整体流程
  10. LeetCode 673. 最长递增子序列的个数(DP)
  11. 轨迹相似性度量方法总结
  12. 沉痛哀悼在汶川特大地震中的遇难者!!!!!
  13. 月薪20k的web前端开发程序员,他们都会的这6招
  14. Facebook的秘密服务器,竟藏着互联网的军事根源?
  15. Arcgis server——arcgis server manager忘记密码
  16. 一个类似京东商城那种多条件筛选效果
  17. 联想Y9000P安装Ubuntu20.04记录
  18. Web前端的优点有哪些?为什么Web前端可以如此火爆?
  19. 压力测试软件 loadr,初学abench压力测试 - 玄大冰 - 51Testing软件测试网 51Testing软件测试网-软件测试人的精神家园...
  20. python连乘函数_(Python3) 连加 连乘 代码

热门文章

  1. zip文件加密、解密解压实操,附带文件上传安全检查
  2. js 对象.key和对象[key]区别
  3. Python的数据类型3-列表list
  4. Hive 分支判断 case函数 if函数
  5. 在Ubuntu中安装Pycharm轻松搞定
  6. Centos6.8无法设置输入法
  7. centos 7 升级 sudo
  8. Excel文件的导出操作
  9. python执行py文件生成pyc_python生成pyc文件
  10. js 手机号、姓名、身份证号脱敏(打星号)