.264视频文件封装成.MP4方法

.264视频文件封装成.MP4方法

需求:

海康威视输出的视频格式为.264格式,而html5端对其不支持,所以需要将其封装成. mp4格式。

Tips:我们常常提到的. mp4格式视频,其实指的是一种容器(或者说集合体),包括视频、音频,甚至是字幕等。而.264是指一种视频的编码方式,起压缩作用。所以将.264文件转换成.mp4文件,其实就是一个解码的过程。

思路:

网上已经提供解决方案,采用ffmpeg库,先将.264文件解码,再编码生成.mp4文件,但这种方式效率较低,10M的视频可能需要几秒钟才能完成。另一种方式根据MP4文件协议直接将H264包封装成MP4格式,由于是直接基于MP4的封装,因而效率很高。在Google Code上找到一个开源的MP4编解码库Mp4v2(https://code.google.com/p/mp4v2/),通过Mp4v2可以很方便的将H264编码成MP4格式文件。为了方便使用,基于该库封装了一个MP4Encoder类。

但明显看出上述解决方案采用的是C++语言编写,要想在C#代码中实现对其进行调用,有两种解决方案:① webservice ,② 动态链接库(dll)。由于C++编写webservice较为困难(涉及到套接字等),故本文采用了生成dll方式,但方法的缺点是,即只要源代码做改动,就得重新生成一遍,好在本文涉及的C++程序不需要做变动。

利用Swig工具可以很方便地实现上述生成dll需求。SWIG是个帮助使用C或者C++编写的软件能与其它各种高级编程语言进行嵌入联接的开发工具。SWIG能应用于各种不同类型的语言包括常用脚本编译语言例如Perl, PHP, Python, Tcl, Ruby and PHP。支持语言列表中也包括非脚本编译语言,例如C#, Common Lisp (CLISP, Allegro CL, CFFI, UFFI), Java, Modula-3, OCAML以及R,甚至是编译器或者汇编的计划应用(Guile, MzScheme, Chicken)。SWIG普遍应用于创建高级语言解析或汇编程序环境,用户接口,作为一种用来测试C/C++或进行原型设计的工具。SWIG还能够导出XML或Lisp s-expressions格式的解析树。SWIG可以被自由使用,发布,修改用于商业或非商业中。

实现过程:

  1. 1.   C++实现格式转换的具体过程:

新建C++ win32空项目DLL,如”videoWrapCPlus”,

选择“DLL”应用类型:

新建“MP4Encoder”类:

修改MP4Encoder.h文件,代码如下:

/********************************************************************

filename:   MP4Encoder.h

created:    2016-04-14

author:     TangSir

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;

};

修改MP4Encoder.cpp文件,代码如下:

/********************************************************************

filename:   MP4Encoder.cpp

created:    2016-04-14

author:     TangSir

purpose:    MP4编码器,基于开源库mp4v2实现(https://code.google.com/p/mp4v2/)。

*********************************************************************/

#include "stdafx.h"

#include "MP4Encoder.h"

#include <string.h>

#pragma comment(lib,"../lib/libmp4v2.lib")

#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;

}

注意:代码中引入了编解码库Mp4v2的头文件和lib文件,可从页面(可能需要FQ)https://code.google.com/p/mp4v2/中下载:

分别将下载包中的相关lib链接库和头文件拷贝到C++工程(具体位置跟C++代码中引用路径想关联)中,其中lib库在下载包lib文件夹下,头文件在inc文件夹下:

编译工程。

  1. 2.   利用Swig工具进行dll生成

下载Swig文件,Swing的使用方式大概有两种,一种是配置Vs对外部工具,一种是配置环境变量通过Cmd命令。前一种方法比较简单,Vs2012配置如图。

其中Title为外部工具名称,Command配置为swig.exe的地址,下面两个按图中配置。这样swig即配置完成,接下来是该工具的使用。

在C++工程根路径上添加.i文件,如test.i,编写内容如下:

%module videoWrapCPlus

%include <windows.i>

%{

#include "MP4Encoder.h"

%}

%include "MP4Encoder.h"

其中第一行module名需要与C++的dll名称一致。

此时,在当前页面的状态下,点击工具栏中刚刚配置的swig工具:

会在videoWrapCPlus工程目录下生成几个文件:

其中以cs为后缀名的文件为C#类型文件。接着在C++项目中添加test_wrap.cxx,并编译整个工程,以生成dll动态链接库。整个C++工程结构图如下:

  1. 3.   C#工程对dll文件的调用

新建C#工程,以控制台为例,并将刚才生成的C#类文件拷贝到工程根目录下:

另外,需要将生成的dll文件和libmp4v2.dll(可从之前Mp4v2网站上下载)和拷贝到C#工程运行目录下(若直接添加引用会报错):

最后,可编写测试函数,实现C#中对.264文件到.mp4文件的转换过程:

其中WriteH264File函数的第一个参数为.264文件地址,第二个参数为目标文件地址(如有中文,需要UTF8编码,建议使用英文路径)。

--2016/4/14 于创意城

时间: 2024-10-16 23:02:31

