技术标签: Audio/Video
最近做项目需要将H264文件封装为mp4文件,从网上找到了MP4V2库,下载下来后不知道从何下手,官方网站https://code.google.com/p/mp4v2/在windows下的编译过程介绍的很简短,对刚刚开始使用VS2010做大型项目的人来说,实在是有些无力。于是从网上找到了下面几篇博客文章,亲测可用,留下来以备查看。
注:此段内容参考自http://blog.csdn.net/elinchen88/article/details/7987284
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文件夹下,然后在进入vstudio9.0目录下,用vs2008打开mp4v2.sln进行编译。编译libmp4v2,选择配置为release win32,成功后,在bin\Windows-Win32\Release下会生成4个文件,lbmp4v2.lib、libmp4v2.dll、libmp4v2.pdb、libmp4v2.exp。这样就完成了。
备注:
1.mp4v2的源码下载需要用到SVN客户端TortoiseSVN,官方网站上能下载的是linux平台下的代码。
2.此过程在VS2010下打开没有问题。
3.以后在你的项目中会用到lbmp4v2.lib、libmp4v2.dll这两个文件。
注:此处测试代码参考自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文件。
class MP4Encoder
{
public:
MP4Encoder(void);
~MP4Encoder(void);
public:
// open or creat a mp4 file.
MP4FileHandle CreateMP4File(const char *fileName,int width,int height,int timeScale = 90000,int frameRate = 25);
// wirte 264 metadata in mp4 file.
bool Write264Metadata(MP4FileHandle hMp4File,LPMP4ENC_Metadata lpMetadata);
// wirte 264 data, data can contain multiple frame.
int WriteH264Data(MP4FileHandle hMp4File,const unsigned char* pData,int size);
// close mp4 file.
void CloseMP4File(MP4FileHandle hMp4File);
// convert H264 file to mp4 file.
// no need to call CreateMP4File and CloseMP4File,it will create/close mp4 file automaticly.
bool WriteH264File(const char* pFile264,const char* pFileMp4);
// Prase H264 metamata from H264 data frame
static bool PraseMetadata(const unsigned char* pData,int size,MP4ENC_Metadata &metadata);
};
#include <stdio.h>
#include "MP4Encoder\MP4Encoder.h"
int main(int argc, char** argv)
{
MP4Encoder mp4Encoder;
// convert H264 file to mp4 file
mp4Encoder.WriteH264File("test.264","test.mp4");
}
1)MP4Encoder.h
/********************************************************************
filename: MP4Encoder.h
created: 2013-04-16
author: firehood
purpose: MP4编码器,基于开源库mp4v2实现(https://code.google.com/p/mp4v2/)。
*********************************************************************/
#pragma once
#include "mp4v2\mp4v2.h"
// NALU单元
typedef struct _MP4ENC_NaluUnit
{
int type;
int size;
unsigned char *data;
}MP4ENC_NaluUnit;
typedef struct _MP4ENC_Metadata
{
// video, must be h264 type
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:
// open or creat a mp4 file.
MP4FileHandle CreateMP4File(const char *fileName,int width,int height,int timeScale = 90000,int frameRate = 25);
// wirte 264 metadata in mp4 file.
bool Write264Metadata(MP4FileHandle hMp4File,LPMP4ENC_Metadata lpMetadata);
// wirte 264 data, data can contain multiple frame.
int WriteH264Data(MP4FileHandle hMp4File,const unsigned char* pData,int size);
// close mp4 file.
void CloseMP4File(MP4FileHandle hMp4File);
// convert H264 file to mp4 file.
// no need to call CreateMP4File and CloseMP4File,it will create/close mp4 file automaticly.
bool WriteH264File(const char* pFile264,const char* pFileMp4);
// Prase H264 metamata from H264 data frame
static bool PraseMetadata(const unsigned char* pData,int size,MP4ENC_Metadata &metadata);
private:
// read one nalu from H264 data buffer
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;
};
/********************************************************************
filename: MP4Encoder.cpp
created: 2013-04-16
author: firehood
purpose: MP4编码器,基于开源库mp4v2实现(https://code.google.com/p/mp4v2/)。
*********************************************************************/
#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/* = 90000*/,int frameRate/* = 25*/)
{
if(pFileName == NULL)
{
return false;
}
// create mp4 file
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, // width
m_nHeight,// height
lpMetadata->Sps[1], // sps[1] AVCProfileIndication
lpMetadata->Sps[2], // sps[2] profile_compat
lpMetadata->Sps[3], // sps[3] AVCLevelIndication
3); // 4 bytes length before each NAL unit
if (m_videoId == MP4_INVALID_TRACK_ID)
{
printf("add video track failed.\n");
return false;
}
MP4SetVideoProfileLevel(hMp4File, 0x01); // Simple Profile @ Level 3
// write sps
MP4AddH264SequenceParameterSet(hMp4File,m_videoId,lpMetadata->Sps,lpMetadata->nSpsLen);
// write pps
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) // sps
{
// 添加h264 track
m_videoId = MP4AddH264VideoTrack
(hMp4File,
m_nTimeScale,
m_nTimeScale / m_nFrameRate,
m_nWidth, // width
m_nHeight, // height
nalu.data[1], // sps[1] AVCProfileIndication
nalu.data[2], // sps[2] profile_compat
nalu.data[3], // sps[3] AVCLevelIndication
3); // 4 bytes length before each NAL unit
if (m_videoId == MP4_INVALID_TRACK_ID)
{
printf("add video track failed.\n");
return 0;
}
MP4SetVideoProfileLevel(hMp4File, 1); // Simple Profile @ Level 3
MP4AddH264SequenceParameterSet(hMp4File,m_videoId,nalu.data,nalu.size);
}
else if(nalu.type == 0x08) // pps
{
MP4AddH264PictureParameterSet(hMp4File,m_videoId,nalu.data,nalu.size);
}
else
{
int datalen = nalu.size+4;
unsigned char *data = new unsigned char[datalen];
// MP4 Nalu前四个字节表示Nalu长度
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;
}
1) 在MP4v2源代码中有一个vstudio9.0目录,用VS2010打开mp4v2.sln,然后新建一个项目,项目类型选Win32控制台应用程序,名称可以写为Test264ToMp4,解决方案选择“添加到解决方案”即可。这样在vstudio9.0目录下,就会多了一个文件夹Test264ToMp4(当然,也可以新打开一个VS2010,新建个名字叫做Test264Mp4的工程,对应的解决方案名字自动为Test264ToMp4,只要在3)中配置好include、lib、dll即可)。
2) 在VS2010中,右击Test64ToMp4工程,添加->类,类名写为 CMP4Encoder,然后把上面MP4Encoder.h和MP4Encoder.cpp的代码拷贝到对应的文件中,main函数所在的文件需要重新写一个cpp,可起名为Test.cpp。步骤为:右击工程Test264ToMp4->添加->新建项,选择C++文件,名称写做Test.cpp,位置默认即可,然后将上面的“客户端调用示例代码”拷贝到此文件。
3)关键部分来了。将MP4V2类库添加到Test264ToMp4工程。
首先添加.h目录。需要将MP4V2开源软件包里面的include目录和vstudio9.0目录下的include目录添加到“附加包含目录”项中。如图所示:
此处最好路径位置最好用相对路径来代替,由于自己对这个还不怎么熟悉,先用绝对路径吧。
然后添加libmp4v2.lib
方法:右击Test264ToMp4->属性->配置属性->链接器->常规,在“附加库目录”里点“编辑”,把第1步中编译好的libmp4v2.lib库所在的路径加上,如图:
然后点击链接器下的“输入”,在“附加依赖项”中点“编辑”,写上libmp4v2.lib,如图:
最后,将libmp4v2.dll拷贝到Test264ToMp4目录下即可。
首先拷贝一个H264测试文件(后缀为.264)到Test264ToMp4文件夹下,测试文件可以从此处下载(http://blog.163.com/[email protected]/blog/static/68117214201381485753386/)。右击Test264ToMp4,生成,然后运行即可生成.mp4文件,可以用VLC播放器打开看看。
当要调用开源项目的库时,最重要的三个东西分别是include目录(里面存放了所需的.h文件)、编译后的动态链接库(.dll文件)和编译后的静态链接库文件(.lib)文件,只要这三个地方配好了,一般就能用了。当VS编译时,报错如找不到.h文件之类,就得看是不是include路径没写对,当出现link错误时,就得看.lib路径是否没写对。
前言同事买到的光敏模块和以前用的不一样,他用万用表量来量去的,看的挺繁琐。写个测试程序,来测试光敏模块的数值和光线的关系。实验环境硬件平台原子探索者407硬件资料和接线见main.c实验环境硬件快照实验工程下载点稍后上传实验预览前几天,搭了一个ucOS-III的模板,就在这个模板中做实验。全部新加的实现,都搬到了main.c.// @file \src\test_ca...
Python是用于web开发、机器学习和复杂数据分析的通用解释性编程。python对于初学者来说是一种完美的语言,因为它易于学习和理解,而且随着这种语言的流行,python程序员的机会也在不断增加。很多小伙伴开始学习python,那么,python培训班要多少钱?Python语言设计简洁,很受程序员的喜爱,究其原因:就是我们身处一个快消时代,IT圈必然的选择。因为同样一个功能,其他语言需要写100...
Python是一个脚本语言,被解释器解释执行。它的发布方式: .py文件:对于开源项目或者源码没那么重要的,直接提供源码,需要使用者自行安装Python并且安装依赖的各种库。(Python官方的各种安装包就是这样做的) .pyc文件:有些公司或个人因为机密或者各种原因,不愿意源码被运行者看到,可以使用pyc文件发布,pyc文件是Python解释器可以识别的二进制码,故发布后也是跨平台的,需要使用者安装相应版本的Python和依赖库。 可执行文件:对于非码农用户或者一些小白用户,你让
做了这么久性能相关的工作,也接触了不少模块,说实话要做好性能这一块,真心不容易.为什么这么说? 是因为需要接触的知识实在是太多了, Android 是一个整体,牵一发而动全身,不是说只懂一个模块就可以做好在学习的过程中,除了看源码,我还接触到了很多互联网上已有的知识,各位前辈们,将他们的知识和经验倾囊相授,让我少走了很多弯路. 我在自己的笔记里面存了很多很优秀的技术文章和技术文档,现在我决定将这些放到网上,让每一个想进入 Android 系统开发和优化这个领域的人,能通过阅读这篇文章,快速入门. 同时也算
前言 在上一篇导出文档番外篇中,我们已经熟悉了怎样根据json数据导出word的文档,生成接口文档,而在这一篇,将对上一篇进行完善补充,增加多种导出方式,实现更加完善的导出功能。回顾 1. 获取Swagger接口文档的Json文件 2. 解析Json文件数据填充到Html的表格中 3.根据生成的html转work文档功能开始根据生成的html转work文档 /// <summary> /// 静态页面转文件 ///...
与普通消费级平台的 ATX 单槽主板相比,双路服务器配置往往需要动用扩展 ATX(简称 E-ATX)、甚至更大的 SSB-EEB 板型。有趣的是,超微(Supermicro)刚刚为英特尔最新的第三代至强可扩展处理器(Ice Lake-SP)平台,推出了具有“小巧”ATX 板型的 X12DPL 系列双路主板。X12DPL-i6据悉,超微 X12DPL-i6 和 X12DPL-NT 6 主板均采用了 ...
最近做的项目,遇到了的情境:要每个日志按固定大小生成,超过设定大大小就生成新的日志文件,同时在文件名字后面加上日期,并自动按照设置的保留天数保留日志,过期的日志自动删除。然而,log4j自带的生成日志的几个方法,可以按照日期时间生成日志,也可以按照设置的大小滚动生成日志,就是没有即按照大小生成,又在日志名字后面加上日期,同时又清除过期日志的方法。看了源码,决定综合重新写一个类,实现这些需求。
PyTorch-Geometric安装教程
内容提要引言1. S32 SDK的软件分层架构介绍1.1 外设驱动层(PD--Periperal Driver)1.2 外设抽象层(PAL--Peripheral Abstract Layer)1.3 中间件层(Middleware)1.4 实时操作系统层(RTOS)1.5 S32 SDK底层驱动软件分层的优点...
SVN分支切换步骤1:切换到svn 代码更新设置步骤二:选择需要切换到那个分支步骤三:点击ok进行切换,不报错的话就切换成功。。SVN代码合并步骤一:在分支项目中切换到主干项目代码。步骤二:打开 version control 面板步骤三:选择合并的过来的,提交代码版本。...
我在侧面板上有以下表格的表格:onetwothreefour当有人点击该列的某一行时,该行的标题将作为参数传递给在主面板中显示搜索结果的函数。$("#content-display").on('click', 'tr', function (){searchResults($(this).attr('title'));});该行的标题是获取请求中使用的搜索词function searchResul...
在线ocr文字识别软件哪个好?楼主给你说哦!其实没有必要咋先ocr文字识别的,可以使用专业的第三方软件来进行ocr文字识别的。识别的效果也是很不错的,准确率达到97%,甚至更高的,建议尝试一下。在线和线下无非多了一个下载过程,其他算起来还是使用专业的软件比较方便!图片文字识别是怎么在线识别出来的?哪个软件好用?在云便签中可以添加图片,识别图片中的文字1、首先打开云便签后,点击时钟图标,然后在内容编...