使用MpV2将H264保存为Mp4文件
/******************************************************************** 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; }; /******************************************************************** 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; } #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"); } 本博客转自这位大哥:https://blog.csdn.net/firehood_/article/details/8813587
本博客转自这位大哥:https://blog.csdn.net/firehood_/article/details/8813587,使用mpv4库将h264的流保存为mp4,H264流所有nul都是以0x00 0x00 0x00 0x01开头
00 00 00 01是Start code后面的ox67为 0110 0111
forbidden_zero_bit 是禁止位,应该是第一位即f(1)=0,1为语法有错误
nal_ref_idc是参考级别,代表被其它帧参考情况,u(2)= 11 = 3最(0为无参考,详见规范)
nal_unit_type是该帧的类型,为剩下的5位,u(5)= 0 0111 = 7
目前类型有:
//H264定义的类型 values for nal_unit_type
typedef enum {
NALU_TYPE_SLICE = 1,
NALU_TYPE_DPA = 2,
NALU_TYPE_DPB = 3,
NALU_TYPE_DPC = 4,
NALU_TYPE_IDR = 5,
NALU_TYPE_SEI = 6,
NALU_TYPE_SPS = 7,
NALU_TYPE_PPS = 8,
NALU_TYPE_AUD = 9,
NALU_TYPE_EOSEQ = 10,
NALU_TYPE_EOSTREAM = 11,
NALU_TYPE_FILL = 12,
#if (MVC_EXTENSION_ENABLE)
NALU_TYPE_PREFIX = 14,
NALU_TYPE_SUB_SPS = 15,
NALU_TYPE_SLC_EXT = 20,
NALU_TYPE_VDRD = 24 // View and Dependency Representation Delimiter NAL Unit
#endif
} NaluType;

浙公网安备 33010602011771号