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文件。
- 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");
 - }
 
MP4Encoder完整的代码如下:
- /********************************************************************
 - 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;
 - };
 
MP4Encoder.cpp
- /********************************************************************
 - 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;
 - }
 
H264视频编码成MP4文件的更多相关文章
- 工具---《.264视频 转成 MP4视频》
		
<.264视频 转成 MP4视频> 安装了“爱奇艺万能播放器”可以打开.264视频,但是opencv却不能直接读取.264视频,还是需要想办法“.264视频 转成 MP4/avi视频”. ...
 - 使用ffmpeg获取视频流后如何封装存储成mp4文件
		
int main(int argc,char *argv[]) 02 { 03 AVFormatContext *pFormatCtx; 04 int i,videoStream; 05 AVC ...
 - 【转】qlv文件如何转换成mp4 怎样把下载好的qlv格式视频转换成MP4格式
		
狸窝 复制 收藏 保存到桌面 快速找教程方案 反馈需求 社会主义核心价值观 客服QQ41442901 马上注册 升级VIP 对于视频文件之间的转换问题,我也已经是无力吐槽了,每个 ...
 - 利用OpenCV进行H264视频编码的简易方式
		
在Python下,利用pip安装预编译的opencv库,并实现h264格式的视频编码. 1. 安装OpenCV $ pip install opencv-python 建议在python虚拟环境下安装 ...
 - 【转】H264视频编码级别说明profile level Encoder
		
版权声明:本文为博主原创文章,未经博主允许不得转载. 首先要阐明所谓的AVC其实就是H.264标准,是由ITU-T和ISO/IEC组成的联合视频组(JVT,Joint Video Team)一起开发的 ...
 - 使用ffmpeg将BMP图片编码为x264视频文件,将H264视频保存为BMP图片,yuv视频文件保存为图片的代码
		
ffmpeg开源库,实现将bmp格式的图片编码成x264文件,并将编码好的H264文件解码保存为BMP文件. 实现将视频文件yuv格式保存的图片格式的測试,图像格式png,jpg, gif等等測试均O ...
 - C#使用FFmpeg 将视频格式转换成MP4示例
		
一.常用视频格式分辨率 640x480p 720p格式,分辨率为1280×720p / 60Hz,行频为45kHz 1080p格式,分辨率为1920×1080逐行扫描,专业格式 二.FFmpeg部分参 ...
 - 【转】Python爬取AES加密的m3u8视频流的小电影并转换成mp4
		
最近发现一个视频网站,准备去爬取得时候,前面很顺利利用fiddler抓包获取网站的post数据loads为python字典数据,分析数据就能发现每个视频的连接地址就在其中, 发现这些都是m3u8文件流 ...
 - 使用HM16.0对视频编码
		
1.编译HM16.0源码: 步骤参照:https://www.vcodex.com/hevc-and-vp9-codecs-try-them-yourself/(可设置pq等参数) [编译过程中遇到l ...
 
随机推荐
- 关于IIS上Yii2的Url路由美化
			
Yii2默认的路由是酱紫的 http://.../admin/web/index.php?r=site/login 心中理想的美化Url应该这样 http://.../admin/web/site/ ...
 - 如何正确对tomcat host进行配置
			
今天在对tomcat的host容器(即虚拟主机的配置)进行配置时,发现即使修改了host name的值(默认为localhost),但是仍无法访问web项目的问题(提示域名解析出错).只能使用默认的值 ...
 - centos7.0 安转mysql5.7
			
安装mysql5.7需要boost依赖包groupadd mysql useradd -r -g mysql -s /bin/false mysql cmake . -DCMAKE_INSTALL_P ...
 - 摘要: CentOS 6.5搭建Redis3.2.8伪分布式集群
			
from https://my.oschina.net/ososchina/blog/856678 摘要: CentOS 6.5搭建Redis3.2.8伪分布式集群 前言 最近在服务器上搭建了 ...
 - 【网络与系统安全】关于SSL/TSL协议的分析
			
前言 TSL协议的前身是由网景(Netscape)公司于1994年研发的安全套接字(Secure Socket Layer)协议.它建立在TCP协议栈的传输层,用于保护面向连接的TCP通信.实际TSL ...
 - cocos2dx的ui封装
			
cocos2dx里加载cocosudio导出的ui配置文件,在这之上封装了一下,封装核心类包括 UIManager,UILayer,UIOwner UIManager是所有ui总的管理类,代码如下: ...
 - java堆分析神器MAT
			
Memory Analyzer(MAT) 基于Eclipse的软件 http://www.eclipse.org/mat/
 - Linux安装Nignx基于域名的多虚拟主机实战
			
看这个文章之前,要保证你的Nginx已经安装成功! 如果没有,请移步到下面这个文章,看完后再回来看! https://www.cnblogs.com/apollo1616/p/10214531.htm ...
 - python webserver客户端
			
1.库 suds库,只能做webserver客户端,轻量化,使用方便.安装使用pip. 2.使用 如有webserver情况如下: url:http://10.110.35.41:8980/wsser ...
 - 分析DNS解析时间
			
提高网页的打开速度,一般地我们会选择使用CDN,利用“就近原则”让用户在最短的时间内获取到服务器资源,实际应用当中采用CDN的方式提高网站访问速度的效果也是最明显的.这也就是为什么国外的空间打开速度远 ...
 
			
		
目录视图
摘要视图
订阅