技术在于交流、沟通,转载请注明出处并保持作品的完整性。
原文:https://blog.csdn.net/hiwubihe/article/details/82346800
[本系列相关文章]
- H264和音频流打包成PS流 (MPEG2-PS)
- PS流解复用成H264和音频流(ES提取)
- H264和音频流打包成TS流 (MPEG2-TS)
- TS流解复用成H264和音频流(ES提取)
- FLV文件格式基础
- 解复用FLV文件(基于FFMPEG解析FLV(h264+aac))
- 解复用FLV文件(不用FFMPEG,C++实现)
本篇运用C++实现一个FLV文件的解复用,FLV可以是h264+AAC封装,也可以是h264+mp3封装的。参考ffmpeg源码flvenc.c实现。
探测视频格式是否FLV格式,这部分直接参考协议定义即可。
static bool ProbeFlv(unsigned char*p,long lSize)
{
unsigned char *d = p;
d+=5;
unsigned offset = avio_rb32(&d);
//头FLV 版本<5 头长度>8
if (p[0] == 'F' &&
p[1] == 'L' &&
p[2] == 'V' &&
p[3] < 5 && p[5] == 0 &&
offset + 100 < lSize &&
offset > 8)
{
return true;
}
return false;
}
读取FLV头
//读取FLV头
static bool flvReadHeader(unsigned char*pData,long lSize,int &iConsumed)
{
iConsumed = 0;
unsigned char*p = pData;
p+=4;
iConsumed+=4;
int flags = avio_r8(&p);
iConsumed+=1;
g_FlvInfo.iStreamFlags = flags & (FLV_HEADER_FLAG_HASVIDEO | FLV_HEADER_FLAG_HASAUDIO);
int offset = avio_rb32(&p);
iConsumed+=4;
if(offset != iConsumed)
{
return false;
}
//前TAG长度字段读取
int iPreTagSize = avio_rb32(&p);
iConsumed+=4;
if(iPreTagSize != 0)
{
return false;
}
return true;
}
循环读取数据,并解析一个完整的TAG
bool FindFlvTag(unsigned char*pData,long lSize,TagHeadInfo &stTagHeadInfo)
{
//头长度11
if(lSize<(11))
{
//长度不够
return false;
}
//没解析头 现在解析
if(stTagHeadInfo.iHeadLen == 0)
{
unsigned char*p = pData;
int iOffset=0;
//TAG头类型
stTagHeadInfo.type = (FlvTagType)(avio_r8(&p) & 0x1F);
iOffset += 1;
//TAG数据长度
stTagHeadInfo.iTagSize = avio_rb24(&p);
iOffset += 3;
uint64_t dts = avio_rb24(&p);
dts |= (unsigned)avio_r8(&p) << 24;
stTagHeadInfo.dts = dts;
iOffset += 4;
//跳过STREAMID
iOffset += 3;
stTagHeadInfo.iHeadLen =iOffset;
stTagHeadInfo.pData = pData;
//头长度+Tag消息长度+前一个Tag长度
if(lSize>= stTagHeadInfo.iHeadLen + stTagHeadInfo.iTagSize+4)
{
return true;
}
else
{
return false;
}
}
//已经解析过头
else
{
//头长度+Tag消息长度+前一个Tag长度
if(lSize>= stTagHeadInfo.iHeadLen + stTagHeadInfo.iTagSize+4)
{
return true;
}
else
{
return false;
}
}
}
找到TAG 类型FLV_TAG_TYPE_META没有解析,只解析了 FLV_TAG_TYPE_AUDIO和FLV_TAG_TYPE_VIDEO
解析 FLV_TAG_TYPE_AUDIO数据,一般音频如MP3直接保存,AAC需要特殊处理。
bool ParserAudioTag(unsigned char*pData,TagHeadInfo &stTagHeadInfo)
{
unsigned char*p = pData+stTagHeadInfo.iHeadLen;
int iUseLen =0;
//视频flag (帧类型和帧编码格式)
int flags = avio_r8(&p);
iUseLen+=1;
stTagHeadInfo.iAudioCodeType = flags & FLV_AUDIO_CODECID_MASK;
stTagHeadInfo.iChans = (flags & FLV_AUDIO_CHANNEL_MASK) == FLV_STEREO ? 2 : 1;
stTagHeadInfo.iSampleRate = 44100 << ((flags & FLV_AUDIO_SAMPLERATE_MASK) >>
FLV_AUDIO_SAMPLERATE_OFFSET) >> 3;
stTagHeadInfo.iSampleBits = (flags & FLV_AUDIO_SAMPLESIZE_MASK) ? 16 : 8;
//AAC 特殊处理
if(stTagHeadInfo.iAudioCodeType == FLV_CODECID_AAC)
{
int AACPacketType = avio_r8(&p);
iUseLen+=1;
//The AudioSpecificConfig is explained in ISO 14496-3
if(AACPacketType == 0)
{
memcpy(g_FlvInfo.stAACInfo.pAacSpecificConfig, p , stTagHeadInfo.iTagSize - iUseLen);
g_FlvInfo.stAACInfo.iAacSpecificConfigLen = stTagHeadInfo.iTagSize - iUseLen;
//解析AudioSpecificConfig结构
aac_decode_extradata(&(g_FlvInfo.stAACInfo.stADTSContext), g_FlvInfo.stAACInfo.pAacSpecificConfig, g_FlvInfo.stAACInfo.iAacSpecificConfigLen);
}
//
else if(AACPacketType == 1)
{
unsigned char szAdtsHead[ADTS_HEADER_SIZE];
aac_set_adts_head(&(g_FlvInfo.stAACInfo.stADTSContext), szAdtsHead, stTagHeadInfo.iTagSize-iUseLen);
printf("write aac autio adts:%d\n",ADTS_HEADER_SIZE);
fwrite(szAdtsHead, 1,ADTS_HEADER_SIZE, gAudioOutputFile);
printf("write aac autio data:%d\n",stTagHeadInfo.iTagSize-iUseLen);
fwrite(p, stTagHeadInfo.iTagSize-iUseLen, 1, gAudioOutputFile);
}
else
{
}
}
else
{
printf("write autio data:%d\n",stTagHeadInfo.iTagSize-iUseLen);
fwrite(p, stTagHeadInfo.iTagSize-iUseLen, 1, gAudioOutputFile);
}
return true;
}
处理FLV_TAG_TYPE_VIDEO,这里主要保存SPS/PPS信息,遇到I帧需要加上SPS/PPS信息。注意一个TAG里面有多个包的情况,一般一个TAG里面是P帧,或者SEI+PPS+SPS+I帧1+I帧2等,注意处理好。I帧可能分成几个包。每个包格式都是"长度+数据"。
//长度上层已经保证
bool ParserVideoTag(unsigned char*pData,TagHeadInfo &stTagHeadInfo)
{
unsigned char*p = pData+stTagHeadInfo.iHeadLen;
int iUseLen =0;
//视频flag (帧类型和帧编码格式)
int flags = avio_r8(&p);
iUseLen+=1;
int flvCodeId = flags& FLV_VIDEO_CODECID_MASK;
//只支持H264目前
switch (flvCodeId)
{
case FLV_CODECID_H263:
break;
case FLV_CODECID_REALH263:
break; // Really mean it this time
case FLV_CODECID_SCREEN:
break;
case FLV_CODECID_SCREEN2:
break;
case FLV_CODECID_VP6:
case FLV_CODECID_VP6A:
break;
//标准里面的结构 AVCVIDEOPACKET
case FLV_CODECID_H264:
{
/* 0: AVC sequence header
1: AVC NALU
2: AVC end of sequence (lower level NALU
sequence ender is not required or supported)
*/
int AVCPacketType = avio_r8(&p);
iUseLen+=1;
//CompositionTime
//CTS 是PTS-DTS 一般没有B帧时cts=0
int32_t cts = (avio_rb24(&p) + 0xff800000) ^ 0xff800000;
iUseLen+=3;
stTagHeadInfo.pts = stTagHeadInfo.dts + cts;
//AVCDecoderConfigurationRecord
if(AVCPacketType == 0)
{
//生成sps和pps 保存
h264_AVCDecoderConfigurationRecord_2SpsPpsSerial(p);
}
//一个TAG里可能有多个NALU 所以是NALUS,可能是把一帧分成几个包如
//一个I帧 分成 (包1长度+包1数据+包2长度+包2数据),处理时需要改成
//00000001+包1数据+000001+包2数据
else if(AVCPacketType == 1)
{
h264_NalusParser(p,stTagHeadInfo.iTagSize - iUseLen );
}
else
{
}
}
break;
case FLV_CODECID_MPEG4:
break;
default:
break;
}
unsigned char*p1 = pData+stTagHeadInfo.iHeadLen+stTagHeadInfo.iTagSize;
int iPreTagLen = avio_rb32(&p1);
if(iPreTagLen != stTagHeadInfo.iHeadLen+stTagHeadInfo.iTagSize)
{
printf("PreviousTagSize Error\n");
}
return true;
}