一、H264视频编码成MP4文件
参看:H264视频编码成MP4文件
参看:mp4v2在VS2010下的编译与在项目中的使用
最近做项目需要将H264文件封装为mp4文件,从网上找到了MP4V2库,下载下来后不知道从何下手,官方网站https://code.google.com/p/mp4v2/在windows下的编译过程介绍的很简短,对刚刚开始使用VS2010做大型项目的人来说,实在是有些无力。于是从网上找到了下面几篇博客文章,亲测可用,留下来以备查看。
(1)mp4v2 库在VS2017下的编译过程
注:此段内容参考:编译MP4v2
Mp4v2 一开始是与 mpeg4ip 这个开源项目捆绑在一起的,现在已经脱离mpeg4ip以一个单独的库存在着。Mp4v2 库提供了一些 API 用来创建修改 MP4 文件。
源码在 http://mp4v2.googlecode.com/svn/trunk/,首先新建一个文件夹用来导入源码,创建文件夹后右键 SVN checkout(需要预先在电脑上安装 TortoiseSVN 客户端),在 URL of repository 中填入http://mp4v2.googlecode.com/svn/trunk/ 这个网址,点击ok后导入代码,再把vstudio9.0\libmp4v2\Version.rc 放入 src 文件夹下。
上面的内容其实就是获取 MP4V2 源码。上面两个谷歌网站没有进入,原因你懂的。可从 CSDN 下载。
下载:mp4v2最新源码
然后在进入 vs2013 目录下,用 vs2017 打开 mp4v2.sln 。选择配置为 release win32,成功后,编译 libmp4v2。
在 mp4v2-master\buildwin\lib.vs2013\Win32\Release 下会生成 4 个文件,lbmp4v2.lib、libmp4v2.dll、libmp4v2.pdb、libmp4v2.exp。这样就完成了。
备注:
1. mp4v2 的源码下载需要用到 SVN 客户端 TortoiseSVN,官方网站上能下载的是 Linux 平台下的代码。
Linux下安装编译之前有讲,参看:mp4v2再学习 -- mp4v2 安装说明
2. 此过程在 VS2017 下打开没有问题。
3. 以后在你的项目中会用到 lbmp4v2.lib、libmp4v2.dll 这两个文件。
上面的操作有几处需要注意的:
(1)首先选择配置为 release win32,这个该如何配置?
配置方法:生成->配置管理器,然后将 libmp4v2 配置改为 Release
(2)问题分析
出现 无法打开包括文件: “corecrt.h”
解决方法,参看:VS2015无法打开包括文件corecrt.h
简单来说就是按如下图,把里面的 10.0.15063.0 文件复制一份,重命名成 10.0.10240.0
(2)mp4v2库在VS2010中的使用
注:此处测试代码参考:H264视频编码成MP4文件,作者代码写的很好,赞一个。此处只是把配置过程再详讲一下。
原文如下:
最近需要将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
- class MP4Encoder
- {
- public:
- MP4Encoder(void);
- ~MP4Encoder(void);
- public:
-
- MP4FileHandle CreateMP4File(const char *fileName,int width,int height,int timeScale = 90000,int frameRate = 25);
-
- bool Write264Metadata(MP4FileHandle hMp4File,LPMP4ENC_Metadata lpMetadata);
-
- int WriteH264Data(MP4FileHandle hMp4File,const unsigned char* pData,int size);
-
- void CloseMP4File(MP4FileHandle hMp4File);
-
-
- bool WriteH264File(const char* pFile264,const char* pFileMp4);
-
- static bool PraseMetadata(const unsigned char* pData,int size,MP4ENC_Metadata &metadata);
- };
客户端调用示例代码:
[cpp] view plain copy
- #include <stdio.h>
- #include "MP4Encoder.h"
-
- int main(int argc, char** argv)
- {
- MP4Encoder mp4Encoder;
-
- mp4Encoder.WriteH264File("test.264","test.mp4");
- }
MP4Encoder的完整代码如下:[cpp] view plain copy
-
-
-
-
-
-
- #pragma once
- #include "mp4v2\mp4v2.h"
-
-
- typedef struct _MP4ENC_NaluUnit
- {
- int type;
- int size;
- unsigned char *data;
- }MP4ENC_NaluUnit;
-
- typedef struct _MP4ENC_Metadata
- {
-
- unsigned int nSpsLen;
- unsigned char Sps[1024];
- unsigned int nPpsLen;
- unsigned char Pps[1024];
-
- } MP4ENC_Metadata,*LPMP4ENC_Metadata;
-
- class MP4Encoder
- {
- public:
- MP4Encoder(void);
- ~MP4Encoder(void);
- public:
-
- MP4FileHandle CreateMP4File(const char *fileName,int width,int height,int timeScale = 90000,int frameRate = 25);
-
- bool Write264Metadata(MP4FileHandle hMp4File,LPMP4ENC_Metadata lpMetadata);
-
- int WriteH264Data(MP4FileHandle hMp4File,const unsigned char* pData,int size);
-
- void CloseMP4File(MP4FileHandle hMp4File);
-
-
- bool WriteH264File(const char* pFile264,const char* pFileMp4);
-
- static bool PraseMetadata(const unsigned char* pData,int size,MP4ENC_Metadata &metadata);
- private:
-
- static int ReadOneNaluFromBuf(const unsigned char *buffer,unsigned int nBufferSize,unsigned int offSet,MP4ENC_NaluUnit &nalu);
- private:
- int m_nWidth;
- int m_nHeight;
- int m_nFrameRate;
- int m_nTimeScale;
- MP4TrackId m_videoId;
- };
MP4Encoder.cpp[cpp] view plain copy
-
-
-
-
-
-
- #include "MP4Encoder.h"
- #include <string.h>
-
- #define BUFFER_SIZE (1024*1024)
-
- MP4Encoder::MP4Encoder(void):
- m_videoId(NULL),
- m_nWidth(0),
- m_nHeight(0),
- m_nTimeScale(0),
- m_nFrameRate(0)
- {
- }
-
- MP4Encoder::~MP4Encoder(void)
- {
- }
-
- MP4FileHandle MP4Encoder::CreateMP4File(const char *pFileName,int width,int height,int timeScale,int frameRate)
- {
- if(pFileName == NULL)
- {
- return false;
- }
-
- MP4FileHandle hMp4file = MP4Create(pFileName);
- if (hMp4file == MP4_INVALID_FILE_HANDLE)
- {
- printf("ERROR:Open file fialed.\n");
- return false;
- }
- m_nWidth = width;
- m_nHeight = height;
- m_nTimeScale = 90000;
- m_nFrameRate = 25;
- MP4SetTimeScale(hMp4file, m_nTimeScale);
- return hMp4file;
- }
-
- bool MP4Encoder::Write264Metadata(MP4FileHandle hMp4File,LPMP4ENC_Metadata lpMetadata)
- {
- m_videoId = MP4AddH264VideoTrack
- (hMp4File,
- m_nTimeScale,
- m_nTimeScale / m_nFrameRate,
- m_nWidth,
- m_nHeight,
- lpMetadata->Sps[1],
- lpMetadata->Sps[2],
- lpMetadata->Sps[3],
- 3);
- if (m_videoId == MP4_INVALID_TRACK_ID)
- {
- printf("add video track failed.\n");
- return false;
- }
- MP4SetVideoProfileLevel(hMp4File, 0x01);
-
-
- MP4AddH264SequenceParameterSet(hMp4File,m_videoId,lpMetadata->Sps,lpMetadata->nSpsLen);
-
-
- MP4AddH264PictureParameterSet(hMp4File,m_videoId,lpMetadata->Pps,lpMetadata->nPpsLen);
-
- return true;
- }
-
- int MP4Encoder::WriteH264Data(MP4FileHandle hMp4File,const unsigned char* pData,int size)
- {
- if(hMp4File == NULL)
- {
- return -1;
- }
- if(pData == NULL)
- {
- return -1;
- }
- MP4ENC_NaluUnit nalu;
- int pos = 0, len = 0;
- while (len = ReadOneNaluFromBuf(pData,size,pos,nalu))
- {
- if(nalu.type == 0x07)
- {
-
- m_videoId = MP4AddH264VideoTrack
- (hMp4File,
- m_nTimeScale,
- m_nTimeScale / m_nFrameRate,
- m_nWidth,
- m_nHeight,
- nalu.data[1],
- nalu.data[2],
- nalu.data[3],
- 3);
- if (m_videoId == MP4_INVALID_TRACK_ID)
- {
- printf("add video track failed.\n");
- return 0;
- }
- MP4SetVideoProfileLevel(hMp4File, 1);
-
- MP4AddH264SequenceParameterSet(hMp4File,m_videoId,nalu.data,nalu.size);
- }
- else if(nalu.type == 0x08)
- {
- MP4AddH264PictureParameterSet(hMp4File,m_videoId,nalu.data,nalu.size);
- }
- else
- {
- int datalen = nalu.size+4;
- unsigned char *data = new unsigned char[datalen];
-
- data[0] = nalu.size>>24;
- data[1] = nalu.size>>16;
- data[2] = nalu.size>>8;
- data[3] = nalu.size&0xff;
- memcpy(data+4,nalu.data,nalu.size);
- if(!MP4WriteSample(hMp4File, m_videoId, data, datalen,MP4_INVALID_DURATION, 0, 1))
- {
- return 0;
- }
- delete[] data;
- }
-
- pos += len;
- }
- return pos;
- }
-
- int MP4Encoder::ReadOneNaluFromBuf(const unsigned char *buffer,unsigned int nBufferSize,unsigned int offSet,MP4ENC_NaluUnit &nalu)
- {
- int i = offSet;
- while(i<nBufferSize)
- {
- if(buffer[i++] == 0x00 &&
- buffer[i++] == 0x00 &&
- buffer[i++] == 0x00 &&
- buffer[i++] == 0x01
- )
- {
- int pos = i;
- while (pos<nBufferSize)
- {
- if(buffer[pos++] == 0x00 &&
- buffer[pos++] == 0x00 &&
- buffer[pos++] == 0x00 &&
- buffer[pos++] == 0x01
- )
- {
- break;
- }
- }
- if(pos == nBufferSize)
- {
- nalu.size = pos-i;
- }
- else
- {
- nalu.size = (pos-4)-i;
- }
-
- nalu.type = buffer[i]&0x1f;
- nalu.data =(unsigned char*)&buffer[i];
- return (nalu.size+i-offSet);
- }
- }
- return 0;
- }
-
- void MP4Encoder::CloseMP4File(MP4FileHandle hMp4File)
- {
- if(hMp4File)
- {
- MP4Close(hMp4File);
- hMp4File = NULL;
- }
- }
-
- bool MP4Encoder::WriteH264File(const char* pFile264,const char* pFileMp4)
- {
- if(pFile264 == NULL || pFileMp4 == NULL)
- {
- return false;
- }
-
- MP4FileHandle hMp4File = CreateMP4File(pFileMp4,352,288);
-
- if(hMp4File == NULL)
- {
- printf("ERROR:Create file failed!");
- return false;
- }
-
- FILE *fp = fopen(pFile264, "rb");
- if(!fp)
- {
- printf("ERROR:open file failed!");
- return false;
- }
- fseek(fp, 0, SEEK_SET);
-
- unsigned char *buffer = new unsigned char[BUFFER_SIZE];
- int pos = 0;
- while(1)
- {
- int readlen = fread(buffer+pos, sizeof(unsigned char), BUFFER_SIZE-pos, fp);
-
-
- if(readlen<=0)
- {
- break;
- }
-
- readlen += pos;
-
- int writelen = 0;
- for(int i = readlen-1; i>=0; i--)
- {
- if(buffer[i--] == 0x01 &&
- buffer[i--] == 0x00 &&
- buffer[i--] == 0x00 &&
- buffer[i--] == 0x00
- )
- {
- writelen = i+5;
- break;
- }
- }
-
- writelen = WriteH264Data(hMp4File,buffer,writelen);
- if(writelen<=0)
- {
- break;
- }
- memcpy(buffer,buffer+writelen,readlen-writelen+1);
- pos = readlen-writelen+1;
- }
- fclose(fp);
-
- delete[] buffer;
- CloseMP4File(hMp4File);
-
- return true;
- }
-
- bool MP4Encoder:: PraseMetadata(const unsigned char* pData,int size,MP4ENC_Metadata &metadata)
- {
- if(pData == NULL || size<4)
- {
- return false;
- }
- MP4ENC_NaluUnit nalu;
- int pos = 0;
- bool bRet1 = false,bRet2 = false;
- while (int len = ReadOneNaluFromBuf(pData,size,pos,nalu))
- {
- if(nalu.type == 0x07)
- {
- memcpy(metadata.Sps,nalu.data,nalu.size);
- metadata.nSpsLen = nalu.size;
- bRet1 = true;
- }
- else if((nalu.type == 0x08))
- {
- memcpy(metadata.Pps,nalu.data,nalu.size);
- metadata.nPpsLen = nalu.size;
- bRet2 = true;
- }
- pos += len;
- }
- if(bRet1 && bRet2)
- {
- return true;
- }
- return false;
- }
其实讲 ffmpeg 时就已经讲了VS 开发环境搭建。参看:ffmpeg再学习 -- Windows下安装说明
好吧,那我再讲一遍。。
(1)新建项目
打开 VS;文件->新建->项目->Win32控制台应用程序->选择空项目,点击完成。注意,选择的位置最好不要有 空格或者汉字。
(2)拷贝考法文件
头文件( *.h)拷贝至项目文件夹的 include 子文件夹下
导入库文件( *.lib)拷贝至项目文件夹的 lib 子文件夹下
动态库文件( *.dll) 拷贝至项目文件夹下
这三组文件具体是,mp4v2-master文件夹下的 include 文件夹 和 mp4v2-master\buildwin\lib.vs2013\Win32\Release 文件夹下的 libmp4v2.lib 和 libmp4v2.dll
点击右键,选择在资源管理器中打开文件夹,进入项目目录。(注意,如果手动进入注意文件夹位置,我就是没找好位置,试了半天最后才发现,将上面的这些文件拷贝到错误的文件夹下了)
放置完成即下图:(3)添加代码右击 MP4Encoder 工程,添加->类
双击 C++类 进入 一般 C++类向导,类名写为 CMP4Encoder ,然后把上面 MP4Encoder.h 和MP4Encoder.cpp 的代码拷贝到对应的文件中。
右击工程 MP4Encoder ->添加->新建项
选择C++文件,名称写做 main.cpp,位置默认即可,然后将上面的“客户端调用示例代码”拷贝到此文件。
(4)配置开发文件打开属性面板
解决方案资源管理器->右键单击项目->属性
头文件配置
配置属性->C/C++->常规->附加包含目录,输入“ include”(刚才拷贝头文件的目录)
导入库配置
配置属性->链接器->常规->附加库目录,输入“ lib” (刚才拷贝库文件的目录)
配置属性->链接器->输入->附加依赖项,输入 libmp4v2.lib;
动态库不用配置
(5)测试
首先拷贝一个 H264 测试文件 (后缀为.264) 到 MP4Encoder 文件夹下。
测试文件可以从此处,下载:[开源世界]分享H.264视频文件下载地址
注意,需要将该264文件,重命名为 test.h264,否则生成的 MP4 文件大小为1K。
或者你更改主函数:
然后点击本地Windows调试器,出现此项目已经过期,选择 是。
即可生成 test.mp4 文件,可以用VLC播放器打开看看。
不过生成文件时会出现,无法查找或打开 PDB 文件。如下图:
它对于生成文件没有影响的。如果看着不舒服,可参看:无法查找或打开 PDB 文件解决办法
这是我写的工程文件:MP4Encoder 工程文件
二、工程文件(可用)
讲了这么多,你发现没有上面这个例子只是将 H264 视频编码成MP4文件,缺少音频啊亲。
下面这个即音视频编码转MP4文件的例子,下载:MP4v2 -- h264 转 MP4