天天看點

MP4v2封裝H264檔案,時長隻有1s[已解決]

嘗試用MP4V2封裝H264檔案,代碼如下

#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*/)
{
	MP4FileHandle hMp4file = MP4Create(pFileName);
	if(pFileName == NULL)
	{
		return hMp4file;
	}
	// create mp4 file
	
	if (hMp4file == MP4_INVALID_FILE_HANDLE)
	{
		printf("ERROR:Open file fialed.\n");
		return hMp4file;
	}
	m_nWidth = width;
	m_nHeight = height;
	m_nTimeScale = 90000;
	m_nFrameRate = 30;
	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))
	{
		printf("size %d pos %d nalu-type %d nalu-size %d\n",size,pos,nalu.type,nalu.size);
		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))
			{
				printf("write nok!\n");
				delete[] data;
				return 0;
			}
			// fflush(hMp4File);
			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,1920,1080);
 
    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;
				}
		}
		static int cnt = 0;
		printf("writeH264Data start %d\n",cnt++);
		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;
}
           

用mp4v2的庫也檢視了生成的視訊資訊,有15個track,理論上應該對應15s,但實際視訊時長就1s,

./mp4trackdump version 2.0.0
mp4file test.mp4, track 1, samples 30, timescale 90000
sampleId      1, size 29107 duration     3000 time        0 00:00:00.000 S
sampleId      2, size   864 duration     3000 time     3000 00:00:00.033 S
sampleId      3, size  2129 duration     3000 time     6000 00:00:00.066 S
sampleId      4, size  9758 duration     3000 time     9000 00:00:00.100 S
sampleId      5, size   547 duration     3000 time    12000 00:00:00.133 S
sampleId      6, size  2318 duration     3000 time    15000 00:00:00.166 S
sampleId      7, size 11209 duration     3000 time    18000 00:00:00.200 S
sampleId      8, size   502 duration     3000 time    21000 00:00:00.233 S
s
           

解決方法:

        通過Elecard工具對h264檔案分析,發現網上的标準的h264檔案均隻有一幀pps,sps,但我們的h264檔案卻有多幀,如圖所示(圖一為網上的H264檔案,圖二為我們自己生成的H264檔案),是以我們可以在進行轉化時,隻提取一幀sps和pps

MP4v2封裝H264檔案,時長隻有1s[已解決]

圖一

MP4v2封裝H264檔案,時長隻有1s[已解決]

 圖二

繼續閱讀