H264视频编码成MP4文件

来源:互联网 发布:淘宝抢拍加购物车技巧 编辑:程序博客网 时间:2024/06/10 00:12

        最近需要将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 framestatic 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 filemp4Encoder.WriteH264File("test.264","test.mp4");}


MP4Encoder完整的代码如下:

/******************************************************************** filename:   MP4Encoder.hcreated:    2013-04-16author:     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 typeunsigned intnSpsLen;unsigned charSps[1024];unsigned intnPpsLen;unsigned charPps[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 framestatic bool PraseMetadata(const unsigned char* pData,int size,MP4ENC_Metadata &metadata);private:// read one nalu from H264 data bufferstatic 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.cppcreated:    2013-04-16author:     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 fileMP4FileHandle 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, // widthm_nHeight,// heightlpMetadata->Sps[1], // sps[1] AVCProfileIndicationlpMetadata->Sps[2], // sps[2] profile_compatlpMetadata->Sps[3], // sps[3] AVCLevelIndication3);           // 4 bytes length before each NAL unitif (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 ppsMP4AddH264PictureParameterSet(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,     // widthm_nHeight,    // heightnalu.data[1], // sps[1] AVCProfileIndicationnalu.data[2], // sps[2] profile_compatnalu.data[3], // sps[3] AVCLevelIndication3);           // 4 bytes length before each NAL unitif (m_videoId == MP4_INVALID_TRACK_ID){printf("add video track failed.\n");return 0;}MP4SetVideoProfileLevel(hMp4File, 1); //  Simple Profile @ Level 3MP4AddH264SequenceParameterSet(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;}


     

原创粉丝点击