您的位置:首页 > 编程语言

pcm+H264封装MP4代码

2014-11-17 13:06 267 查看
昵称:海鱼 日期:2014年11月10日—2014年11月17日

1.本周已完成任务:对添加osd代码进行了解

2.本周未完成任务:改善添加的osd代码

3.下周计划:改善osd代码

4.关键技术点说明:

因为osd代码没有完成,所以就把前面的代码拿出来了

为了音视频同步,我在裸音频流中每写320个字节,加入了8字节的时间戳,每个I,p帧H264的视频流中加了时间戳。

程序读出各时间戳比较,因为是随便写的DEMO,前提是视频全程都有,音频从一段时间后开始,所以我只是将视频的第一个时间戳拿出来,作为起始时间,然后音频的第一个时间戳大于它就写入空数据,直到小于它时再开始写入音频。

前面一个微博写的G711A+H264封装成MP4的有个严重的bug,因为写入音视频是一起的,哪个读完数据就停止,然而写音频和视频的时间不同。比如我录制的pcm音频2048个字节大概播放0.128秒,H264视频每个I,P帧是40ms,这样会导致音频插入了20s而视频就只插入了7s左右,导致播放中视频就卡住了,音频还在播。

所以这次改成了先把视频全部写进MP4文件后再写音频,靠起始时间戳和每次写入加上对应的时间来计算当前时间,这样可以将音视频更好的加入,也能保持音视频同步。

#include <fcntl.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#include <signal.h>
#include <dirent.h>
#include <pthread.h>

#include <asm/types.h>
#include <arpa/inet.h>

#include <sys/vfs.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/utsname.h>

#include <netdb.h>
#include <net/if.h>
#include <netinet/in.h>
#include <net/route.h>
#include <net/if_arp.h>

#include <linux/fs.h>
#include <linux/sockios.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>

#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ether.h>
#include <netinet/ip_icmp.h>

#include "faac.h"
#include "mp4v2.h"

#define  JOSEPH_G711A_LOCATION  "../av_file/test1.pcm"
#define  JOSEPH_H264_LOCALTION "../av_file/test1.h264"
#define  JOSEPH_MP4_FILE   "test.mp4"

#define  MP4_DETAILS_ALL	 0xFFFFFFFF
#define  NALU_SPS  0
#define  NALU_PPS  1
#define  NALU_I    2
#define  NALU_P    3
#define  NALU_SET  4

typedef unsigned int  uint32_t;
typedef unsigned char   uint8_t;

typedef struct Joseph_Acc_Config
{
FILE* fpIn;                    //打开的音频文件
faacEncHandle hEncoder;        //音频文件描述符
unsigned long nSampleRate;     //音频采样数
unsigned int nChannels;  	      //音频声道数
unsigned int nPCMBitSize;        //音频采样精度
unsigned long nInputSamples;      //每次调用编码时所应接收的原始数据长度
unsigned long nMaxOutputBytes;    //每次调用编码时生成的AAC数据的最大长度
unsigned char* pbPCMBuffer;       //pcm数据
unsigned char* pbAACBuffer;       //aac数据
unsigned long long int timestamp;
unsigned long long int mtimestamp;
}JOSEPH_ACC_CONFIG;

typedef struct Joseph_Mp4_Config
{
FILE* fpInVideo;               //打开的视频文件
MP4FileHandle hFile;          //mp4文件描述符
MP4TrackId video;              //视频轨道标志符
MP4TrackId audio;              //音频轨道标志符
int m_vFrameDur;               //帧间隔时间
unsigned int timeScale;        //视频每秒的ticks数,如90000
unsigned int fps;              //视频帧率
unsigned short width;          //视频宽
unsigned short height;         //视频高
unsigned long long int timestamp;
unsigned long long int mtimestamp;
}JOSEPH_MP4_CONFIG;
int joseph_get_h264_frame(JOSEPH_MP4_CONFIG* joseph_mp4_config)
{
joseph_mp4_config->fpInVideo = fopen(JOSEPH_H264_LOCALTION, "rb");
return 0;
}

