音视频入门基础:FLV专题(21)——FFmpeg源码中,获取FLV文件音频信息的实现(上)
由于本文篇幅较长,分为上下两篇。
一、引言
通过FFmpeg命令可以获取到FLV文件的音频压缩编码格式、音频采样率、通道数、音频码率信息:
./ffmpeg -i XXX.flv
而由《音视频入门基础:FLV专题(9)——Script Tag简介》和《音视频入门基础:FLV专题(18)——Audio Tag简介》可以知道:FLV文件中名称为“onMetadata”的Script Tag、每个Audio Tag的AudioTagHeader、AudioSpecificConfig都会包含音频信息。
名称为“onMetadata”的Script Tag:
每个Audio Tag的AudioTagHeader:
AudioSpecificConfig:
可以看到,FLV文件中的上述三个地方都会存在音频信息,有些信息比如:音频压缩编码格式、音频采样率、通道数甚至是重复的。所以FFmpeg到底获取的是哪个地方的音频信息呢,本文为大家揭开谜底。
二、音频压缩编码格式
FFmpeg获取FLV文件的音频压缩编码格式,获取的是Audio Tag的AudioTagHeader中的音频压缩编码格式。由《音视频入门基础:FLV专题(18)——Audio Tag简介》可以知道,AudioTagHeader存在一个占4位的SoundFormat属性,表示音频的压缩编码格式:
0:Linear PCM, platform endian
1:ADPCM
2:MP3
3:Linear PCM, little endian
4:Nellymoser 16 kHz mono
5:Nellymoser 8 kHz mono
6:Nellymoser
7:G.711 A-law logarithmic PCM
8:G.711 mu-law logarithmic PCM
9:reserved
10:AAC
11:Speex
14:MP3 8 kHz
15:Device-specific sound
由《音视频入门基础:FLV专题(19)——FFmpeg源码中,解码Audio Tag的AudioTagHeader,并提取AUDIODATA的实现》可以知道,FFmpeg源码中使用flv_read_packet函数来读取每个Tag的信息。如果判断出该Tag为Audio Tag,flv_read_packet函数中会通过下面代码块将AudioTagHeader的SoundFormat属性提取出来,转换得到音频压缩编码格式。将音频压缩编码格式赋值给st->codecpar->codec_id中。st->codecpar为指向一个AVCodecParameters类型变量的指针:
if (!st->codecpar->codec_id) {
flv_set_audio_codec(s, st, st->codecpar,
flags & FLV_AUDIO_CODECID_MASK);
//...
}
然后在flv_read_packet函数外部,通过avcodec_parameters_to_context函数将AVCodecParameters的codec_id赋值给AVCodecContext的codec_id:
int avcodec_parameters_to_context(AVCodecContext *codec,
const AVCodecParameters *par)
{
//...
codec->codec_id = par->codec_id;
//...
}
然后在dump_stream_format函数中,通过avcodec_string函数中的语句:codec_name = avcodec_get_name(enc->codec_id) 拿到AVCodecContext的codec_id对应的音频压缩编码格式名称。最后再在dump_stream_format函数中将音频压缩编码格式打印出来:
void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode)
{
//...
codec_name = avcodec_get_name(enc->codec_id);
//...
}
所以FFmpeg获取FLV文件的音频压缩编码格式,获取的是Audio Tag的AudioTagHeader中的音频压缩编码格式:
三、音频压缩编码格式的profile
音频压缩编码格式还有附带的profile(规格)。比如音频压缩编码格式为AAC,根据《ISO14496-3-2009.pdf》第124页,还有AAC Main、AAC LC、AAC SSR、AAC LTP这几种规格:
FFmpeg获取FLV文件的音频压缩编码格式的profile,获取的是AudioSpecificConfig中的audioObjectType。由《音视频入门基础:AAC专题(11)——AudioSpecificConfig简介》可以知道,FLV文件中的音频为AAC时,正常情况下它必定存在一个Audio Tag包含Audio Specific Config,而Audio Specific Config中存在一个占5位或11位的audioObjectType属性,表示音频对象类型:
0: Null
1: AAC Main
2: AAC LC (Low Complexity)
3: AAC SSR (Scalable Sample Rate)
4: AAC LTP (Long Term Prediction)
5: SBR (Spectral Band Replication)
6: AAC Scalable
7: TwinVQ
8: CELP (Code Excited Linear Prediction)
9: HXVC (Harmonic Vector eXcitation Coding)
10: Reserved
11: Reserved
12: TTSI (Text-To-Speech Interface)
13: Main Synthesis
14: Wavetable Synthesis
15: General MIDI
16: Algorithmic Synthesis and Audio Effects
17: ER (Error Resilient) AAC LC
18: Reserved
19: ER AAC LTP
20: ER AAC Scalable
21: ER TwinVQ
22: ER BSAC (Bit-Sliced Arithmetic Coding)
23: ER AAC LD (Low Delay)
24: ER CELP
25: ER HVXC
26: ER HILN (Harmonic and Individual Lines plus Noise)
27: ER Parametric
28: SSC (SinuSoidal Coding)
29: PS (Parametric Stereo)
30: MPEG Surround
31: (Escape value)
32: Layer-1
33: Layer-2
34: Layer-3
35: DST (Direct Stream Transfer)
36: ALS (Audio Lossless)
37: SLS (Scalable LosslesS)
38: SLS non-core
39: ER AAC ELD (Enhanced Low Delay)
40: SMR (Symbolic Music Representation) Simple
41: SMR Main
42: USAC (Unified Speech and Audio Coding) (no SBR)
43: SAOC (Spatial Audio Object Coding)
44: LD MPEG Surround
45: USAC
由《音视频入门基础:AAC专题(12)——FFmpeg源码中,解码AudioSpecificConfig的实现》可以知道,FFmpeg源码中使用decode_audio_specific_config_gb函数来读取AudioSpecificConfig的信息。decode_audio_specific_config_gb函数中会调用ff_mpeg4audio_get_config_gb函数,而ff_mpeg4audio_get_config_gb函数中,通过语句:c->object_type = get_object_type(gb) 获取AudioSpecificConfig的audioObjectType属性。执行decode_audio_specific_config_gb函数后,m4ac指向的变量会得到从AudioSpecificConfig中解码出来的属性:
static inline int get_object_type(GetBitContext *gb)
{
int object_type = get_bits(gb, 5);
if (object_type == AOT_ESCAPE)
object_type = 32 + get_bits(gb, 6);
return object_type;
}
然后在decode_audio_specific_config_gb函数外部,通过aac_decode_frame_int函数将上一步得到的audioObjectType属性赋值给AVCodecContext的profile:
static int aac_decode_frame_int(AVCodecContext *avctx, AVFrame *frame,
int *got_frame_ptr, GetBitContext *gb,
const AVPacket *avpkt)
{
//...
// The AV_PROFILE_AAC_* defines are all object_type - 1
// This may lead to an undefined profile being signaled
ac->avctx->profile = ac->oc[1].m4ac.object_type - 1;
//...
}
然后在dump_stream_format函数中,通过avcodec_string函数中的语句:profile = avcodec_profile_name(enc->codec_id, enc->profile)拿到上一步中得到的AVCodecContext的profile。最后再在dump_stream_format函数中将profile打印出来:
void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode)
{
//...
profile = avcodec_profile_name(enc->codec_id, enc->profile);
//...
}
所以FFmpeg获取FLV文件的音频压缩编码格式的profile,获取的是AudioSpecificConfig中的audioObjectType: