共计 2171 个字符,预计需要花费 6 分钟才能阅读完成。
最近在用 C ++ 和 FFmpeg 做屏幕录制工具时,遇到了一个经典问题:合成的视频画面正常,但完全没有声音。经过一番折腾终于解决了这个问题,这里把排查过程和解决方案整理成笔记,希望能帮到遇到同样问题的朋友。

问题背景
当我们尝试用 FFmpeg 同时捕获麦克风音频和桌面画面时,理想情况下应该输出一个音画同步的 MP4 文件。但实际运行时经常会出现以下现象:
- 视频文件能正常播放且画面完整
- 视频时长符合预期
- 但播放时完全没有声音
- 用 ffprobe 检查发现视频中可能根本没有音频流
原因分析
通过调试和查阅文档,发现导致无声的常见原因主要有以下几个:
- 音频流未被正确添加:在输出容器中忘记添加音频流,或者添加了但格式不匹配
- 编码器配置错误:音频编码器的采样率、声道数等参数与输入设备不匹配
- 时间基准不一致 :音频和视频使用不同的时间基准(time_base) 导致同步失败
- 数据包写入顺序 :音视频帧写入顺序混乱导致复用器(muxer) 丢弃音频包
- 设备权限问题:麦克风设备未被正确打开或系统权限不足
解决方案
下面是修正后的关键代码片段(使用 FFmpeg 4.4+ API):
// 初始化音频编码器上下文
AVCodecContext* setup_audio_codec(AVFormatContext* fmt_ctx) {const AVCodec* audio_codec = avcodec_find_encoder(fmt_ctx->oformat->audio_codec);
AVCodecContext* c = avcodec_alloc_context3(audio_codec);
c->bit_rate = 128000;
c->sample_fmt = audio_codec->sample_fmts[0];
c->sample_rate = 44100; // 必须与输入设备一致
c->channel_layout = AV_CH_LAYOUT_STEREO;
c->channels = 2;
c->time_base = (AVRational){1, c->sample_rate}; // 关键!音频时间基准
if (fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
avcodec_open2(c, audio_codec, nullptr);
return c;
}
// 主录制循环中的关键处理
while (recording) {
// 优先处理音频包(防止队列积压)if (av_compare_ts(audio_next_pts, audio_ctx->time_base,
video_next_pts, video_ctx->time_base) <= 0) {AVPacket pkt = get_audio_packet();
pkt.stream_index = audio_stream->index;
pkt.pts = audio_next_pts;
pkt.dts = pkt.pts;
audio_next_pts += pkt.duration;
av_interleaved_write_frame(fmt_ctx, &pkt); // 必须用 interleaved 写入
av_packet_unref(&pkt);
} else {// 处理视频帧...}
}
几个关键点说明:
- 音频时间基准 (time_base) 必须设置为
1/ 采样率 - 写入数据包时必须使用
av_interleaved_write_frame保证音视频交错 - PTS 计算要基于各自流的时间基准
- 每次循环优先处理音频防止队列积压
避坑指南
根据踩坑经验,整理出 5 个常见错误及解决方法:
- 错误:直接使用 av_write_frame
- 现象:视频能播但音频时有时无
-
解决:改用 av_interleaved_write_frame 保证交错写入
-
错误:忽略 AVFrame 的 nb_samples
- 现象:音频杂音或播放速度异常
-
解决:确保每个音频帧的样本数匹配编码器要求
-
错误:时间基准混用
- 现象:音视频不同步越来越严重
-
解决:视频用 1 /framerate,音频用 1 /sample_rate
-
错误:未检查设备采样格式
- 现象:无法打开音频设备
-
解决:先用 avdevice_list_input_sources 检查支持格式
-
错误:遗漏音频流 disposition
- 现象:播放器不识别音频流
- 解决:设置 stream->disposition = AV_DISPOSITION_DEFAULT
性能优化
音频处理对性能的影响经常被低估,以下是几个优化方向:
- 缓冲策略:
- 音频捕获建议使用独立线程 + 环形缓冲区
-
缓冲区大小 = 采样率×声道数×2(16bit)×100ms
-
硬件加速:
- 使用 AVFrame 的 hw_frames_ctx
-
考虑 CUDA 或 QSV 编码器
-
采样率选择:
- 语音场景可用 16kHz 替代 44.1kHz
-
单声道比立体声节省 50% 流量
-
异步处理:
- 音频编码使用独立线程
- 通过队列与主线程通信
总结与扩展
通过这次调试,深刻理解了 FFmpeg 音视频同步的复杂性。建议后续可以尝试:
- 动态比特率调整 (VBR) 提高语音质量
- 添加降噪滤波器(如 anlmdn)
- 实现多轨道音频混合
- 探索 WebRTC 的音频处理模块
FFmpeg 的功能远比表面看到的强大,期待大家挖掘出更多高级用法。如果遇到其他音频相关问题,欢迎一起讨论交流!
正文完
