生成mp4库的开源有很多
- 1.gpac https://gpac.wp.mines-telecom.fr
- 2.mpeg4ip http://mpeg4ip.sourceforge.net
- 3.mp4v2 从mpeg4ip提取 https://code.google.com/p/mp4v2/
MP4Encoder m_objFileWriter;
int r = m_objFileWriter.CreateMP4File((char*)m_file.c_str(),m_w,m_h,90000,m_fps);
m_objFileWriter.WriteH264Data(data,iLen);
m_objFileWriter.CloseMP4File();
#pragma once
#include "mp4v2/mp4v2.h"
#include <string>
using namespace std;
// NALU单元
//#define NALU_TYPE_SLICE 1
//#define NALU_TYPE_DPA 2
//#define NALU_TYPE_DPB 3
//#define NALU_TYPE_DPC 4
//#define NALU_TYPE_IDR 5
//#define NALU_TYPE_SEI 6
//#define NALU_TYPE_SPS 7
//#define NALU_TYPE_PPS 8
//#define NALU_TYPE_AUD 9
//#define NALU_TYPE_EOSEQ 10
//#define NALU_TYPE_EOSTREAM 11
//#define NALU_TYPE_FILL 12
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.
bool CreateMP4File(const char *fileName,int width,int height,int timeScale = 90000,int frameRate = 25);
// wirte 264 metadata in mp4 file.
bool Write264Metadata(LPMP4ENC_Metadata lpMetadata);
// wirte 264 data, data can contain multiple frame.
int WriteH264Data(char* pData,int size,int videoWidth=0, int videoHeight=0);
// close mp4 file.
void CloseMP4File();
// 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);
string getFileName() {
return fileName;
}
// Prase H264 metamata from H264 data frame
static bool PraseMetadata(char* pData,int size,MP4ENC_Metadata &metadata);
static unsigned int test4byte(char* data, unsigned int offset) {
return (data[offset] << 24) | (data[offset+1] << 16) | (data[offset+2] << 8) | (data[offset+3]);
}
private:
// read one nalu from H264 data buffer
static int ReadOneNaluFromBuf(char *buffer,unsigned int nBufferSize,unsigned int offSet,MP4ENC_NaluUnit &nalu);
void reset();
private:
int m_nWidth;
int m_nHeight;
int m_nFrameRate;
int m_nTimeScale;
bool haveGetSPS;
bool haveGetPPs;
string fileName;
MP4TrackId m_videoId;
MP4FileHandle hMp4File;
};
#endif /* MP4ENCODER_H_ */
#include "MP4Encoder.h"
#include <string.h>
#define BUFFER_SIZE (1024*1024)
MP4Encoder::MP4Encoder(void)
{
reset();
}
MP4Encoder::~MP4Encoder(void)
{
}
bool MP4Encoder::CreateMP4File(const char *pFileName,int width,int height,int timeScale/* = 90000*/,int frameRate/* = 25*/)
{
reset();
if(pFileName == NULL)
{
return false;
}
fileName = pFileName;
// create mp4 file
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 = timeScale;
m_nFrameRate = frameRate;
MP4SetTimeScale(hMp4File, m_nTimeScale);
return true;
}
bool MP4Encoder::Write264Metadata(LPMP4ENC_Metadata lpMetadata)
{
if(hMp4File == NULL) {
return false;
}
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(char* pData, int size, int videoWidth, int videoHeight)
{
if(hMp4File == NULL)
{
printf("MP4Encoder::WriteH264Data hMp4File == NULL! create file first!.\n");
return -1;
}
if(pData == NULL || size == 0)
{
printf("MP4Encoder::WriteH264Data pData == NULL or size == 0!.\n");
return -1;
}
if(videoWidth>0&&videoHeight>0) {
m_nWidth = m_nWidth== videoWidth?m_nWidth:videoWidth;
m_nHeight = m_nHeight== videoHeight?m_nHeight:videoHeight;
}
if(test4byte(pData, 0) == 0x00000001
&& (pData[4]&0x1F) == 9) {//skip aud 00 00 00 01 09 10 or 00 00 00 01 09 30
pData = pData + 6;
size = size - 6;
}
MP4ENC_NaluUnit nalu;
int pos = 0, len = 0;
while (len = ReadOneNaluFromBuf(pData,size,pos,nalu))
{
if(nalu.type != 0x01
&& nalu.type != 0x05
&& nalu.type != 0x06
&& nalu.type != 0x07
&& nalu.type != 0x08
&& nalu.type != 0x09
){
printf("recv invalidate frame! drop it\n");
return 0;
}
if(nalu.type == 0x07) // sps
{
// 添加h264 track
if(!haveGetSPS) {
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;
}
printf("find sps! add Track %d %d %d.\n", m_videoId, m_nWidth, m_nHeight);
MP4SetVideoProfileLevel(hMp4File, 1); // Simple Profile @ Level 3
MP4AddH264SequenceParameterSet(hMp4File,m_videoId,nalu.data,nalu.size);
haveGetSPS = true;
}
}
else if(nalu.type == 0x08) // pps
{
if(!haveGetPPs) {
MP4AddH264PictureParameterSet(hMp4File,m_videoId,nalu.data,nalu.size);
haveGetPPs = true;
}
}
else
{
if(!haveGetSPS
|| !haveGetPPs) {
printf("MP4Encoder watting first i frame\n");
return 0;
}
if(nalu.type == 0x06) {//skip sei
pos += len;
continue;
}
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))
{
printf("MP4Encoder::MP4WriteSample error.\n");
return 0;
}
delete[] data;
}
pos += len;
}
return pos;
}
int MP4Encoder::ReadOneNaluFromBuf(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::reset() {
m_videoId = -1;
m_nWidth = 0;
m_nHeight = 0;
m_nTimeScale = 0;
m_nFrameRate = 0;
haveGetSPS = false;
haveGetPPs = false;
hMp4File = NULL;
}
void MP4Encoder::CloseMP4File()
{
if(hMp4File)
{
MP4Duration fileDuration = MP4GetDuration(hMp4File);
MP4Duration tmpDuration = MP4_INVALID_DURATION;
uint64_t durationInMs = 0;
if(m_videoId != MP4_INVALID_TRACK_ID) {
tmpDuration = MP4GetTrackDuration(hMp4File,m_videoId);
durationInMs =
double(MP4ConvertFromTrackDuration(hMp4File, m_videoId,
tmpDuration, MP4_MSECS_TIME_SCALE));
}
// if(fileDuration == 0
// && tmpDuration != 0) {
if(tmpDuration != 0) {
fileDuration = tmpDuration;
// qDebug() << "MP4SetDuration before MP4Encoder::closeMP4File " << fileDuration;
// MP4SetDuration(hMp4File, fileDuration);
}
// qDebug() << " MP4Encoder::CloseMP4File " << fileName
// << " fileDuration is " << fileDuration
<< " " << tmpDuration
// << " durationInMs " << durationInMs;
MP4Close(hMp4File);
hMp4File = NULL;
}
}
bool MP4Encoder::WriteH264File(const char* pFile264,const char* pFileMp4)
{
if(pFile264 == NULL || pFileMp4 == NULL)
{
return false;
}
if(!CreateMP4File(pFileMp4,352,288)) {
printf("ERROR:Create file failed!\n");
return false;
}
FILE *fp = fopen(pFile264, "rb");
if(!fp)
{
printf("ERROR:open file failed!\n");
return false;
}
fseek(fp, 0, SEEK_SET);
char *buffer = new 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(buffer,writelen);
if(writelen<=0)
{
break;
}
memcpy(buffer,buffer+writelen,readlen-writelen+1);
pos = readlen-writelen+1;
}
fclose(fp);
delete[] buffer;
CloseMP4File();
return true;
}
bool MP4Encoder:: PraseMetadata(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;
}
原文作者: 伍意