/***********************************************************mp4 encode***********************************************/
JOSEPH_MP4_CONFIG* InitMp4Encoder(JOSEPH_ACC_CONFIG* joseph_aac_config)
{
JOSEPH_MP4_CONFIG *joseph_mp4_config = NULL;
joseph_mp4_config =(JOSEPH_MP4_CONFIG *)malloc(sizeof(JOSEPH_MP4_CONFIG));

joseph_mp4_config->m_vFrameDur = 0;
joseph_mp4_config->video = MP4_INVALID_TRACK_ID;
joseph_mp4_config->audio = MP4_INVALID_TRACK_ID;
joseph_mp4_config->hFile = NULL;
joseph_mp4_config->timeScale = 90000;
joseph_mp4_config->fps = 25;
joseph_mp4_config->width = 640;
joseph_mp4_config->height = 480;

/*file handle*/
joseph_mp4_config->hFile = MP4Create(JOSEPH_MP4_FILE, 0);
if(joseph_mp4_config->hFile == MP4_INVALID_FILE_HANDLE)
{
printf("open file fialed.\n");
return NULL;
}
MP4SetTimeScale(joseph_mp4_config->hFile, joseph_mp4_config->timeScale);    //timeScale
/*audio track*/
joseph_mp4_config->audio = MP4AddAudioTrack(joseph_mp4_config->hFile, joseph_aac_config->nSampleRate, \
joseph_aac_config->nInputSamples, MP4_MPEG4_AUDIO_TYPE);
if(joseph_mp4_config->audio == MP4_INVALID_TRACK_ID)
{
printf("add audio track failed.\n");
return NULL;
}

MP4SetAudioProfileLevel(joseph_mp4_config->hFile, 0x2);
unsigned char aacConfig[2] = {0x15, 0x88};                                               // 0001 0101 1000 1000
MP4SetTrackESConfiguration(joseph_mp4_config->hFile, joseph_mp4_config->audio, aacConfig, 2);

return joseph_mp4_config;
}

