译文说明
本文由 Hy4 preview 机器翻译自韩文原文,仅供参考。技术细节请以韩文原文为准。
AAC 音频转码各参数实测对比
概述
处理视频转码时,注意力很容易集中在分辨率、码率、GOP、Profile 这类视频参数上。但实际播放质量的咨询中,相当大一部分来自音频轨。比如画面明明正常、音频听感却发闷;或者只在特定设备上音频偏向一侧;再或者低码率档位下听感格外粗糙。
本文将针对 AAC 音频转码中经常调整的三个参数——sampling rate、audio bitrate、channel 配置——在 FFmpeg 与腾讯云 MPS 两侧实际反复调整,并用 ffprobe 验证产物。
素材是一段 10 秒片段:720p H.264 视频,附带 32000Hz 立体声 AAC 音频,是由 AI 生成的合成内容。
实战场景:收到「低码率流里音频不对劲」的咨询
假设收到这样一条咨询:移动端低码率档位(音频 32kbps 上下)下音频会出现破音。此时需要确认的问题大致分为三个。
第一,当请求 32kbps 这样低的码率时,编码器是原样采用该值,还是会为了产出可播放的结果而悄悄降低 sample rate 或 channel 数。
第二,同样是 32kbps,mono 与 stereo 哪个更稳妥。第三,在基于预设的托管服务(MPS)中,这些值能被精细控制到什么程度。
为了用实测而非直觉回答这三个问题,我用同一素材在 FFmpeg 原生 AAC 编码器与 MPS 两侧,把相同的参数组合各跑了多次。
FFmpeg 实测:sampling rate
只改 -ar 值,码率(128k)与声道(立体声)保持不变。
ffmpeg -y -i src.mp4 -vn -c:a aac -b:a 128k -ar 48000 -ac 2 ar_48000.m4a
ffmpeg -y -i src.mp4 -vn -c:a aac -b:a 128k -ar 44100 -ac 2 ar_44100.m4a
ffmpeg -y -i src.mp4 -vn -c:a aac -b:a 128k -ar 22050 -ac 2 ar_22050.m4a用 ffprobe 确认的结果如下。
| 请求 sample rate | 实际 sample_rate | channels | 实测 bit_rate | 文件大小 |
|---|---|---|---|---|
| 48000 | 48000 | 2 | 128,133 bps | 162,418 bytes |
| 44100 | 44100 | 2 | 128,228 bps | 162,415 bytes |
| 22050 | 22050 | 2 | 124,178 bps | 156,868 bytes |
请求的 sample rate 在三种情况下都被精确照原样体现。降到 22050Hz 时文件略有变小,与其说是 sample rate 本身所致,不如说是因为编码器在低 sample rate 条件下未能完全达到目标码率(124,178 对请求值 128,000)。这种程度的偏差属于正常范围。
FFmpeg 实测:audio bitrate
这次固定 sample rate(44100)与声道(立体声),只改 -b:a。
ffmpeg -y -i src.mp4 -vn -c:a aac -b:a 320k -ar 44100 -ac 2 br_320k.m4a
ffmpeg -y -i src.mp4 -vn -c:a aac -b:a 128k -ar 44100 -ac 2 br_128k.m4a
ffmpeg -y -i src.mp4 -vn -c:a aac -b:a 64k -ar 44100 -ac 2 br_64k.m4a
ffmpeg -y -i src.mp4 -vn -c:a aac -b:a 32k -ar 44100 -ac 2 br_32k.m4a| 请求 bitrate | 实测 bit_rate | 文件大小 |
|---|---|---|
| 320k | 261,755 bps | 328,910 bytes |
| 128k | 128,228 bps | 162,415 bytes |
| 64k | 64,252 bps | 82,643 bytes |
| 32k | 32,205 bps | 42,683 bytes |
128k、64k、32k 的请求值与实测值几乎完全一致。但唯独 320k 的实测值只有 261,755 bps,仅约为请求值的 82%。
这并非偶然,而与 FFmpeg 原生 AAC 编码器内部的硬上限有关。下面单独深挖这一部分。
为什么 320k 无法原样输出:6144 比特帧上限
FFmpeg 原生 AAC 编码器(aac)严格遵守 AAC 规范规定的每声道帧比特上限(每 1024 采样帧、每声道最多 6144 比特)。请求的码率一旦超过该上限,就会在控制台留下警告并强制降低数值。
是否超过该上限,取决于 요청 비트레이트 × 1024 / sample_rate 是否超过 6144 × channel 수。
为验证这个公式是否真的成立,我用四种边界条件做了检验。
# 모노 44100Hz에 320k 요청 → 7430 > 6144(모노 캡), 클램프 발동
ffmpeg -y -loglevel warning -i src.mp4 -vn -c:a aac -b:a 320k -ar 44100 -ac 1 mono_320k.m4a
# [aac @ ...] Too many bits 7430.385488 > 6144 per frame requested, clamping to max
# 실측 bit_rate=168,336
# 스테레오 44100Hz에 320k 요청 → 7430 < 12288(스테레오 캡), 클램프 미발동
ffmpeg -y -loglevel warning -i src.mp4 -vn -c:a aac -b:a 320k -ar 44100 -ac 2 stereo_320k.m4a
# 경고 없음, 실측 bit_rate=261,755
# 스테레오 22050Hz에 320k 요청 → 14860 > 12288(스테레오 캡), 클램프 발동
ffmpeg -y -loglevel warning -i src.mp4 -vn -c:a aac -b:a 320k -ar 22050 -ac 2 s22_320k.m4a
# [aac @ ...] Too many bits 14860.770975 > 12288 per frame requested, clamping to max
# 실측 bit_rate=147,440
# 모노 22050Hz에 128k 요청 → 5942 < 6144(모노 캡), 클램프 미발동
ffmpeg -y -loglevel warning -i src.mp4 -vn -c:a aac -b:a 128k -ar 22050 -ac 1 s22_mono_128k.m4a
# 경고 없음, 실측 bit_rate=99,785四个条件都与公式精确吻合。这里引出一个重要的实战要点:立体声 44100Hz 下请求 320k 的情况,虽然没有触发钳制警告,实测值(261,755 bps)却比请求值低了近 18%。
也就是说,即便没有撞上硬上限,原生 AAC 编码器也可能因内容特性而无法完全填满请求的码率。这次实测确认:像 320kbps 这样对 AAC 而言相当高的取值,务必用 ffprobe 复核产物。
反过来说,128k 以下区间内请求值与实测值几乎完全一致,因此实战中常用的 64k~128k 区间可以放心使用。
FFmpeg 实测:channel 配置
固定 sample rate(44100)与 bitrate(128k),只改声道。
ffmpeg -y -i src.mp4 -vn -c:a aac -b:a 128k -ar 44100 ch_original.m4a # -ac 생략, 원본 채널 유지
ffmpeg -y -i src.mp4 -vn -c:a aac -b:a 128k -ar 44100 -ac 1 ch_mono.m4a
ffmpeg -y -i src.mp4 -vn -c:a aac -b:a 128k -ar 44100 -ac 2 ch_stereo.m4a| 配置 | channels | channel_layout | 实测 bit_rate | 文件大小 |
|---|---|---|---|---|
保持原始(省略 -ac) | 2 | stereo | 128,228 bps | 162,415 bytes |
-ac 1(mono) | 1 | mono | 128,992 bps | 163,367 bytes |
-ac 2(stereo) | 2 | stereo | 128,228 bps | 162,415 bytes |
由于原始素材本身就是立体声,省略 -ac 与 -ac 2 得到了完全相同的结果。有意思的是,mono 结果的文件大小反而比 stereo 略大(163,367 对 162,415 bytes)。
这是因为在请求同样 128kbps 时,mono 会把全部比特集中到一个声道,使每声道比特密度达到 stereo 的两倍,而编码器确实把这份余量全部填满了。这个案例说明:在保持相同码率的前提下,「mono 就一定文件更小」这个通识并不成立。
极低码率(32kbps)下 mono 与 stereo 对比
我直接验证了实战场景中提出的问题:32kbps 下 mono 与 stereo 哪个更安全。
ffmpeg -y -i src.mp4 -vn -c:a aac -b:a 32k -ar 44100 -ac 1 extreme_32k_mono.m4a
ffmpeg -y -i src.mp4 -vn -c:a aac -b:a 32k -ar 44100 -ac 2 extreme_32k_stereo.m4a| 配置 | channels | 实测 bit_rate | 文件大小 |
|---|---|---|---|
| 32k, mono | 1 | 32,390 bps | 42,914 bytes |
| 32k, stereo | 2 | 32,205 bps | 42,683 bytes |
两者都原样保持了请求的 sample rate 与 channel 数,编码器没有偷偷做 mono 下混或降低 sample rate 的动作。只不过,把 32kbps 分摊到立体声两个声道上,每声道的实际比特密度就只有 mono 的一半水平。
咨询里提到的「低码率下听感粗糙」这一症状,很可能并非编码器扭曲了数值,而是「立体声分摊 32kbps」这个配置本身造成了每声道信息量不足。同样的 32kbps 预算,编码为 mono 的每声道音质余量更大。
与其只用文字说明,不如直接试听。以下是对同一素材以 32kbps、仅改变声道编码的结果。
32kbps, mono
32kbps, stereo
试听下来,stereo 一侧听感更闷、细节更糊,而 mono 一侧虽同为 32kbps,听感却相对清晰。可见每声道实际比特密度相差一倍,不只是数字上的差异,而是确实能被感知到的差别。
参考:素材为立体声时请求上混到 5.1(6 声道)
用 -ac 6 把立体声素材强制指定为 6 声道,FFmpeg 会在没有任何特别警告的情况下完成编码。
ffmpeg -y -i src.mp4 -vn -c:a aac -b:a 320k -ar 48000 -ac 6 upmix_6ch.m4a结果显示为 channels=6、channel_layout=5.1。不过由于原始素材并非真正的 5.1 混音而是立体声,这并非在环绕声道中填入了真正具有空间感的内容,而只是把声道布局标签改成了 5.1 的机械式上混。这也算是借实测确认了:增加声道数与制作真正的环绕混音是两件不同的事。
MPS 实测:用 RawParameter 直接指定音频参数
如果不想用 MPS 中已注册的预设模板、而要即时指定参数,就要把 RawParameter 与 Definition: 0 一起使用。这里首先确认的一点是:即便只想改音频,只要把 RawParameter.Container 和 RawParameter.VideoTemplate.Codec 留空,请求就会被拒绝。
# AudioTemplate만 채우고 VideoTemplate을 생략한 첫 시도
task["RawParameter"] = {
"Container": "mp4",
"AudioTemplate": {"Codec": "aac", "Bitrate": 128, "SampleRate": 44100, "AudioChannel": 2},
}
# 결과: InvalidParameterValue.VideoCodec is null如果想彻底去掉视频轨、只处理音频,可以指定 RemoveVideo: 1。这样即使没有 VideoTemplate,请求也能通过。
task["RawParameter"] = {
"Container": "mp4",
"RemoveVideo": 1,
"AudioTemplate": {"Codec": "aac", "Bitrate": 128, "SampleRate": 44100, "AudioChannel": 2},
}MPS 实测:sampling rate
我分别把 48000/44100/32000/22050 填入 SampleRate 做了测试。
| 请求 SampleRate | 实际 sample_rate | channels | 实测 bit_rate | 文件大小 |
|---|---|---|---|---|
| 48000 | 48000 | 2 | 129,086 bps | 163,264 bytes |
| 44100 | 44100 | 2 | 129,049 bps | 163,066 bytes |
| 32000 | 32000 | 2 | 129,194 bps | 162,766 bytes |
| 22050 | 22050 | 2 | 129,614 bps | 162,912 bytes |
四个值都被原样体现,实测 bit_rate 也几乎精确贴合请求的 128kbps。反而比 FFmpeg 的 22050Hz 结果(124,178 bps)更接近目标值,这应该是 MPS 转码流水线在填满目标码率上的码率控制调校与 FFmpeg 原生 AAC 编码器不同所致。
MPS 实测:audio bitrate 的 256kbps 硬截断
我把 Bitrate 值依次提升为 256、288、320(kbps)进行测试。
# Bitrate: 256 → 성공
# Bitrate: 288 → InvalidParameterValue.AudioBitrate(288) for Container(mp4)
# Bitrate: 320 → InvalidParameterValue.AudioBitrate(320) for Container(mp4)到 256kbps 为止都能正常处理,但从 288kbps 起请求本身就被拒绝了。这一点是与 FFmpeg 差异最明显的地方。
FFmpeg 原生 AAC 编码器在收到 320kbps 请求时会先接受,再因内部上限而悄悄(或伴随警告)降低数值后处理。相反,MPS 对 mp4 容器中的 AAC 音频明确划定了 256kbps 的有效码率上限,超过该值的请求直接以 InvalidParameterValue 被弹回。
也就是说,它宁可在请求阶段直接失败、而不是悄悄改动数值。至于哪种更安全取决于视角,但至少在「不必去调试为什么产物与请求不一致」这一点上,MPS 更清晰。
我也一并确认了低码率区间。
| 配置 | channels | 实测 bit_rate | 文件大小 |
|---|---|---|---|
| 32kbps, mono | 1 | 32,532 bps | 42,998 bytes |
| 32kbps, stereo | 2 | 32,814 bps | 43,349 bytes |
与 FFmpeg 一样,即便在 32kbps 极低码率下,也没有偷偷降低 sample rate 或 channel 数的动作。
MPS 实测:channel 配置与 5.1 上混
我比较了 AudioChannel 字段留空、指定为 1、指定为 2 三种情况。
| 配置 | 实际 channels | channel_layout | 备注 |
|---|---|---|---|
省略 AudioChannel | 2 | stereo | 保持原始(立体声)声道 |
AudioChannel: 1 | 1 | mono | |
AudioChannel: 2 | 2 | stereo | 与省略时连 MD5 都完全一致 |
省略 AudioChannel 会保持原始声道数(本素材为立体声);在本就已是立体声的这个案例中,省略与显式指定 AudioChannel: 2 产出了字节级完全一致的结果(MD5 相同)。
我也实际试了 5.1(6 声道)选项。
audio_template = {"Codec": "aac", "Bitrate": 128, "SampleRate": 48000, "AudioChannel": 6}该请求成功执行且无报错,产物的实际声道信息准确显示为 channels=6、channel_layout=5.1。也就是说,MPS 的 RawParameter 自定义编码路径正式支持 AudioChannel: 6,也能把立体声素材上混到 6 声道处理。
如果把 3、4、8 这类非 1/2/6 的值填入 AudioChannel,会分别以 InvalidParameterValue.AudioChannel(3)、(4)、(8) 被明确拒绝,因此该字段接受的有效值实际上只有四种:未指定(保持原始)/1(mono)/2(stereo)/6(5.1)。
不过在控制台注册的标准预设模板中,这个选项并未暴露。我用 DescribeTranscodeTemplates 把 290 个预设类型模板全部扫了一遍,AudioChannel 值无一例外全是 2(立体声)。
总结一下:mono 与 5.1 声道指定虽不在标准预设目录中,但在 RawParameter 自定义编码路径里已作为 API 层级的功能正式支持。如果因为在预设列表里找不到 mono 或 5.1 选项就以为不支持,建议用 RawParameter 直接指定试试。
MPS 实测中发现的附带陷阱:批量提交与失败传播
在这次实测中,我还发现了一个与参数本身无关、但值得注意的运维陷阱。一次 ProcessMedia 调用可以用 TranscodeTaskSet 数组一次性提交多个转码任务;但只要这个数组里混入了任何一个参数无效的任务,同批次的其他任务也会全部被判为失败。
实测中我一次性提交了 4 个任务,Bitrate 分别指定为 320、128、64、32(kbps);结果尽管只有 320 超出了有效范围,128/64/32 的任务也全部以 FAIL 状态结束,四个任务都原样返回了相同的错误信息(InvalidParameterValue.AudioBitrate(320) for Container(mp4))。而 128/64/32 单独提交时是能正常成功的取值。
也就是说,MPS 似乎会先校验批次内的所有任务,只要有一个被拦下就作废整个批次。一次性测试多种参数组合、或在生产中批量提交时,把每个组合拆成单独请求、或提前做取值校验会更安全。
小结
综合在 FFmpeg 与 MPS 两侧对三个参数的实测结果如下。
| 参数 | FFmpeg 原生 AAC | MPS RawParameter |
|---|---|---|
| Sampling rate | 原样体现请求值。低 sample rate 下实测 bit_rate 可能略低于目标 | 原样体现请求值。更精确地收敛到 128kbps 目标 |
| Audio bitrate | 超过每声道 6144 比特/帧上限则自动钳制(会留下警告日志)。即便在上限内,高码率也可能达不到目标 | 以 mp4 容器为准,超过 256kbps 的请求会以 InvalidParameterValue 被立即拒绝。256 以下精确收敛到目标值 |
| Channel | 保持原始/mono/stereo 均被精确体现。与声道数无关,请求码率保持不变(只是密度不同)。也可用 -ac 6 做机械式上混 | 省略 AudioChannel 时保持原始声道。只有 1/2/6 为有效值,其他值会被明确拒绝。6(5.1)在 API 层级也已正式支持,但未暴露在 290 个标准预设中 |
两条路径在极低码率(32kbps)下都没有偷偷降低 sample rate 或 channel 数的动作。只不过当取值超出有效范围时,两者的应对方式明显不同。
FFmpeg 会一直接受到编码器内部规范上限,超出后再悄悄降低;而 MPS 则在超出服务划定的有效范围的瞬间就拒绝请求本身。诊断低码率流中的音频问题时,要牢记这个差异,以 ffprobe 确认的实际值为判断依据,而非请求值,这样才安全。
本次实测所用的 COS 对象(audio-transcode-test/ 下的源文件和转码产物)已在验证结束后全部删除。