.264视频文件封装成.MP4方法的相关文章

qsv视频如何转换成mp4格式?

我们通常都需要将这些qsv格式的视频转换成mp4格式,那么下面就让小编给大家简单介绍一下. 步骤一:需要将qsv格式的视频文件转换成MP4格式,我们需要给准备好qsv格式的视频文件,然后通过在浏览器上的搜索迅捷PDF在线转换器进入到在线网站:步骤二:在首页的导航栏中找到音视频转换中的qsv转mp4功能点击进入即可:步骤三:进入之后就可以进行qsv格式的文件选择,将qsv格式的文件选择到界面中去,然后进行打开即可:步骤四:视频文件转换是需要一定的时间的,我们可以耐心的等待一段时间,等视频文件转换完

使用GPAC将h.265文件转换成mp4和ts文件

在GPAC官网下下载程序并安装,支持以下操作系统: Windows, Windows Mobile, Linux, GCC+X11 or GCC+SDL, including MacOSX, iOS 4.2, Android 2.1, and Symbian 9 for GPAC <= 0.4.5 你GPAC的安装目录下面有两个exe程序mp4box.exe和mp42ts.exe,还有一个mp4client.exe(用于播放视频文件的程序) 以管理员权限进入GPAC目录 1.将h.265转换成m

C#实现通过ffmpeg从flv视频文件中截图的方法

本文实例讲述了C#实现通过ffmpeg从flv视频文件中截图的方法.分享给大家供大家参考.具体分析如下: 需要先下载ffmpeg,这是开源的,代码如下所示: 代码如下: using System; using System.Configuration; public class PublicMethod:System.Web.UI.Page { public PublicMethod() { } //文件路径 public static string ffmpegtool = "ffmpeg/f

爱奇艺qsv视频文件怎么转为mp4的格式

爱奇艺qsv转mp4?qsv是爱奇艺播放器中的视频播放格式,要想将qsv视频文件在其他播放器中打开的话,比较简单的操作就是转换视频文件的格式了,那么今天为大家带来的是qsv视频文件转为mp4的操作方法,一起来了解下吧.第一步:我们打开电脑中的浏览器搜索"迅捷PDF在线转换器",借助这样一个在线操作工具可以完成qsv转mp4的操作.第二步:在进入工具后,鼠标挪至"音视频转换"的下方选择并点击"QSV转 MP4"功能进入待转换的操作页面.第三步:在待

使用ffmpeg合并视频文件的三种方法

ffmpeg合并视频的方法有三种.国内大多数仅介绍了其中之一.于是觉得有必要翻译一下.其实在ffmpeg的 FAQ文档中有比较详细的说明. 使用concat协议进行视频文件的合并 这种方式的适用场景是:视频容器是MPEG-1, MPEG-2 PS或DV等可以直接进行合并的.换句话说,其实可以直接用cat或者copy之类的命令来对视频直接进行合并.很多文章介绍了这种方法,但适用性却没有提及.这并不是一个通用的方法.典型的命令示例如下: ffmpeg -i concat:"intermediate1

视频转码成mp4格式,添加关键帧,添加元数据,把元数据放在第一帧,可拖动

作者测试是在windows下使用,所以下载的页面地址是: http://ffmpeg.zeranoe.com/builds/点击页面上的Download FFmpeg git-738ebb4 64-bit Static(我是64位的),如果你是32位点击 Download FFmpeg git-738ebb4 32-bit Static下载下来解压缩后我把文件夹改成了ffmpeg放在D盘下面运行三个文件:D:ffmpeg/bin/ffmpeg.exeD:ffmpeg/bin/ffplay.exe

EOS 坑 右击java文件封装成Web Service不弹界面

说到普元 eos很多人不太了解,他是一个封装好的企业快速开发的一个框架.而java人员用的基本都是自己写的或是自己搭建的框架所以这个EOS在很多人印象里没有痕迹 . 在使用的这些年里感觉他是应用了ssh+mybatis做的一个封装了eclipse的一个开发软件 闲话不说了进入正题 1.在简单的eos开发过程中 基本都是简单的 写一个java文件写一个方法之后操作步骤如下: 选择java文件->右击文件->在菜单中选择“封装成Web Service”->在弹出的输入框输入一些信息->

关于m3u8格式的视频文件ts转mp4下载和key加密问题

一,利用网站浏览器F12键,利用谷歌浏览器插件找到视频的.m3u8文件,并打开. 二,打开m3u8文件后,里面有很多.ts的链接,和key的链接. 三,保存为html文件,下载ts文件,代码如下:可加多线程,可能需要用代理. 1 # 爬虫 123.html就是打开m3u8文件右键保存为html格式. 2 htmlf=open('./123.html','r',encoding="utf-8") 3 htmlcont=htmlf.read() 4 # print(htmlcont) 5

腾讯视频怎么转成mp4模式 软件 工具 方法 最新【已解决】

1.搜索: 小白兔视频格式在线转换 2.转换好后视频已经是MP4格式了. 原文地址:https://blog.51cto.com/14204019/2396896