音视频入门基础:FLV专题(21)——FFmpeg源码中,获取FLV文件音频信息的实现(上)

由于本文篇幅较长,分为上、中、下三篇。

一、引言

通过FFmpeg命令可以获取到FLV文件的音频压缩编码格式、音频采样率、通道数、音频码率信息:

cpp 复制代码
./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类型变量的指针:

cpp 复制代码
        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:

cpp 复制代码
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函数中将音频压缩编码格式打印出来:

cpp 复制代码
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中解码出来的属性:

cpp 复制代码
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:

cpp 复制代码
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打印出来:

cpp 复制代码
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:

相关推荐
EasyCVR6 小时前
智能守护校园:视频汇聚平台EasyCVR如何构建校园消防安全新防线
音视频
mortimer12 小时前
一键实现人声伴奏分离:基于 `uv`, `FFmpeg` 和 `audio-separator` 的高效解决方案
python·ffmpeg·音视频开发
加油201912 小时前
音视频处理(三):hls协议和m3u8详解和视频下载爬虫实战
爬虫·音视频·hls·m3u8·mpeg-2·mpeg2-ts·电视迷
音视频牛哥14 小时前
从 SmartMediaKit 设计探讨音视频工程师的面试与能力评估框架
程序人生·职场和发展·音视频·rtsp播放器·rtmp播放器·音视频工程师面试·音视频程序员面试题
18538162800航14 小时前
短视频矩阵系统搭建指南:源码部署与全流程解析
线性代数·矩阵·音视频
美摄科技15 小时前
AR短视频SDK,打造差异化竞争壁垒
ar·音视频
sunly_15 小时前
Flutter:视频预览功能
javascript·flutter·音视频
热爱编程的小白白15 小时前
IPIDEA海外代理助力-Youtube视频AI领域选题数据获取实践
人工智能·音视频
骄傲的心别枯萎21 小时前
RV1126 NO.40:OPENCV图形计算面积、弧长API讲解
人工智能·opencv·计算机视觉·音视频·rv1126
撬动未来的支点1 天前
【音视频】MP4文件格式
音视频