Skip to content

Latest commit

 

History

History
473 lines (419 loc) · 10.7 KB

026-如何生成mp4文件.md

File metadata and controls

473 lines (419 loc) · 10.7 KB

如何生成mp4文件

生成mp4库的开源有很多

1.创建文件

MP4Encoder m_objFileWriter;
int r = m_objFileWriter.CreateMP4File((char*)m_file.c_str(),m_w,m_h,90000,m_fps);

2.写入视频数据

m_objFileWriter.WriteH264Data(data,iLen);

3.结束

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









原文作者: 伍意