录制最终格式是MP4,视频流是采用H264编码流,音频是aac编码流
最终需要将两个流合并到一个文件里
采用的方案,是通过mp4v2的库,进行合并
原理很简单:
先创建文件,输入编码参数
需要创建视频流初始
也需要创建音频流初始化
//创建MP4文件
mFileHandle = CreateMP4File(file,90000); 记住handle
//创建音频通道
MP4TrackId audio = MP4AddAudioTrack(mFileHandle, VOICE_RATE, 1024, MP4_MPEG4_AUDIO_TYPE);
//创建视频通道
mVideoId = MP4AddH264VideoTrack( mFileHandle,
mTimeScale,
mTimeScale / mFrameRate,
mWidth,
mHeight,
nalu.data[1], /* sps[1] AVCProfileIndication */
nalu.data[2], /* sps[2] profile_compat */
nalu.data[3], /* sps[3] AVCLevelIndication */
3);
然后按每帧内容存储至文件
看似简单,实际也有很多
1. 音频初始化参数设置
GetDecoderSpecificInfo
注意,他的参数含义,网上很多例子都是44.1k,因此,而参数里如果不细究,这个就会被忽略
auto config = GetDecoderSpecificInfo(2, 3, 2);
/*
参数1:AAC_LC-2; 这个表示音频编码
参数2: 48000-3,44100-4;
参数3: 2 channels : front - left, front - right
*/
/*添加aac音频*/
MP4TrackId audio = MP4AddAudioTrack(mFileHandle, VOICE_RATE, 1024, MP4_MPEG4_AUDIO_TYPE);
if (audio == MP4_INVALID_TRACK_ID)
{
errorf("add audio track failed.\n");
return false;
}
mVoiceId = audio;
MP4SetAudioProfileLevel(mFileHandle, 0x2);
/*P1:AAC_LC-2; P2:48000-3,44100-4; P3:2 channels : front - left, front - right*/
auto config = GetDecoderSpecificInfo(2, 3, 2);
if(!MP4SetTrackESConfiguration(mFileHandle, audio, (uint8_t*) &config, 2))
{
errorf("set audio config failed=2 4 2\n");
}
2. 视频初始化
视频编码库编码之后,会产生不同帧,需要解析nalu,不能直接存储
收到sps帧,需要对视频通道进行初始化
mVideoId = MP4AddH264VideoTrack( mFileHandle,
mTimeScale,
mTimeScale / mFrameRate,
mWidth,
mHeight,
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 (mVideoId == MP4_INVALID_TRACK_ID)
{
errorf("add video track failed.\n");
return 0;
}
MP4SetVideoProfileLevel(mFileHandle, 0x7f); /* main Profile @ Level 4 */
MP4AddH264SequenceParameterSet(mFileHandle,mVideoId,nalu.data+4,nalu.size);
收到pps帧,需要设置参数
MP4AddH264PictureParameterSet(mFileHandle,mVideoId,nalu.data+4,nalu.size);
另外需要注意是同步帧信息
3. 时间戳概念
往往新手会忘记时间戳,但这个是神器,必须好好理解
比如说:视频设置是25帧/秒,如果实际采集只有20帧,或者在变化(一会20,一会25)类似这样。如果不设置时间戳,那么回放的时候会很奇怪,视频一会快一会慢(当然相差不大,裸眼很难分辨出来),但回放时间很真实,例如明明录制了30分钟,但实际回放的时候,只有29分钟。
因此要设置时间戳,时间戳的原点是创建通道的,后面每写入一帧数据,带入时间戳(相对第一个点的时间偏移)
音频也是
音频跟视频是分开两个通道的,因此,时间戳也是分开累计,同理,视频跟音频同步,也是基于时间戳!!
注意第5参数
if(!MP4WriteSample(mFileHandle, mVideoId, nalu.data, nalu.size+4, (now - mVideoTick) * 90, 0, isSync))
uint64_t timeStemp = (tickTime - mVoiceTick) * VOICE_RATE_STAMP;
mVoiceTick = tickTime;
if(!MP4WriteSample(mFileHandle, mVoiceId, buf, size, timeStemp, 0, 1))