//------------------------------------------------------------------------------------------------- Mp4Encode说明
// 【h264编码出的NALU规律】
// 第一帧 SPS【0 0 0 1 0x67】 PPS【0 0 0 1 0x68】 SEI【0 0 0 1 0x6】 IDR【0 0 0 1 0x65】
// p帧      P【0 0 0 1 0x61】
// I帧    SPS【0 0 0 1 0x67】 PPS【0 0 0 1 0x68】 IDR【0 0 0 1 0x65】
// 【mp4v2封装函数MP4WriteSample】
// 此函数接收I/P nalu,该nalu需要用4字节的数据大小头替换原有的起始头,并且数据大小为big-endian格式
//-------------------------------------------------------------------------------------------------
int Mp4VEncode(JOSEPH_MP4_CONFIG* joseph_mp4_config, unsigned char* naluData, int naluSize)
{
int index = -1;

if(naluData[0]==0 && naluData[1]==0 && naluData[2]==0 && naluData[3]==1 && naluData[4]==0x67)
{
index = NALU_SPS;
//		printf("%s[%d]====NALU_SPS\n",__FUNCTION__,__LINE__);
}
if(index!=NALU_SPS && joseph_mp4_config->video == MP4_INVALID_TRACK_ID)
{
return -1;
}
if(naluData[0]==0 && naluData[1]==0 && naluData[2]==0 && naluData[3]==1 && naluData[4]==0x68)
{
index = NALU_PPS;
//		printf("%s[%d]====NALU_PPS\n",__FUNCTION__,__LINE__);
}
if(naluData[0]==0 && naluData[1]==0 && naluData[2]==0 && naluData[3]==1 && naluData[4]==0x65)
{
index = NALU_I;
//		printf("%s[%d]====NALU_I\n",__FUNCTION__,__LINE__);
}
if(naluData[0]==0 && naluData[1]==0 && naluData[2]==0 && naluData[3]==1 && naluData[4]==0x61)
{
index = NALU_P;
//		printf("%s[%d]====NALU_P\n",__FUNCTION__,__LINE__);
}
if(naluData[0]==0 && naluData[1]==0 && naluData[2]==0 && naluData[3]==1 && naluData[4]==0x6)
{
index = NALU_SET;
//		printf("%s[%d]====NALU_SET\n",__FUNCTION__,__LINE__);
}
switch(index)
{
case NALU_SPS:
if(joseph_mp4_config->video == MP4_INVALID_TRACK_ID)
{
joseph_mp4_config->video = MP4AddH264VideoTrack
(joseph_mp4_config->hFile,
joseph_mp4_config->timeScale,                               //timeScale
(joseph_mp4_config->timeScale / joseph_mp4_config->fps),    //sampleDuration    timeScale/fps
joseph_mp4_config->width,     								// width
joseph_mp4_config->height,    								// height
naluData[5], 												// sps[1] AVCProfileIndication
naluData[6], 												// sps[2] profile_compat
naluData[7], 												// sps[3] AVCLevelIndication
3);           												// 4 bytes length before each NAL unit
if(joseph_mp4_config->video == MP4_INVALID_TRACK_ID)
{
printf("add video track failed.\n");
return -1;
}
//MP4SetVideoProfileLevel(joseph_mp4_config->hFile, 1); //  Simple Profile @ Level 3   mp4编码标准
MP4SetVideoProfileLevel(joseph_mp4_config->hFile, 0x7F); //  Simple Profile @ Level 3
}
MP4AddH264SequenceParameterSet(joseph_mp4_config->hFile, joseph_mp4_config->video, naluData+4, naluSize-4);
break;
case NALU_PPS:
MP4AddH264PictureParameterSet(joseph_mp4_config->hFile, joseph_mp4_config->video, naluData+4, naluSize-4);
break;
case NALU_SET:
MP4AddH264PictureParameterSet(joseph_mp4_config->hFile, joseph_mp4_config->video, naluData+4, naluSize-4);
break;
case NALU_I:
{
unsigned char* IFrameData = (unsigned char*)malloc((naluSize)* sizeof(unsigned char));

IFrameData[0] = (naluSize-4) >>24;
IFrameData[1] = (naluSize-4) >>16;
IFrameData[2] = (naluSize-4) >>8;
IFrameData[3] = (naluSize-4) &0xff;

memcpy(IFrameData+4, naluData+4, naluSize-4);
//if(!MP4WriteSample(joseph_mp4_config->hFile, joseph_mp4_config->video, IFrameData, naluSize+1, m_vFrameDur/8000*90000, 0, 1))
if(!MP4WriteSample(joseph_mp4_config->hFile, joseph_mp4_config->video, IFrameData, naluSize, MP4_INVALID_DURATION, 0, 1))
{
return -1;
}

//joseph_mp4_config->m_vFrameDur = 0;
free(IFrameData);
IFrameData = NULL;

break;
}
case NALU_P:
{
naluData[0] = (naluSize-4) >>24;
naluData[1] = (naluSize-4) >>16;
naluData[2] = (naluSize-4) >>8;
naluData[3] = (naluSize-4) &0xff;

//if(!MP4WriteSample(joseph_mp4_config->hFile, joseph_mp4_config->video, naluData, naluSize, m_vFrameDur/8000*90000, 0, 1))
if(!MP4WriteSample(joseph_mp4_config->hFile, joseph_mp4_config->video, naluData, naluSize, MP4_INVALID_DURATION, 0, 1))
{
return -1;
}

//joseph_mp4_config->m_vFrameDur = 0;

break;
}
default:
break;
}

return 0;
}

int Mp4AEncode(JOSEPH_MP4_CONFIG* joseph_mp4_config, unsigned char* aacData, int aacSize)
{
if(joseph_mp4_config->video == MP4_INVALID_TRACK_ID)
{
return -1;
}
MP4WriteSample(joseph_mp4_config->hFile, joseph_mp4_config->audio, aacData, aacSize , MP4_INVALID_DURATION, 0, 1);

//joseph_mp4_config->m_vFrameDur += 1024;

return 0;
}

