1、flv格式标准
参考手册:video_file_format_spec_v10_1.pdf (附件可以下载)
2、flv头信息解析
通过手册的68页,在flv文件的前面9个字节的数据中识别出为flv文件,是否包含音频,视频包含视频信息;
3、flv的body信息解析
接下来的就是解析flv的tag信息,在tag的前面有4个字节的数据,这个数据是表示前面一个tag的长度为多少,像第一个数据,前面是没有tag的,所以always 0;
在每一个tag里面也会有这个tag的长度信息,我们就可以把这个tag给提取出来,分析是里面是音频帧数据还是视频帧数据,长度是多少,解析完后,可以通过后面的previous tag size 来进行校验;
4、flv的tag数据的解析
通过上面的分析,大家可以提取出tag的数据出来,下面我们对tag的数据进行解析,tag 数据里面会有一个tagtype,通过这个位段的数据,我们可以分析出这个tag是音频数据、视频数据、medadata信息;
再解析对应的数据;
5、关于音频tag数据的解析
我们解析只分析aac数据的解析,这里的前面2个字节的tag头信息,里面会包含音频的类型、采样率、位宽、声道信息;后面的数据是adts的raw数据,也就是这个数据是没有adts的固定头和可变头信息的;
如果我们需要把数据还原为aac的数据,必须补充前面的7个字节的信息,通过上面解析出来的音频信息组包7字节作为adts的raw数据的头,这样解析出来的音频才能正常的播放出来;
aac协议的解析可以参考:
https://bbs.eeworld.com.cn/thread-478714-1-1.html
<font size="3"> if((aac_config_data.SoundFormat == FLV_CODECID_AAC) && (aac_config_data.AACPacketType == 0x01)) // 0x00 Synchronous data, 0x01 raw data
{
// dynamic parameters
switch (aac_config_data.SoundRate)
{
case FLV_SAMPLERATE_44100HZ:
adts_header_data.sampling_frequency_index = 4;
break;
case FLV_SAMPLERATE_22050HZ:
adts_header_data.sampling_frequency_index = 7;
break;
case FLV_SAMPLERATE_11025HZ:
adts_header_data.sampling_frequency_index = 10;
break;
case FLV_SAMPLERATE_SPECIAL:
adts_header_data.sampling_frequency_index = 11; // 8kHz
break;
default:
adts_header_data.sampling_frequency_index = 4;
break;
}
switch (aac_config_data.SoundType)
{
case FLV_MONO:
adts_header_data.channel_configuration = 1;
break;
case FLV_STEREO:
adts_header_data.channel_configuration = 2;
break;
default:
adts_header_data.channel_configuration = 2;
break;
}
adts_header_data.aac_frame_length = flvdemux.flv_tag.date_size + 5;
adts_write_frame_header_buf(adts_header_buf,&adts_header_data);
fwrite(adts_header_buf,7,1,fp_audio_destfile);
fwrite(flvdemux.flv_tag.data+2,flvdemux.flv_tag.date_size-2,1,fp_audio_destfile);
}</font> 复制代码
6、关于视频tag数据的解析
下面我只针对视频H264数据来进行分析,下面是video tag 的结构信息,通过avc packet type来分析出这个数据是同步数据包还是编码数据包;
如果这个数据包是avc sequence header的数据,可以按下面的协议的来进行解析;
ISO 14496-15, 5.2.4.1 for the description of AVCDecoderConfigurationRecord. This contains the same
information that would be stored in an avcC box in an MP4/FLV file.
如果是avc nalu 数据,可以之间提取出来就可以了;
这里我们需要注意的一个地方是,拿到avc数据后,提取处理的时候需要加上nalu的头 00 00 00 01信息,否则数据解析出来后,播放器会无法播放出来;
<font size="3"> if(flvdemux.flv_tag.video_data.codec_id != FLV_CODECID_H264 )
{
printf("FLV format video coding is not avc, unable to properly parse !!! \n");
return 0;
}
if(flvdemux.flv_tag.video_data.frame_type == 1 &&
flvdemux.flv_tag.video_data.codec_id == 7 &&
flvdemux.flv_tag.video_data.AVCPacketType == 0 &&
flvdemux.flv_tag.video_data.CompositionTime == 0)
{
//AVC sequence header , sps and pps
avc_config_data.configurationVersion = flvdemux.flv_tag.data[i++];
avc_config_data.AVCProfileIndication = flvdemux.flv_tag.data[i++];
avc_config_data.profile_compatibility = flvdemux.flv_tag.data[i++];
avc_config_data.AVCLevelIndication = flvdemux.flv_tag.data[i++];
avc_config_data.lengthSizeMinusOne = flvdemux.flv_tag.data[i++] & 0x3;
avc_config_data.numOfSequenceParameterSets = flvdemux.flv_tag.data[i++] & 0x1f;
for(num = 0;num < avc_config_data.numOfSequenceParameterSets;num++)
{
length_a = flvdemux.flv_tag.data[i++];
length_b = flvdemux.flv_tag.data[i++];
avc_config_data.sequenceParameterSetLength = (length_a << 8 ) | length_b;
sequence_buf[0] = 0x00;
sequence_buf[1] = 0x00;
sequence_buf[2] = 0x00;
sequence_buf[3] = 0x01;
memcpy(sequence_buf+4,flvdemux.flv_tag.data+i,avc_config_data.sequenceParameterSetLength);
fwrite(sequence_buf,avc_config_data.sequenceParameterSetLength+4,1,fp_video_destfile);
i+=avc_config_data.sequenceParameterSetLength;
}
avc_config_data.numOfPictureParameterSets = flvdemux.flv_tag.data[i++] & 0xff;
for(num = 0;num < avc_config_data.numOfPictureParameterSets;num++)
{
length_a = flvdemux.flv_tag.data[i++];
length_b = flvdemux.flv_tag.data[i++];
avc_config_data.pictureParameterSetLength = (length_a << 8 ) | length_b;
sequence_buf[0] = 0x00;
sequence_buf[1] = 0x00;
sequence_buf[2] = 0x00;
sequence_buf[3] = 0x01;
memcpy(sequence_buf+4,flvdemux.flv_tag.data+i,avc_config_data.pictureParameterSetLength);
fwrite(sequence_buf,avc_config_data.pictureParameterSetLength+4,1,fp_video_destfile);
i+=avc_config_data.sequenceParameterSetLength;
}</font> 复制代码
7、关于metadata的tag数据的解析;
方法大致和上面的差不多;
有一个比较不错的文章,大家可以来参考;
http://blog.chinaunix.net/uid-11344913-id-3996948.html
或者看下面的代码对应解析也可以的;
8、flv解析源代码,和相关的文档