ffmpeg libopus编码器不支持直接写入pcm裸流,必须严格配置采样率(48000等)、通道布局、av_sample_fmt_s16格式,对齐opus帧长(如480样本),显式设置priv_data参数(application/vbr等),手动处理ogg头或确保mp4封装正确,并通过swr_convert完成重采样与格式转换。

ffmpeg\_libopus编码器不支持直接写入PCM裸流
直接用 avcodec_send_frame() 向 libopus 编码器喂 PCM 数据会失败,常见错误是 AVERROR(EINVAL) 或编码器静默丢帧。根本原因是 Opus 编码器要求输入必须是符合其采样率、通道布局和样本格式的 AVFrame,且需提前设置好 AVCodecContext 的全部参数——尤其是 codec_id 必须设为 AV_CODEC_ID_OPUS,而不能靠自动探测。
实操要点:
-
AVCodecContext->sample_rate必须是 Opus 支持的值:8000 / 12000 / 16000 / 24000 / 48000(推荐 48000) -
AVCodecContext->channels和AVCodecContext->channel_layout必须匹配,例如双声道需同时设channels = 2和channel_layout = AV_CH_LAYOUT_STEREO -
AVCodecContext->sample_fmt必须为AV_SAMPLE_FMT_S16(Opus 编码器不接受AV_SAMPLE_FMT_FLTP或AV_SAMPLE_FMT_S32) - PCM 数据需按 Opus 帧长对齐:默认帧长 20ms → 480 个样本(48kHz 下),否则
avcodec_send_frame()可能返回AVERROR(EAGAIN)
必须手动配置Opus专用选项(如application、vbr)
Opus 编码行为高度依赖 AVCodecContext->priv_data 中的私有选项,若不显式设置,输出音质差、延迟高或文件无法被标准播放器识别。FFmpeg 的 libopus 封装层不会自动补全这些关键参数。
典型配置项(通过 av_opt_set() 设置):
-
application:设为"voip"(低延迟语音)或"audio"(通用音频),影响频宽和算法策略 -
vbr:设为"on"或"off",开启后需配合compression_level和frame_duration -
packet_loss:整数百分比(如"5"),用于抗丢包优化,非流媒体可设为"0" -
max_bandwidth:如"fullband"、"wideband",控制最高频响上限
示例代码片段:
av_opt_set(codec_ctx->priv_data, "application", "audio", AV_OPT_SEARCH_CHILDREN); av_opt_set(codec_ctx->priv_data, "vbr", "on", AV_OPT_SEARCH_CHILDREN); av_opt_set(codec_ctx->priv_data, "compression_level", "10", AV_OPT_SEARCH_CHILDREN);
封装成Ogg/MP4时务必写入Opus头(特别是Ogg)
Ogg 容器要求前两个页(page)必须是 OpusHead 和 OpusTags,否则多数播放器(包括 Chrome、VLC)拒绝解码。FFmpeg 的 libopus 编码器本身不生成这些头;你必须手动构造并写入。
关键步骤:
- 调用
avcodec_parameters_from_context()获取AVCodecParameters后,用avformat_write_header()—— 它会触发 FFmpeg 内部自动生成 OpusHead(仅当AVOutputFormat->flags & AVFMT_GLOBALHEADER且封装格式为 Ogg 时) - 若手动写包,需用
av_packet_rescale_ts()对齐时间戳,并确保第一个AVPacket的flags包含AV_PKT_FLAG_KEY - MP4 封装相对宽松,但需确认
AVOutputFormat->name是"mp4"且codec_ctx->codec_id == AV_CODEC_ID_OPUS,否则 muxer 可能拒绝写入
常见坑:avformat_write_header() 返回成功不代表头已写出——需检查返回值是否为 0,且后续调用 av_interleaved_write_frame() 前不能修改 codec_ctx 参数。
PCM数据预处理:重采样与格式转换不可省略
原始 PCM 往往是 16-bit LE、单声道、44.1kHz,而 Opus 要求 48kHz + stereo + AV_SAMPLE_FMT_S16。直接 memcpy 会导致静音、爆音或编码崩溃。
必须走 FFmpeg 的 swr_convert() 流程:
- 初始化
SwrContext*时,out_sample_rate设为 48000,out_channel_layout与codec_ctx->channel_layout一致 -
swr_convert()输入缓冲区长度需按输出采样率换算:例如输入 1024 样本 @ 44.1kHz → 输出约 1119 样本 @ 48kHz,需动态分配输出缓冲 - 输出缓冲大小按公式计算:
max_out_samples = av_rescale_rnd(in_samples, out_rate, in_rate, AV_ROUND_UP) - 每次
swr_convert()后检查实际输出样本数,不足一帧(如 480)则缓存,凑够再送入编码器
漏掉重采样或格式转换,avcodec_send_frame() 可能返回 0(成功假象),但输出包为空或损坏。
Opus 编码真正的难点不在 API 调用链,而在参数对齐:采样率、帧长、容器头、重采样精度,四者缺一不可。任意一项错位,结果都是无声或解码失败,且错误提示极其模糊。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