void CloseMp4Encoder(JOSEPH_MP4_CONFIG* joseph_mp4_config)
{
if(joseph_mp4_config->hFile)
{
MP4Close(joseph_mp4_config->hFile, 0);
joseph_mp4_config->hFile = NULL;
}

return ;
}
/*************************************************aac encode******************************************/
JOSEPH_ACC_CONFIG* InitAccEncoder(void)
{
JOSEPH_ACC_CONFIG* joseph_aac_config = NULL;

faacEncConfigurationPtr pConfiguration;

int nRet = 0;
int nPCMBufferSize = 0;

joseph_aac_config = (JOSEPH_ACC_CONFIG*)malloc(sizeof(JOSEPH_ACC_CONFIG));

joseph_aac_config->nSampleRate = 8000;
joseph_aac_config->nChannels = 1;
joseph_aac_config->nPCMBitSize = 16;
joseph_aac_config->nInputSamples = 0;
joseph_aac_config->nMaxOutputBytes = 0;

joseph_aac_config->fpIn = fopen(JOSEPH_G711A_LOCATION, "rb");

//open FAAC engine
joseph_aac_config->hEncoder = faacEncOpen(joseph_aac_config->nSampleRate, joseph_aac_config->nChannels, \
&joseph_aac_config->nInputSamples, &joseph_aac_config->nMaxOutputBytes);
if(joseph_aac_config->hEncoder == NULL)
{
printf("failed to call faacEncOpen()\n");
return NULL;
}

nPCMBufferSize = (joseph_aac_config->nInputSamples*(joseph_aac_config->nPCMBitSize/8));
joseph_aac_config->pbPCMBuffer=(unsigned char*)malloc(nPCMBufferSize*sizeof(unsigned char));
memset(joseph_aac_config->pbPCMBuffer, 0, nPCMBufferSize);
joseph_aac_config->pbAACBuffer=(unsigned char*)malloc(joseph_aac_config->nMaxOutputBytes*sizeof(unsigned char));
memset(joseph_aac_config->pbAACBuffer, 0, joseph_aac_config->nMaxOutputBytes);

//GET current encoding configuration
pConfiguration = faacEncGetCurrentConfiguration(joseph_aac_config->hEncoder);
#if 1
pConfiguration->inputFormat = FAAC_INPUT_16BIT;
pConfiguration->outputFormat = 0;
pConfiguration->aacObjectType = LOW;
#else
pConfiguration->inputFormat = FAAC_INPUT_16BIT;

/*0 - raw; 1 - ADTS*/
pConfiguration->outputFormat = 0;
pConfiguration->useTns = 0;
pConfiguration->allowMidside = 1;
pConfiguration->shortctl = SHORTCTL_NORMAL;
pConfiguration->aacObjectType = LOW;
pConfiguration->mpegVersion = MPEG2;
#endif
//set encoding configuretion
nRet = faacEncSetConfiguration(joseph_aac_config->hEncoder, pConfiguration);

return joseph_aac_config;
}
void CloseAccEncoder(JOSEPH_ACC_CONFIG* joseph_aac_config)
{
if(joseph_aac_config->hEncoder)
{
faacEncClose(joseph_aac_config->hEncoder);
joseph_aac_config->hEncoder = NULL;
}

return ;
}
/*********************************************main**************************************************/
int main(int argc, char* argv[])
{
JOSEPH_ACC_CONFIG *joseph_aac_config = NULL;
JOSEPH_MP4_CONFIG *joseph_mp4_config = NULL;

int nRet = 0;
int nTmp = 0;
int nCount = 0;
int nStatus = 0;
int PCMSize = 320;
int nPCMRead = 0;
int nH264Read = 0;
int H264Read_one = 0;
unsigned long long int nVideoSize = 0;
int nPCMBufferSize = 0;
int i = 0,j,k;
unsigned char nVideoBuffer[1048576] = {0};
unsigned char VideoBuffer[1036] = {0};
unsigned char mVideoBuffer[1024] = {0};
unsigned char *pbPCMTmpBuffer = NULL;
int timeout=0;
pbPCMTmpBuffer = (unsigned char *)malloc(PCMSize *sizeof(unsigned char));
memset(pbPCMTmpBuffer, 0, PCMSize);
//	joseph_mp4_config->kk = 0;
/*init aac */
if((joseph_aac_config=InitAccEncoder()) == NULL)
{
printf("init aac failed\n");
return -1;
}

/*init mp4*/
if((joseph_mp4_config=InitMp4Encoder(joseph_aac_config)) == NULL)
{
printf("init mp4 failed\n");
return -1;
}

nPCMBufferSize = (joseph_aac_config->nInputSamples*(joseph_aac_config->nPCMBitSize/8));
/*get video frame*/
nVideoSize = joseph_get_h264_frame(joseph_mp4_config);
if(nVideoSize < 0)
{
printf("read g711a frame failed\n");
return -1;
}
nH264Read = fread(&VideoBuffer, 1, 1024, joseph_mp4_config->fpInVideo);
nStatus = 0;
while(nH264Read>0)
{
H264Read_one = 1;
while(H264Read_one>0)
{
if(nH264Read<12)
{
k = nH264Read;
nH264Read = fread(&mVideoBuffer, 1, 1024, joseph_mp4_config->fpInVideo);
memcpy(VideoBuffer+k,mVideoBuffer,nH264Read);
nH264Read += k;
}
for(j=5;j<nH264Read;j++)
{
if((VideoBuffer[j]==0x00)&&(VideoBuffer[j+1]==0x00)&&(VideoBuffer[j+2]==0x00)&&(VideoBuffer[j+3]==0x01)&&(j+4<nH264Read))
{
memcpy(nVideoBuffer+nVideoSize,VideoBuffer,j);
H264Read_one = 0;
nVideoSize += j;
if((VideoBuffer[j+4]==0x61)||(VideoBuffer[j+4]==0x67))
{
joseph_mp4_config->timestamp = 0;
for(k=1;k<=8;k++)
{
joseph_mp4_config->timestamp = joseph_mp4_config->timestamp<<8;
joseph_mp4_config->timestamp += VideoBuffer[j-k];
}
if(nStatus == 0)
{
joseph_mp4_config->mtimestamp = joseph_mp4_config->timestamp;
nStatus = 1;
}
nVideoSize -= 8;
memcpy(nVideoBuffer,nVideoBuffer,nVideoSize);
}
memcpy(VideoBuffer,VideoBuffer+j,nH264Read-j);
nH264Read -= j;
break;
}
if(nH264Read == (j+1))
{
memcpy(nVideoBuffer+nVideoSize,VideoBuffer,nH264Read-12);
nVideoSize += nH264Read-12;
memcpy(VideoBuffer,VideoBuffer+nH264Read-12,12);
nH264Read = 0;
nH264Read = fread(&mVideoBuffer, 1, 1024, joseph_mp4_config->fpInVideo);
memcpy(VideoBuffer+12,mVideoBuffer,nH264Read);
nH264Read += 12;
if(nH264Read <1036)
{
memcpy(nVideoBuffer+nVideoSize,VideoBuffer,nH264Read-8);
nVideoSize += nH264Read-8;
joseph_mp4_config->timestamp = 0;
for(k=1;k<=8;k++)
{
joseph_mp4_config->timestamp = joseph_mp4_config->timestamp<<8;
joseph_mp4_config->timestamp += VideoBuffer[nH264Read-k];
}
nH264Read = 0;
H264Read_one = 0;
}
}
}
}

if(nVideoSize < 0)
{
printf("read g711a frame failed\n");
break;
}
/*write video frame to mp4*/
if((Mp4VEncode(joseph_mp4_config, nVideoBuffer, nVideoSize)) < 0)
{
printf("write video frame failed\n");
break;
}
nVideoSize = 0;
memset(nVideoBuffer, 0, nVideoSize);
}

nPCMRead = 1;
k = 0;
while(nPCMRead>0)
{
nStatus = 0;
nPCMRead = fread(pbPCMTmpBuffer, 1, 160, joseph_aac_config->fpIn);
if(timeout == 0)
timeout = 1;
else
{
joseph_aac_config->timestamp = 0;
fread(&joseph_aac_config->timestamp, 8, 1, joseph_aac_config->fpIn);
timeout = 0;
while((k == 0)&&(joseph_aac_config->timestamp > joseph_mp4_config->mtimestamp))
{
nRet = 160;
if((Mp4AEncode(joseph_mp4_config, joseph_aac_config->pbAACBuffer, nRet)) < 0)
{
printf("write audio frame failed\n");
break;
}
joseph_mp4_config->mtimestamp += 128000;
}
k = 1;
}
/*pcm to aac*/
if((nPCMBufferSize - nCount) < nPCMRead)
{
nStatus = 1;
memcpy((joseph_aac_config->pbPCMBuffer + nCount), pbPCMTmpBuffer, (nPCMBufferSize - nCount));
joseph_aac_config->nInputSamples = (nPCMBufferSize / (joseph_aac_config->nPCMBitSize / 8));
nRet = faacEncEncode(joseph_aac_config->hEncoder, (int*) (joseph_aac_config->pbPCMBuffer), \
joseph_aac_config->nInputSamples, joseph_aac_config->pbAACBuffer, joseph_aac_config->nMaxOutputBytes);

/*write audio frame to mp4*/
if((Mp4AEncode(joseph_mp4_config, joseph_aac_config->pbAACBuffer, nRet)) < 0)
{
printf("write audio frame failed\n");
break;
}
memset(joseph_aac_config->pbPCMBuffer,0,1024);
/*Treatment of redundant frames*/
nTmp = (nPCMRead - (nPCMBufferSize - nCount));
memset(joseph_aac_config->pbPCMBuffer, 0, nPCMBufferSize);
memcpy(joseph_aac_config->pbPCMBuffer, (pbPCMTmpBuffer + (nPCMBufferSize-nCount)), nTmp);
nCount = 0;
nCount += nTmp;

}
if( nStatus == 0)
{
memcpy(joseph_aac_config->pbPCMBuffer + nCount, pbPCMTmpBuffer, nPCMRead);
nCount += nPCMRead;
}
if(nPCMRead < 160)
{
joseph_aac_config->nInputSamples = (nCount / (joseph_aac_config->nPCMBitSize / 8));
nRet = faacEncEncode(joseph_aac_config->hEncoder, (int*) (joseph_aac_config->pbPCMBuffer), \
joseph_aac_config->nInputSamples, joseph_aac_config->pbAACBuffer, joseph_aac_config->nMaxOutputBytes);

/*write audio frame to mp4*/
if((Mp4AEncode(joseph_mp4_config, joseph_aac_config->pbAACBuffer, nRet)) < 0)
{
printf("write audio frame failed\n");
break;
}
break;
}
memset(pbPCMTmpBuffer, 0, 160);
}
/*Close FAAC engine*/
CloseAccEncoder(joseph_aac_config);
CloseMp4Encoder(joseph_mp4_config);

/*close file fp*/
fclose(joseph_aac_config->fpIn);

/*free the source of malloc*/
//audio part
free(joseph_aac_config->pbPCMBuffer);
joseph_aac_config->pbPCMBuffer = NULL;
free(joseph_aac_config->pbAACBuffer);
joseph_aac_config->pbAACBuffer = NULL;
free(pbPCMTmpBuffer);
pbPCMTmpBuffer = NULL;
free(joseph_aac_config);
joseph_aac_config = NULL;
//video part
free(joseph_mp4_config);
joseph_mp4_config = NULL;

//	printf("%s:[%d] The sys changover succeed !\n",__FUNCTION__,__LINE__);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: