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 ...
随机推荐
- Redis加锁与解锁
Redis加锁 customerM = BaseMemCached.setMLock(customerId); /** * 个人账户表加锁 **/ public static CustomerM se ...
- 安装mingw后,在命令窗体编译c文件
1.编译test.cpp文件 #include<iostream> int main(int argc,char **argv) { std::cout<<"he ...
- Python 推导式、迭代器、生成器、模块和包
一.推导式 (一).列表推导式(集合推导式也同理于此) 利用列表推导式,取出1-20内所有偶数 li = [i for i in range(1, 21) if i % 2 == 0] # 如果只有一 ...
- Educational Codeforces Round 1 (C) (atan2 + long double | 大数)
这题只能呵呵了. 东搞西搞,折腾快一天,最后用了一个800多行的代码AC了. 好好的题目你卡这种精度干啥. 还有要卡您就多卡点行不,为什么long double 又可以过... 废了N长时间写个了不管 ...
- 【BZOJ3689】异或之 堆+可持久化Trie树
[BZOJ3689]异或之 Description 给定n个非负整数A[1], A[2], ……, A[n].对于每对(i, j)满足1 <= i < j <= n,得到一个新的数A ...
- (比赛)B - 棋盘问题(dfs)
B - 棋盘问题 Time Limit:1000MS Memory Limit:10000KB 64bit IO Format:%lld & %llu Practice POJ ...
- linux字符集查看与设置
linux字符集查看与设置 命令:locale -a 查看本地的字符集 locale -m 查看所有支持的字符集 查看当前默认设置 echo $LANG 记录系统默认使用 ...
- 电路分析三------KCL,KVL,VCR方程
1.2b方程 2.举例 举例2
- CentOS7.x 报错 There are no enabled repos.
地址 :http://mirrors.163.com/centos/7/os/x86_64/Packages/ wget http://mirrors.163.com/centos/7/os/x8 ...
- hdu 3718 Different Division
Different Division Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Other ...
目录视图
摘要视图
订阅