服务端6月4日 13:52
FFmpeg Filter语法详解:缩放、裁剪、叠加和组合滤镜FFmpeg 的 Filter 是它区别于其他转码工具的核心能力——不只是格式转换,而是对画面的像素、音频的采样点做任意变换。但 Filter 的语法是出了名的难读:方括号标签、分号逗号混用、多个输入输出的管线。这篇文章先把语法规则讲透,再按场景列举常用滤镜。
## Filter 语法:三分钟搞懂
### 简单滤镜 vs 复杂滤镜
- `-vf "滤镜链"`:简单视频滤镜,单个输入,单个输出
- `-af "滤镜链"`:简单音频滤镜,同上
- `-filter_complex "滤镜图"`:复杂滤镜,可以有多个输入输出、分支和合并
能用 `-vf`/`-af` 解决的,不要用 `-filter_complex`——简单滤镜更快更不容易出错。
### 语法规则
```bash
# 逗号:串联同一链的滤镜,前一个的输出是后一个的输入
-vf "scale=1280:720,crop=640:360"
# 分号:分隔不同的滤镜链(用于 -filter_complex)
-filter_complex "[0:v]scale=1280:720[v1];[1:v]scale=640:360[v2]"
# 方括号标签:给输入/输出命名
-filter_complex "[0:v]scale=1280:720[big];[1:v]scale=640:360[small];[big][small]overlay=10:10"
```
`[0:v]` 表示第一个输入的视频流,`[0:a]` 表示第一个输入的音频流。`[big]`、`[small]` 是自定义标签,后续滤镜用这个标签引用。
### 最常见的语法错误
```bash
# 错误:简单滤镜里用了分号
-vf "scale=1280:720;crop=640:360" # 报错
# 正确:简单滤镜用逗号
-vf "scale=1280:720,crop=640:360" # OK
```
## 视频滤镜
### 缩放(scale)
```bash
# 固定分辨率
ffmpeg -i input.mp4 -vf "scale=1280:720" output.mp4
# 保持宽高比(只指定宽度,高度自动计算)
ffmpeg -i input.mp4 -vf "scale=1280:-1" output.mp4
# 等比缩小到一半
ffmpeg -i input.mp4 -vf "scale=iw/2:ih/2" output.mp4
# 限制最大尺寸(不超过 1280x720,小的保持原尺寸)
ffmpeg -i input.mp4 -vf "scale='min(1280,iw)':'min(720,ih)'" output.mp4
```
`-1` 是"自动计算"的写法,FFmpeg 根据原始宽高比算出高度。注意:某些编码器要求宽高都是偶数,如果算出来是奇数会报错,可以用 `scale=1280:trunc(ow/2)*2` 强制偶数。
### 裁剪(crop)
```bash
# 从中心裁剪 640x480(默认居中)
ffmpeg -i input.mp4 -vf "crop=640:480" output.mp4
# 指定裁剪起点(左上角偏移 100,50)
ffmpeg -i input.mp4 -vf "crop=640:480:100:50" output.mp4
# 自动检测黑边并裁掉
ffmpeg -i input.mp4 -vf "cropdetect" -f null - 2>&1 | grep crop
# 然后用输出的 crop 值
ffmpeg -i input.mp4 -vf "crop=1920:800:0:140" output.mp4
```
`cropdetect` 是调优利器——先跑一遍检测黑边参数,再用 crop 裁掉。
### 叠加(overlay)
给视频加 logo、画中画都用 overlay:
```bash
# 左上角加 logo
ffmpeg -i video.mp4 -i logo.png -filter_complex "overlay=10:10" output.mp4
# 右下角加 logo(W=视频宽, w=logo宽)
ffmpeg -i video.mp4 -i logo.png -filter_complex "overlay=W-w-10:H-h-10" output.mp4
# 居中画中画
ffmpeg -i main.mp4 -i pip.mp4 -filter_complex "[1:v]scale=320:240[pip];[0:v][pip]overlay=(W-w)/2:(H-h)/2" output.mp4
```
overlay 的坐标用 `x:y` 格式,支持表达式。`W` 和 `H` 是主视频的宽高,`w` 和 `h` 是叠加层的宽高。
### 旋转(transpose)
```bash
# 顺时针 90 度
ffmpeg -i input.mp4 -vf "transpose=1" output.mp4
# 逆时针 90 度
ffmpeg -i input.mp4 -vf "transpose=2" output.mp4
# 180 度(两次 90 度)
ffmpeg -i input.mp4 -vf "transpose=1,transpose=1" output.mp4
# 水平翻转(镜像)
ffmpeg -i input.mp4 -vf "hflip" output.mp4
```
旋转后分辨率会变(1080x1920 变 1920x1080),如果编码器有分辨率限制需要注意。
### 文字水印(drawtext)
```bash
# 静态文字
ffmpeg -i input.mp4 -vf "drawtext=text='Hello':fontcolor=white:fontsize=24:x=10:y=10" output.mp3
# 带阴影的文字(提高可读性)
ffmpeg -i input.mp4 -vf "drawtext=text='Hello':fontcolor=white:fontsize=32:x=10:y=10:shadowcolor=black:shadowx=2:shadowy=2" output.mp4
# 显示时间戳
ffmpeg -i input.mp4 -vf "drawtext=text='%{pts\:hms}':fontcolor=white:fontsize=20:x=10:y=10" output.mp4
```
drawtext 需要编译时启用 libfreetype。如果报 "Unknown filter 'drawtext'",说明你的 FFmpeg 没有这个支持。
### 模糊和锐化
```bash
# 高斯模糊
ffmpeg -i input.mp4 -vf "gblur=sigma=2" output.mp4
# 锐化
ffmpeg -i input.mp4 -vf "unsharp=5:5:1.0" output.mp4
```
模糊常用于背景虚化或隐私遮挡,锐化常用于低分辨率素材的提升(但过度锐化会产生光晕伪影)。
## 音频滤镜
音频滤镜用 `-af`,语法和视频一样:
```bash
# 音量调整
ffmpeg -i input.mp4 -af "volume=2.0" output.mp4
# 淡入淡出
ffmpeg -i input.mp4 -af "afade=t=in:st=0:d=3" output.mp4
# 混合两路音频
ffmpeg -i voice.mp3 -i bgm.mp3 -filter_complex "amix=inputs=2:duration=first" output.mp3
# 延迟
ffmpeg -i input.mp4 -af "adelay=500|500" output.mp4
```
音频滤镜的详细用法参见"FFmpeg 音频处理"一文。
## 组合滤镜实战
### 四宫格
```bash
ffmpeg -i v1.mp4 -i v2.mp4 -i v3.mp4 -i v4.mp4 -filter_complex "[0:v]scale=640:360[v0];[1:v]scale=640:360[v1];[2:v]scale=640:360[v2];[3:v]scale=640:360[v3]; [v0][v1]hstack[top];[v2][v3]hstack[bottom];[top][bottom]vstack" output.mp4
```
`hstack` 水平拼接,`vstack` 垂直拼接。所有输入的分辨率必须一致。
### 视频变速
```bash
# 2 倍速
ffmpeg -i input.mp4 -filter_complex "[0:v]setpts=0.5*PTS[v];[0:a]atempo=2.0[a]" -map "[v]" -map "[a]" output.mp4
# 0.5 倍速
ffmpeg -i input.mp4 -filter_complex "[0:v]setpts=2.0*PTS[v];[0:a]atempo=0.5[a]" -map "[v]" -map "[a]" output.mp4
```
视频用 `setpts` 调速(乘以系数),音频用 `atempo`。atempo 范围 0.5-2.0,超出要链式:`atempo=2.0,atempo=2.0` 达到 4 倍速。
### 加 logo + 时间戳 + 淡入
```bash
ffmpeg -i input.mp4 -i logo.png -filter_complex "[0:v]drawtext=text='%{pts\:hms}':fontcolor=white:fontsize=20:x=10:y=10[vt]; [vt][1:v]overlay=W-w-10:10" -af "afade=t=in:st=0:d=2" output.mp4
```
## 性能提示
滤镜是 CPU 密集操作,优化思路:
- **滤镜顺序**:先 crop 再 scale,减少处理的像素量
- **GPU 滤镜**:NVIDIA 用 `scale_npp` 替代 `scale`,`hwupload_cuda` 后在 GPU 上做滤镜
- **避免不必要的滤镜**:每个滤镜都增加一帧的处理时间,去掉不必要的
- **预览时降低分辨率**:调参时加 `scale=640:360` 加快迭代,确认效果后再用原始分辨率输出标签
FFmpeg
FFmpeg 是一个开源的跨平台解决方案,用于录制、转换和流式传输音频和视频。它提供了强大的命令行工具,可以处理多种多样的多媒体格式,并提供了一组丰富的库和组件,使得开发者能够在自己的应用程序中集成音视频处理功能。

服务端6月4日 13:51
FFmpeg性能优化:硬件加速对比、preset选择和实测数据FFmpeg 默认配置偏保守,处理 1080p 视频可能只有 0.5x 实时速度——1 小时的视频要转 2 小时。但调整几个参数就能提到 5x 甚至 20x。这篇文章从硬件加速、编码参数、多线程三个层面讲优化,并给出不同场景的推荐配置。
## 硬件加速方案对比
四种主流硬件加速,按你的 GPU 选择:
| 方案 | 适用硬件 | 编码器 | 解码参数 | 特点 |
|------|----------|--------|----------|------|
| NVENC/NVDEC | NVIDIA GPU | h264_nvenc, hevc_nvenc | -hwaccel cuda | 生态最成熟,质量接近 x264 medium |
| Intel QSV | Intel 集显/独显 | h264_qsv, hevc_qsv | -hwaccel qsv | 低延迟,适合转码服务器 |
| AMD AMF | AMD GPU | h264_amf, hevc_amf | -hwaccel d3d11va | Windows 为主 |
| VideoToolbox | Apple 芯片/Mac | h264_videotoolbox | -hwaccel videotoolbox | macOS 原生,M 系列芯片性能极强 |
### NVIDIA GPU 加速
```bash
# GPU 解码 + GPU 编码(全程不经过 CPU,最快)
ffmpeg -hwaccel cuda -hwaccel_output_format cuda -i input.mp4 -c:v h264_nvenc -preset fast -b:v 5M output.mp4
# GPU 解码 + CPU 编码(质量要求高时)
ffmpeg -hwaccel cuda -i input.mp4 -c:v libx264 -preset slow output.mp4
# 指定使用哪块 GPU(多卡服务器)
ffmpeg -hwaccel_device 0 -hwaccel cuda -i input.mp4 -c:v h264_nvenc output.mp4
```
`-hwaccel_output_format cuda` 让解码后的帧留在 GPU 显存里,避免 GPU→CPU→GPU 的数据搬运。全程 GPU 处理比"GPU 解码 + CPU 编码"快 2-3 倍。
### Intel QSV 加速
```bash
ffmpeg -hwaccel qsv -i input.mp4 -c:v h264_qsv -preset fast -b:v 5M output.mp4
```
QSV 在 Intel 集显上性能不错,低功耗场景(如 NAS 转码)比 NVIDIA 方案更省电。但编码质量不如 NVENC。
### Apple VideoToolbox
```bash
ffmpeg -i input.mp4 -c:v h264_videotoolbox -b:v 5M output.mp4
```
M1/M2/M3 芯片上 VideoToolbox 编码 4K 视频可以跑到实时 10x 以上。但 FFmpeg 对 VideoToolbox 的参数控制不如 NVENC 丰富。
### 硬件加速的局限
- 硬件编码器的 CRF 控制不如 x264 精细,同码率下质量通常比 x264 slow 低一档
- 硬件编码器的 B 帧策略和参考帧数量受限,某些高级参数不支持
- 如果要用滤镜(如 drawtext、overlay),帧通常需要从 GPU 搬回 CPU,抵消加速效果
- 不同硬件编码器的输出比特率波动大,不适合对码率有严格要求的场景
## 编码参数优化
### x264 preset 选择
```bash
# 追求速度(实时转码、预览)
ffmpeg -i input.mp4 -c:v libx264 -preset veryfast -crf 23 output.mp4
# 平衡速度和质量(通用场景)
ffmpeg -i input.mp4 -c:v libx264 -preset medium -crf 23 output.mp4
# 追求质量(离线存档)
ffmpeg -i input.mp4 -c:v libx264 -preset slow -crf 18 output.mp4
```
实测数据(1080p 25fps,i7-12700K):
| preset | 编码速度 | 相同 CRF 下的体积 |
|--------|----------|-------------------|
| ultrafast | 120fps (5x) | +40% |
| veryfast | 80fps (3x) | +20% |
| fast | 55fps (2x) | +10% |
| medium | 35fps (1.4x) | 基准 |
| slow | 12fps (0.5x) | -8% |
从 veryfast 到 slow,速度差 7 倍但体积只省 20%。大多数场景选 veryfast 或 fast 就够了。
### CRF 值选择
CRF(Constant Rate Factor)是 x264 的质量控制参数,0 是无损,51 是最差:
| CRF | 质量 | 场景 |
|-----|------|------|
| 18 | 视觉无损 | 存档、后期素材 |
| 23 | 默认 | 通用 |
| 28 | 明显压缩痕 | 预览、小体积 |
| 32 | 画质差 | 仅可辨认内容 |
CRF 和 preset 是独立的:CRF 控制质量目标,preset 控制达到该目标的效率。同一 CRF 下,slow preset 的文件更小但质量一样。
### 线程数
```bash
# 自动检测(推荐,通常等于 CPU 核心数)
ffmpeg -i input.mp4 -threads 0 -c:v libx264 output.mp4
# 手动指定(给其他任务留核心)
ffmpeg -i input.mp4 -threads 4 -c:v libx264 output.mp4
```
x264 的线程数不是越多越好——超过 16 线程后编码效率开始下降(切片并行导致的参考帧问题)。4K 视频推荐 8-16 线程,1080p 推荐 4-8 线程。
## 批量处理
### GNU Parallel 多文件并行
```bash
# 同时处理 4 个文件,每个占一个 GPU
find input/ -name "*.mp4" | parallel -j 4 ffmpeg -i {} -c:v h264_nvenc output/{/.}.mp4
```
多文件并行比单文件多线程更高效——FFmpeg 单进程的线程扩展性有限,但多个进程各占一个核心/GPU 可以线性扩展。
### 分段处理大文件
```bash
# 先分成小段
ffmpeg -i input.mp4 -c copy -f segment -segment_time 60 segment_%03d.mp4
# 并行转码每一段
for f in segment_*.mp4; do
ffmpeg -i "$f" -c:v libx264 -preset fast transcoded_"$f" &
done
wait
# 合并
echo "$(for f in transcoded_segment_*.mp4; do echo "file '$PWD/$f'"; done)" | ffmpeg -f concat -safe 0 -i - -c copy output.mp4
```
分段处理要确保每段都有关键帧开头(`-c copy` 分段依赖关键帧),否则合并不连续。
## 内存优化
处理超大文件(4K+ 长视频)时内存可能不够:
```bash
# 限制缓冲区大小
ffmpeg -i input.mp4 -c:v libx264 -bufsize 2M output.mp4
# 流式处理(不落盘)
ffmpeg -i rtmp://source/live -c:v libx264 -f flv rtmp://target/live
```
## 推荐配置速查
| 场景 | 命令 |
|------|------|
| 实时转码 | `-c:v h264_nvenc -preset fast -b:v 4M` |
| 离线高质量 | `-c:v libx264 -preset slow -crf 18` |
| 批量转码 | `-c:v libx264 -preset veryfast -crf 23` + GNU Parallel |
| 小体积 | `-c:v libx264 -preset medium -crf 28` |
| Mac 本地 | `-c:v h264_videotoolbox -b:v 5M` |服务端6月4日 13:49
FFmpeg C API集成:解码、编码和最容易踩的坑FFmpeg 的命令行工具够用的话,没人愿意碰它的 C API——函数多、生命周期复杂、版本间 API 变动频繁。但当你要做实时流处理、自定义滤镜链、或者把音视频能力嵌入产品时,命令行就不够了。这篇文章把 FFmpeg API 集成的核心流程讲清楚:从打开文件到解码、从编码到输出,以及最容易踩的坑。
## 核心库和职责
| 库 | 职责 | 你什么时候会用到 |
|---|---|---|
| libavformat | 封装格式(容器)读写 | 打开文件、读写 MP4/MKV/FLV 等 |
| libavcodec | 编解码 | 解码 H.264/AAC,编码 H.265/Opus |
| libavutil | 工具函数 | 内存分配、数学运算、日志 |
| libswscale | 图像缩放和色彩转换 | YUV → RGB、分辨率缩放 |
| libswresample | 音频重采样 | 采样率转换、声道布局转换 |
| libavfilter | 滤镜 | 视频加水印、音频降噪 |
不需要全部链接,按需引入。只做解码的话,`libavformat` + `libavcodec` + `libavutil` 就够。
## 解码流程:从文件到原始帧
### 1. 初始化
```c
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
// FFmpeg 4.0+ 不需要手动注册,老版本需要:
// av_register_all();
avformat_network_init(); // 如果要处理网络流(RTMP/HLS)
```
版本差异是最大的坑之一。FFmpeg 4.0 废弃了 `av_register_all()`,5.0 废弃了 `avcodec_register_all()`,新版自动注册所有内置编解码器。如果你的代码还要兼容 3.x,加版本判断:
```c
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 0, 0)
av_register_all();
#endif
```
### 2. 打开文件、找流
```c
AVFormatContext *fmt_ctx = NULL;
int ret = avformat_open_input(&fmt_ctx, "input.mp4", NULL, NULL);
if (ret < 0) {
char errbuf[128];
av_strerror(ret, errbuf, sizeof(errbuf));
fprintf(stderr, "无法打开文件: %s\n", errbuf);
return -1;
}
avformat_find_stream_info(fmt_ctx, NULL);
```
`avformat_open_input` 只打开文件头,不读帧数据。`avformat_find_stream_info` 读几帧探测流信息(编码器、分辨率、帧率),如果省略这步,后续 `codecpar` 里的信息可能不完整。
### 3. 找视频流、打开解码器
```c
// 找第一个视频流
int video_idx = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if (video_idx < 0) {
fprintf(stderr, "没找到视频流\n");
return -1;
}
// 打开解码器
AVCodecParameters *codecpar = fmt_ctx->streams[video_idx]->codecpar;
const AVCodec *codec = avcodec_find_decoder(codecpar->codec_id);
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
avcodec_parameters_to_context(codec_ctx, codecpar);
avcodec_open2(codec_ctx, codec, NULL);
```
`av_find_best_stream` 比 手动遍历 `nb_streams` 更好——它会根据流的质量和语言偏好选最优的。
### 4. 读取和解码帧
FFmpeg 3.1+ 使用 send/receive 模型:
```c
AVPacket *pkt = av_packet_alloc();
AVFrame *frame = av_frame_alloc();
while (av_read_frame(fmt_ctx, pkt) >= 0) {
if (pkt->stream_index != video_idx) {
av_packet_unref(pkt);
continue;
}
ret = avcodec_send_packet(codec_ctx, pkt);
if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
break;
}
while (ret >= 0) {
ret = avcodec_receive_frame(codec_ctx, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break;
if (ret < 0) break;
// frame->data[0] 就是 YUV 数据
// frame->width, frame->height, frame->format 可用
process_frame(frame);
}
av_packet_unref(pkt);
}
```
send/receive 是异步的:一个 packet 可能产生多个 frame(如 B 帧重排序),也可能多个 packet 才产出一个 frame(音频解码缓冲)。`EAGAIN` 不是错误,意思是"再发一个 packet 过来"。
### 5. 冲刷解码器
读取完所有帧后,解码器里可能还缓存着几帧,需要冲刷:
```c
avcodec_send_packet(codec_ctx, NULL); // NULL 触发冲刷
while (avcodec_receive_frame(codec_ctx, frame) != AVERROR_EOF) {
process_frame(frame);
}
```
## 编码流程:从原始帧到文件
编码是解码的逆过程,但多了输出容器的初始化:
```c
// 创建输出上下文
AVFormatContext *out_ctx = NULL;
avformat_alloc_output_context2(&out_ctx, NULL, NULL, "output.mp4");
// 添加视频流
AVStream *out_stream = avformat_new_stream(out_ctx, NULL);
const AVCodec *encoder = avcodec_find_encoder(AV_CODEC_ID_H264);
AVCodecContext *enc_ctx = avcodec_alloc_context3(encoder);
enc_ctx->width = 1280;
enc_ctx->height = 720;
enc_ctx->time_base = (AVRational){1, 25}; // 时间基准
enc_ctx->framerate = (AVRational){25, 1};
enc_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
enc_ctx->gop_size = 10;
enc_ctx->max_b_frames = 1;
// 如果输出是 MP4,需要把编码器参数拷到流里
avcodec_open2(enc_ctx, encoder, NULL);
avcodec_parameters_from_context(out_stream->codecpar, enc_ctx);
// 打开输出文件
avio_open(&out_ctx->pb, "output.mp4", AVIO_FLAG_WRITE);
avformat_write_header(out_ctx, NULL);
```
编码帧和写文件:
```c
AVPacket *enc_pkt = av_packet_alloc();
// 对每个帧:
avcodec_send_frame(enc_ctx, frame);
while (avcodec_receive_packet(enc_ctx, enc_pkt) == 0) {
enc_pkt->stream_index = out_stream->index;
av_interleaved_write_frame(out_ctx, enc_pkt);
av_packet_unref(enc_pkt);
}
// 冲刷编码器
avcodec_send_frame(enc_ctx, NULL);
while (avcodec_receive_packet(enc_ctx, enc_pkt) == 0) {
enc_pkt->stream_index = out_stream->index;
av_interleaved_write_frame(out_ctx, enc_pkt);
av_packet_unref(enc_pkt);
}
// 写文件尾
av_write_trailer(out_ctx);
```
`av_interleaved_write_frame` 会自动按 DTS 排序后写入,比 `av_write_frame` 更安全。如果你不确定 DTS/PTS 的关系,用 interleaved 版本。
## 资源释放
FFmpeg 的资源释放顺序不能乱——先释放依赖项,再释放容器:
```c
av_frame_free(&frame);
av_packet_free(&pkt);
avcodec_free_context(&codec_ctx);
avformat_close_input(&fmt_ctx); // 输入
avcodec_free_context(&enc_ctx);
av_write_trailer(out_ctx);
avformat_close_input(&out_ctx); // 输出(如果用 avformat_close_input)
// 或者:
avio_closep(&out_ctx->pb);
avformat_free_context(out_ctx);
```
忘写 `av_write_trailer` 的话,MP4 文件的 moov atom 不会被写入,播放器无法打开。
## 错误处理的正确姿势
FFmpeg 的错误码是负数,用 `av_strerror` 转成可读字符串:
```c
char errbuf[AV_ERROR_MAX_STRING_SIZE] = {0};
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
fprintf(stderr, "错误: %s (code=%d)\n", errbuf, ret);
}
```
常见错误码:
- `AVERROR(EAGAIN)` — 需要更多输入,不是真错误
- `AVERROR_EOF` — 流结束,正常退出条件
- `AVERROR(EINVAL)` — 参数无效,检查传参
- `AVERROR(ENOMEM)` — 内存不足,检查是否有泄漏
- `AVERROR_EXIT` — 被 callback 终止
## 线程安全
FFmpeg API 大部分不是线程安全的。多线程环境下:
- 每个 `AVCodecContext` 只能被一个线程使用
- `AVFormatContext` 的读写操作需要加锁
- `av_log` 是线程安全的,可以放心在多线程中使用
- 推荐模式:一个线程负责读取和解码,通过队列把帧传给另一个线程编码
## 编译链接
```bash
# 查看链接需要的库
pkg-config --libs libavformat libavcodec libavutil
# 典型编译命令
gcc -o myapp myapp.c $(pkg-config --cflags --libs libavformat libavcodec libavutil libswscale)
```
静态链接时注意依赖顺序:`libavformat` 依赖 `libavcodec`,`libavcodec` 依赖 `libavutil`,链接顺序要反过来写。服务端6月4日 13:48
FFmpeg报错怎么排查?编码、格式和推流问题解决方案用 FFmpeg 处理音视频,报错信息常常一句话带过,搜索引擎给你的答案又是"试试这条命令"却不解释原因。这篇文章按错误类型分类,每个问题先说原因再说解决方案。
## 编码问题
### "Error while opening encoder"——编码器不可用
原因:FFmpeg 编译时没包含该编码器,或者编码器名称拼写错了。
```bash
# 先确认编码器是否可用
ffmpeg -encoders | grep h264
```
如果列表里没有 `libx264`,说明编译时没加 `--enable-libx264`。解决方案:重新编译 FFmpeg 或安装完整版。Ubuntu 上 `sudo apt install ffmpeg` 通常包含常用编码器;如果是从源码编译的,需要手动 enable。
检查编码器名称也容易出错:NVIDIA GPU 编码器叫 `h264_nvenc` 不是 `nvenc_h264`,Intel QSV 叫 `h264_qsv` 不是 `qsv_h264`。
### 编码速度太慢
原因:默认 preset 是 `medium`,追求质量但速度一般。
```bash
# 优先速度,牺牲一点质量
ffmpeg -i input.mp4 -c:v libx264 -preset veryfast output.mp4
# 极速(质量明显下降,适合预览)
ffmpeg -i input.mp4 -c:v libx264 -preset ultrafast output.mp4
# 有 GPU 的话直接用硬件编码
ffmpeg -hwaccel cuda -i input.mp4 -c:v h264_nvenc output.mp4
```
preset 从快到慢:`ultrafast` → `veryfast` → `fast` → `medium` → `slow` → `veryslow`。越慢质量越好、文件越小,但收益递减——`slow` 比 `medium` 慢 2-3 倍但只省 5-10% 体积。一般选 `veryfast` 或 `fast` 就够。
### 音视频不同步
原因通常是三种:源文件本身时间戳不对、编码时帧率设置错误、或者 `-async` 没处理音频漂移。
```bash
# 方案一:强制音频同步(简单粗暴)
ffmpeg -i input.mp4 -async 1 output.mp4
# 方案二:重新编码音视频(更可靠)
ffmpeg -i input.mp4 -c:v libx264 -c:a aac output.mp4
# 方案三:指定正确帧率
ffmpeg -i input.mp4 -r 25 output.mp4
```
`-async 1` 让音频时间戳强制对齐到视频,简单有效但可能引入音频跳变。如果源文件 PTS(Presentation Time Stamp)本身错乱,需要用 `-vsync cfr` 强制固定帧率。
## 格式问题
### "Unsupported codec"——格式不支持
```bash
# 查看 FFmpeg 支持的所有格式
ffmpeg -formats
# 查看某格式支持的编码器
ffmpeg -encoders | grep mp4
```
最常见的场景:MKV 转 MP4 时 MKV 里有字幕流或特殊编码,MP4 容器不支持。解决方案是只保留音视频流:
```bash
ffmpeg -i input.mkv -c:v libx264 -c:a aac -map 0:v:0 -map 0:a:0 output.mp4
```
`-map` 手动选择要保留的流,忽略字幕等不兼容的。
### 播放器兼容性差
某些播放器(特别是老款电视、浏览器)对 MP4 的 H.264 profile 和 level 有要求:
```bash
# 生成最大兼容性的 MP4
ffmpeg -i input.mp4 -c:v libx264 -profile:v baseline -level 3.0 -c:a aac output.mp4
```
`baseline` profile 去掉了 B 帧和高级编码特性,几乎所有设备都能播。代价是同质量下文件更大。加上 `-movflags +faststart` 让 moov atom 移到文件头部,网页播放不用等下载完:
```bash
ffmpeg -i input.mp4 -c:v libx264 -c:a aac -movflags +faststart output.mp4
```
## 流媒体问题
### RTMP 推流失败
常见原因:网络不通、推流地址格式错误、编码格式不兼容。
```bash
# 先确认网络连通
ping rtmp.server.com
# 推流时加 -re 按实际帧率发送(否则一口气全推出去,服务器跟不上)
ffmpeg -re -i input.mp4 -c copy -f flv rtmp://server/live/stream_key
# 加超时避免卡死
ffmpeg -re -i input.mp4 -timeout 5000000 -c copy -f flv rtmp://server/live/stream_key
```
`-re` 是推流的关键参数——不加的话 FFmpeg 会以最快速度把所有帧塞过去,服务器和观众端都会缓冲溢出。
### HLS 播放卡顿
```bash
# 增加分片时长(默认 2 秒太短,改 10 秒减少请求次数)
ffmpeg -i input.mp4 -f hls -hls_time 10 output.m3u8
# 缩短 GOP(关键帧间隔),确保每个分片都有关键帧
ffmpeg -i input.mp4 -c:v libx264 -g 25 -f hls -hls_time 10 output.m3u8
```
`-g 25` 每 25 帧一个关键帧(25fps 下就是 1 秒一个),确保 HLS 分片边界对齐关键帧,否则切分时会出现花屏。
## 滤镜问题
### "Invalid filterchain"——滤镜语法错误
滤镜语法是 FFmpeg 里最容易出错的部分。简单滤镜用 `-vf`/`-af`,复杂滤镜用 `-filter_complex`:
```bash
# 简单滤镜:单个输入
ffmpeg -i input.mp4 -vf "scale=1280:720" output.mp4
# 复杂滤镜:多个输入或需要中间标签
ffmpeg -i input.mp4 -filter_complex "[0:v]scale=1280:720[v]" -map "[v]" output.mp4
```
常见语法错误:标签没对应(定义了 `[v]` 但 `-map` 里写成 `[out]`)、分号和逗号混用(逗号连接同一链内的滤镜,分号分隔不同链)。
### 滤镜处理后速度变慢
原因:滤镜是纯 CPU 计算,有些滤镜(如去隔行、缩放)非常吃资源。
```bash
# 用 GPU 加速的缩放滤镜(NVIDIA)
ffmpeg -hwaccel cuda -i input.mp4 -vf "scale_npp=1280:720" output.mp4
# 优化滤镜顺序:先裁剪再缩放(处理更少的像素)
ffmpeg -i input.mp4 -vf "crop=640:480,scale=320:240" output.mp4
```
## 调试方法
遇到问题先看详细日志:
```bash
# 详细日志(看编码器选择、流信息)
ffmpeg -v verbose -i input.mp4 output.mp4
# 调试日志(看每一帧的处理)
ffmpeg -v debug -i input.mp4 output.mp4
# 只分析不编码(快速检查输入文件信息)
ffmpeg -i input.mp4 -f null -
```
性能基准测试:
```bash
# 测试解码速度
ffmpeg -benchmark -i input.mp4 -f null -
# 测试编码速度
ffmpeg -benchmark -i input.mp4 -c:v libx264 -f null -
```
`-benchmark` 会输出 `utime`(用户态耗时)、`stime`(内核态耗时)、`rtime`(实际耗时),用来对比不同参数的性能差异。服务端6月4日 13:47
FFmpeg音频处理速查:格式转换、调音量、剪辑混音和降噪FFmpeg 做音频处理,不需要打开 DAW,一条命令就能完成格式转换、音量调整、剪辑拼接、降噪混音。但很多教程只是罗列命令,参数什么意思、什么场景该选什么参数,一笔带过。这篇文章把常用音频操作按场景分类,每个命令都解释关键参数。
## 提取和格式转换
### 从视频里提取音频
```bash
ffmpeg -i video.mp4 -vn -acodec copy audio.aac
```
`-vn` 禁用视频流,`-acodec copy` 音频直接拷贝不重编码(速度极快,但输出格式必须和源一致)。如果要转格式,就不能 copy:
```bash
ffmpeg -i video.mp4 -vn -c:a libmp3lame -b:a 192k audio.mp3
```
`-c:a` 指定音频编码器,`-b:a` 指定比特率。192k 是 MP3 的甜点比特率,音质和体积平衡。
### 常见格式互转
```bash
# WAV → MP3(有损压缩,体积缩小约 10 倍)
ffmpeg -i input.wav -c:a libmp3lame -b:a 192k output.mp3
# FLAC → MP3(无损转有损,建议 320k 减少质量损失)
ffmpeg -i input.flac -c:a libmp3lame -b:a 320k output.mp3
# WAV → AAC(iOS/YouTube 常用)
ffmpeg -i input.wav -c:a aac -b:a 128k output.m4a
# WAV → Opus(同等音质下比特率最低,WebRTC 常用)
ffmpeg -i input.wav -c:a libopus -b:a 64k output.opus
```
选编码器的原则:兼容性选 AAC,体积最小选 Opus,无损存档选 FLAC,通用分享选 MP3。
## 音量调整
### 直接调整
```bash
ffmpeg -i input.mp3 -af "volume=2.0" output.mp3 # 音量翻倍
ffmpeg -i input.mp3 -af "volume=0.5" output.mp3 # 音量减半
```
### 分贝调整
```bash
ffmpeg -i input.mp3 -af "volume=3dB" output.mp3 # 增加 3dB
ffmpeg -i input.mp3 -af "volume=-3dB" output.mp3 # 减少 3dB
```
分贝调整比倍数调整更直观——人耳对音量的感知是对数的,3dB 大约是"刚能听出差别"的增量。
### 响度归一化
```bash
ffmpeg -i input.mp3 -af "loudnorm" output.mp3
```
`loudnorm` 把音频响度标准化到 EBU R128 标准(-16 LUFS)。批量处理多个文件时,用这个保证所有文件响度一致,避免切歌时音量忽大忽小。播客和音乐平台的响度标准都用它。
## 采样率和声道
### 调整采样率
```bash
ffmpeg -i input.wav -ar 44100 output.wav # CD 音质
ffmpeg -i input.wav -ar 48000 output.wav # 视频标准
ffmpeg -i input.wav -ar 16000 output.wav # 语音识别常用(够用且省空间)
```
采样率只能往下降不能无损往上升——44.1k 升 48k 不会凭空多出高频信息,反而可能引入重采样噪声。
### 声道操作
```bash
# 立体声转单声道
ffmpeg -i input.mp3 -ac 1 output.mp3
# 单声道转立体声(只是复制一份,不是真正的立体声)
ffmpeg -i input.mp3 -ac 2 output.mp3
# 提取左声道
ffmpeg -i input.mp3 -af "pan=mono|c0=c0" output.mp3
# 提取右声道
ffmpeg -i input.mp3 -af "pan=mono|c0=c1" output.mp3
```
## 剪辑和拼接
### 按时间剪辑
```bash
# 从第 10 秒开始,截取 30 秒
ffmpeg -i input.mp3 -ss 00:00:10 -t 00:00:30 -c copy output.mp3
# 从第 1 分钟到第 3 分钟
ffmpeg -i input.mp3 -ss 00:01:00 -to 00:03:00 -c copy output.mp3
```
`-c copy` 不重编码,秒级精度。如果需要帧级精度(某些格式),去掉 `-c copy` 让 FFmpeg 重编码,但速度会慢很多。
### 拼接多个音频
最可靠的方式是 concat 协议——先把文件列表写到文本里:
```bash
echo "file 'part1.mp3'" > filelist.txt
echo "file 'part2.mp3'" >> filelist.txt
ffmpeg -f concat -safe 0 -i filelist.txt -c copy output.mp3
```
前提:所有片段的编码参数必须一致(采样率、声道数、编码器)。不一致的话用 `-c copy` 会出问题,去掉 copy 让 FFmpeg 重编码即可。
## 音频特效
### 淡入淡出
```bash
# 开头淡入 3 秒
ffmpeg -i input.mp3 -af "afade=t=in:st=0:d=3" output.mp3
# 5 秒处开始淡出 3 秒
ffmpeg -i input.mp3 -af "afade=t=out:st=5:d=3" output.mp3
# 同时淡入淡出
ffmpeg -i input.mp3 -af "afade=t=in:st=0:d=3,afade=t=out:st=7:d=3" output.mp3
```
`st` 是起始时间,`d` 是持续时间。注意两个 afade 用逗号连在同一条 `-af` 里。
### 混音
```bash
# 两个音频混合,以第一个的时长为准
ffmpeg -i bgm.mp3 -i voice.mp3 -filter_complex "amix=inputs=2:duration=first" output.mp3
```
混合时音量会叠加,容易爆音。建议先降低各路音量再混合:
```bash
ffmpeg -i bgm.mp3 -i voice.mp3 -filter_complex "[0:a]volume=0.3[bgm];[1:a]volume=1.0[voice];[bgm][voice]amix=inputs=2" output.mp3
```
背景音乐 0.3 倍音量,人声 1.0 倍——播客和视频解说的典型配比。
### 变速
```bash
# 1.5 倍速
ffmpeg -i input.mp3 -af "atempo=1.5" output.mp3
# 0.75 倍速(慢放)
ffmpeg -i input.mp3 -af "atempo=0.75" output.mp3
```
**atempo 的范围是 0.5 到 2.0**。超出这个范围需要链式调用:
```bash
# 4 倍速 = 2.0 × 2.0
ffmpeg -i input.mp3 -af "atempo=2.0,atempo=2.0" output.mp3
```
很多人不知道这个限制,直接写 `atempo=4.0` 会报错。
### 降噪
简单的滤波降噪:
```bash
# 去除 200Hz 以下的低频噪声(电流声、风声)
ffmpeg -i input.mp3 -af "highpass=f=200" output.mp3
# 去除 3000Hz 以上的高频噪声(嘶嘶声)
ffmpeg -i input.mp3 -af "lowpass=f=3000" output.mp3
# 保留 200-3000Hz 的人声频段
ffmpeg -i input.mp3 -af "highpass=f=200,lowpass=f=3000" output.mp3
```
这种方式简单但粗糙——会把有效频段也一起砍掉。专业降噪需要用 Audition 或 RNNoise,FFmpeg 的滤波只能做初步处理。
## 音频信息查看
```bash
# 查看完整音频信息
ffprobe -i input.mp3 -show_streams -select_streams a
# 检测音量峰值和均值
ffmpeg -i input.mp3 -af "volumedetect" -f null -
```
`volumedetect` 输出 `max_volume` 和 `mean_volume`,用来判断是否需要调整音量。如果 `max_volume` 接近 0dB,说明已经接近削波,不要再增加音量。
## 中间处理用无损格式
多次处理同一个音频时(先调音量、再剪辑、再混音),每轮有损编码都会损失质量。正确做法:中间步骤用 WAV 或 FLAC,最后一步才转 MP3/AAC。
```bash
# 第一步:调音量,输出无损 WAV
ffmpeg -i input.mp3 -af "volume=2.0" intermediate.wav
# 第二步:剪辑 WAV
ffmpeg -i intermediate.wav -ss 10 -t 30 intermediate2.wav
# 第三步:最终转 MP3
ffmpeg -i intermediate2.wav -c:a libmp3lame -b:a 192k output.mp3
```服务端6月2日 00:01
FFmpeg 是什么?能做什么?核心组件和常用命令入门FFmpeg 是音视频领域的瑞士军刀——转码、剪辑、录屏、推流、截图、质量分析,一条命令搞定。它不是带界面的软件,而是命令行工具集,所有操作都在终端完成。OBS、VLC、YouTube 后台都用到了 FFmpeg。
## FFmpeg 能做什么
- **转码**:AVI 转 MP4、H.264 转 H.265、4K 降 1080p
- **剪辑**:截取视频片段、合并多个视频、去掉静音段
- **录屏**:录制屏幕和摄像头(macOS/Linux/Windows)
- **推流**:RTMP 直播推流、生成 HLS 流媒体
- **截图**:截取视频帧、批量生成缩略图
- **分析**:用 ffprobe 查看视频信息、用 PSNR/SSIM 评估画质
## 核心组件
FFmpeg 项目包含一组命令行工具和底层库:
| 工具 | 用途 |
|------|------|
| `ffmpeg` | 转码、剪辑、推流(最常用) |
| `ffprobe` | 查看音视频文件信息(不转码,只分析) |
| `ffplay` | 简易播放器(调试用) |
底层库(开发者在自己的程序中调用):
| 库 | 功能 |
|------|------|
| libavformat | 处理封装格式(MP4、MKV、FLV...) |
| libavcodec | 编解码(H.264、AAC、VP9...) |
| libavutil | 通用工具函数 |
| libswscale | 图像缩放和色彩空间转换 |
| libswresample | 音频重采样 |
日常只用 `ffmpeg` 和 `ffprobe` 两个命令。库是给开发者写程序用的,普通用户不用管。
## 最常用的 5 条命令
**1. 格式转换**
```bash
ffmpeg -i input.avi -c:v libx264 -c:a aac output.mp4
```
**2. 视频剪辑**
```bash
ffmpeg -ss 00:01:00 -i input.mp4 -t 30 -c copy output.mp4
```
从 1 分钟处截取 30 秒,`-c copy` 不重编码,秒级完成。
**3. 视频截图**
```bash
ffmpeg -i input.mp4 -ss 00:00:05 -vframes 1 screenshot.jpg
```
截取第 5 秒的一帧。
**4. 查看视频信息**
```bash
ffprobe -v quiet -print_format json -show_format -show_streams input.mp4
```
输出分辨率、码率、时长、编码格式等所有信息,JSON 格式方便脚本解析。
**5. 压缩视频**
```bash
ffmpeg -i input.mp4 -c:v libx264 -crf 28 -preset slow output.mp4
```
CRF 28 比默认的 23 压得更狠,`-preset slow` 用更多编码时间换取更小的文件。
## 安装
```bash
# macOS
brew install ffmpeg
# Ubuntu/Debian
sudo apt install ffmpeg
# Windows
# 从 ffmpeg.org 下载,解压后把 bin 目录加到 PATH
```
安装后跑 `ffmpeg -version` 确认可用。注意某些发行版的 FFmpeg 可能缺少某些编码器(如 libx265),需要从第三方源安装或自行编译。
## 命令语法模式
FFmpeg 命令的通用结构:
```bash
ffmpeg [全局选项] [输入选项] -i 输入文件 [输出选项] 输出文件
```
关键概念:选项的位置很重要。`-ss` 放在 `-i` 前面是快速跳转(不精确),放在后面是精确跳转(慢)。`-c:v` 和 `-c:a` 是输出选项,必须放在输出文件前面。服务端6月2日 00:00
FFmpeg 视频转码怎么做?常用格式转换命令和参数详解视频转码 = 解码 + 重新编码。最简单的形式是换个容器(MP4 转 MKV),最复杂的是同时改编码器、分辨率、码率、帧率。FFmpeg 一条命令搞定。
## 最基本的转码
```bash
# 让 FFmpeg 自动选择编码器(按输出文件后缀推断)
ffmpeg -i input.avi output.mp4
```
这样 FFmpeg 会自动把 AVI 里的 MPEG-4 视频转成 H.264,音频转成 AAC。简单但不可控——你不知道它选了什么参数。
明确指定编码器:
```bash
ffmpeg -i input.avi -c:v libx264 -c:a aac output.mp4
```
`-c:v` 指定视频编码器,`-c:a` 指定音频编码器。这是转码命令的基本骨架,所有高级参数都加在这个基础上。
## 常见转码场景
### AVI/MKV 转 MP4(最常见)
```bash
ffmpeg -i input.mkv -c:v libx264 -crf 23 -c:a aac -b:a 128k output.mp4
```
如果原视频已经是 H.264 编码,不需要重编码视频流,只换个容器:
```bash
ffmpeg -i input.mkv -c:v copy -c:a aac -b:a 128k output.mp4
```
`-c:v copy` 直接拷贝视频流,几秒完成。但 MKV 支持的编码器比 MP4 多——如果 MKV 里是 H.265 或 VP9,copy 到 MP4 后部分播放器可能不兼容。
### 转 WebM(网页视频)
```bash
ffmpeg -i input.mp4 -c:v libvpx-vp9 -crf 30 -b:v 0 -c:a libopus output.webm
```
VP9 编码必须用 `-b:v 0` 配合 CRF 模式,否则 CRF 不生效。Opus 是 WebM 的标准音频编码器,音质优于 AAC。
### 转 H.265(省空间)
```bash
ffmpeg -i input.mp4 -c:v libx265 -crf 28 -c:a aac -b:a 128k output.mp4
```
H.265 的 CRF 28 大约等于 H.264 的 CRF 23,文件小 30-50%。编码慢 3-5 倍,老设备可能播不了。
## 只转视频或只转音频
```bash
# 只转视频,音频原样拷贝
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -c:a copy output.mp4
# 只转音频,视频原样拷贝
ffmpeg -i input.mp4 -c:v copy -c:a aac -b:a 128k output.mp4
# 去掉视频只保留音频
ffmpeg -i input.mp4 -vn -c:a aac -b:a 128k output.aac
```
`-vn` 去掉视频,`-an` 去掉音频,`-sn` 去掉字幕。转码时去掉不需要的流可以节省时间。
## 硬件加速转码
CPU 编码太慢?用 GPU 加速:
```bash
# NVIDIA GPU(NVENC)
ffmpeg -hwaccel cuda -i input.mp4 -c:v h264_nvenc -preset p4 -cq 23 -c:a aac output.mp4
# Intel GPU(QSV)
ffmpeg -hwaccel qsv -i input.mp4 -c:v h264_qsv -preset medium -c:a aac output.mp4
# Apple Silicon(VideoToolbox)
ffmpeg -hwaccel videotoolbox -i input.mp4 -c:v h264_videotoolbox -b:v 5M -c:a aac output.mp4
```
硬件编码速度是 CPU 的 5-10 倍,但同码率下画质略差(约 5-10%)。如果追求极致画质用 CPU libx264,追求速度用 NVENC。`-preset p4` 是 NVENC 的质量档位,p1 最快 p7 最慢质量最好。
## 转码时调分辨率和帧率
```bash
# 1080p 转 720p
ffmpeg -i input.mp4 -vf "scale=1280:720" -c:v libx264 -crf 23 -c:a copy output.mp4
# 保持宽高比,只限宽度
ffmpeg -i input.mp4 -vf "scale=1280:-2" -c:v libx264 -crf 23 -c:a copy output.mp4
# 降帧率 60fps -> 30fps
ffmpeg -i input.mp4 -r 30 -c:v libx264 -crf 23 -c:a copy output.mp4
```
`-2` 让 FFmpeg 自动计算高度保持比例,必须用偶数(有些编码器要求宽高都是偶数)。
## 两遍编码:精确控制文件大小
CRF 模式无法精确控制输出文件大小。如果需要视频刚好 100MB:
```bash
# 第一遍:分析(不输出视频)
ffmpeg -i input.mp4 -c:v libx264 -b:v 1500k -pass 1 -an -f null /dev/null
# 第二遍:实际编码
ffmpeg -i input.mp4 -c:v libx264 -b:v 1500k -pass 2 -c:a aac -b:a 128k output.mp4
```
第一遍分析视频复杂度分布,第二遍据此分配码率。两遍编码比单遍 CRF 质量稍好,但编码时间翻倍。通常只在需要精确文件大小(如 DVD/蓝光)时才用。服务端6月2日 00:00
FFmpeg 视频剪辑、合并和截图怎么做?常用命令速查视频剪辑、合并、截图是 FFmpeg 最高频的日常操作。核心命令不多,但参数位置有讲究——放错位置效果完全不同。
## 视频剪辑:截取片段
### 最常用的剪辑命令
```bash
# 从第 10 秒开始,截取 30 秒
ffmpeg -ss 00:00:10 -i input.mp4 -t 30 -c copy output.mp4
# 从 1:30 截到 3:00
ffmpeg -ss 00:01:30 -to 00:03:00 -i input.mp4 -c copy output.mp4
```
`-c copy` 直接拷贝音视频流,不重新编码,速度极快(几秒搞定),但切割点可能不精确——因为视频是按关键帧压缩的,`-c copy` 只能在关键帧处切开。结果可能是你想从 10 秒切,实际从 8 秒的关键帧开始了。
### 精确剪辑
需要精确到帧的剪辑,必须重新编码:
```bash
ffmpeg -i input.mp4 -ss 00:00:10 -t 30 -c:v libx264 -crf 23 -c:a aac output.mp4
```
去掉 `-c copy`,让 FFmpeg 重新编码。速度慢很多但切割点精确。
### -ss 放在 -i 前面还是后面?
- `-ss` 在 `-i` 前面:FFmpeg 先跳到指定时间再开始解码(seek 模式),速度快但可能不精确
- `-ss` 在 `-i` 后面:FFmpeg 从头解码到指定时间,精确但慢
最佳实践:**先放前面快速定位,再精确微调**。对于长视频(>10 分钟),放前面能省很多时间。
## 视频合并:拼接多个片段
### concat 协议(最快,要求格式完全一致)
```bash
# 先创建文件列表
echo "file 'part1.mp4'" > list.txt
echo "file 'part2.mp4'" >> list.txt
echo "file 'part3.mp4'" >> list.txt
# 合并
ffmpeg -f concat -safe 0 -i list.txt -c copy output.mp4
```
`-c copy` 不重编码,秒级完成。但要求所有片段的编码参数(分辨率、帧率、编码器)完全一致,否则合并后可能花屏或播放异常。
### 格式不一致时:先统一再合并
```bash
# 先把所有片段转成统一格式
for f in part*.mp4; do
ffmpeg -i "$f" -c:v libx264 -crf 23 -c:a aac -b:a 128k -vf "scale=1920:1080" -r 30 "normalized_$f"
done
# 再用 concat 合并
echo "file 'normalized_part1.mp4'" > list.txt
echo "file 'normalized_part2.mp4'" >> list.txt
ffmpeg -f concat -safe 0 -i list.txt -c copy output.mp4
```
### filter 复杂合并(加转场等效果)
```bash
# 两个视频左右并排
ffmpeg -i left.mp4 -i right.mp4 -filter_complex "[0:v][1:v]hstack=inputs=2" output.mp4
# 上下并排
ffmpeg -i top.mp4 -i bottom.mp4 -filter_complex "[0:v][1:v]vstack=inputs=2" output.mp4
```
filter 合并必须重编码,速度慢但能做各种效果。
## 视频截图
```bash
# 截取第 5 秒的一帧
ffmpeg -i input.mp4 -ss 00:00:05 -vframes 1 screenshot.jpg
# 每隔 10 秒截一张
ffmpeg -i input.mp4 -vf "fps=1/10" screenshot_%04d.jpg
# 截取多张(前 60 秒,每秒 1 帧)
ffmpeg -i input.mp4 -t 60 -vf "fps=1" frame_%04d.jpg
```
`fps=1/10` 表示每 10 秒一帧。`%04d` 是序号占位符,生成 screenshot_0001.jpg、screenshot_0002.jpg...
截图的质量参数:`-q:v 2`(1 最好,31 最差,2 近乎无损)。
```bash
ffmpeg -i input.mp4 -ss 00:00:05 -vframes 1 -q:v 2 screenshot.jpg
```
## 实用技巧
**去除视频静音段**:用 silenceremove 滤镜自动剪掉没有声音的部分,适合处理会议录像。
**加速/减速视频**:`-vf "setpts=0.5*PTS"` 2 倍速,`-vf "setpts=2*PTS"` 半速。音频也要对应调整:`-af "atempo=2.0"`(atempo 范围 0.5-2.0,超出需要串联)。
**旋转视频**:`-vf "transpose=1"` 顺时针 90 度。手机竖拍视频在电脑上横着显示时用这个修正。服务端6月1日 23:59
FFmpeg 直播推流怎么配?RTMP 推流和 HLS/DASH 流媒体生成实战FFmpeg 是直播推流的核心工具——OBS 底层也是调用 FFmpeg 做编码和推流。直接用 FFmpeg 推流更轻量、更灵活,适合服务器端自动化场景。
## RTMP 推流:最常用的直播协议
RTMP 是直播平台(B站、斗鱼、YouTube)的标准推流协议。核心命令:
```bash
ffmpeg -re -i input.mp4 -c:v libx264 -preset veryfast -b:v 2500k -c:a aac -b:a 128k -f flv rtmp://server/live/stream_key
```
关键参数:
- `-re`:按原始帧率读取输入,不加这个 FFmpeg 会以最快速度把文件读完然后全推出去,直播就变成了快进
- `-preset veryfast`:实时编码必须用快 preset,slow 的话编码速度跟不上帧率,画面会卡顿
- `-b:v 2500k`:固定码率 2.5Mbps,直播平台通常有码率上限(B站 6000k,YouTube 5100k)
- `-f flv`:RTMP 推流必须用 FLV 容器格式
## 摄像头实时推流
```bash
# macOS 摄像头 + 麦克风推流
ffmpeg -f avfoundation -i "0:0" -c:v libx264 -preset veryfast -b:v 2500k -c:a aac -b:a 128k -f flv rtmp://server/live/stream_key
# Linux 摄像头推流
ffmpeg -f v4l2 -i /dev/video0 -f alsa -i hw:0 -c:v libx264 -preset veryfast -b:v 2500k -c:a aac -b:a 128k -f flv rtmp://server/live/stream_key
```
摄像头推流比推文件更需要注意编码速度——如果 CPU 不够快,需要降低分辨率或码率:`-vf "scale=1280:720"` 降到 720p。
## HLS:苹果系的流媒体方案
HLS 把视频切成小片段(.ts),通过 .m3u8 播放列表索引。延迟较高(通常 10-30 秒),但兼容性最好——所有浏览器和 iOS 设备都支持。
```bash
ffmpeg -re -i input.mp4 -c:v libx264 -preset veryfast -b:v 2500k -c:a aac -b:a 128k -f hls -hls_time 6 -hls_list_size 0 -hls_segment_filename "seg_%03d.ts" stream.m3u8
```
关键参数:
- `-hls_time 6`:每个切片 6 秒,越短延迟越低但碎片越多
- `-hls_list_size 0`:播放列表包含所有切片(0 = 不限制),直播场景可以设成 5-10 只保留最近几个
- `-hls_segment_filename`:切片文件命名模板
HLS 文件是纯静态文件,用 Nginx 或任何 HTTP 服务器托管就行,不需要专门的流媒体服务器。
## DASH:开源的流媒体方案
DASH 和 HLS 类似,但基于 MPEG 标准,没有苹果的专利限制。浏览器支持不如 HLS(Safari 不原生支持 DASH),但功能更灵活。
```bash
ffmpeg -re -i input.mp4 -c:v libx264 -preset veryfast -b:v 2500k -c:a aac -b:a 128k -f dash -seg_duration 6 -out output.mpd
```
实际上 DASH 在国内使用较少,大部分场景用 HLS 或 RTMP 就够了。
## 多码率自适应推流
给不同网速的用户提供不同清晰度——3G 用户看 480p,WiFi 用户看 1080p。用 `-var_stream_map` 生成多码率 HLS:
```bash
ffmpeg -re -i input.mp4 -c:v libx264 -preset veryfast -b:v:0 5000k -s:v:0 1920x1080 -b:v:1 2500k -s:v:1 1280x720 -b:v:2 1000k -s:v:2 854x480 -c:a aac -b:a 128k -var_stream_map "v:0,a:0 v:1,a:0 v:2,a:0" -f hls -hls_time 6 -master_pl_name master.m3u8 "stream_%v.m3u8"
```
播放器会根据带宽自动切换码率,用户看到的效果就是网速慢时自动降清晰度。
## 常见问题
**推流中断**:网络波动导致 RTMP 连接断开。加 `-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5` 让 FFmpeg 自动重连,但不是所有版本都支持。
**延迟太高**:HLS 延迟 10-30 秒是正常的。要低延迟用 RTMP 直推或 WebRTC(FFmpeg 不直接支持 WebRTC,需要 mediamtx 等中间件)。
**音视频不同步**:通常是编码速度跟不上导致帧被丢弃。降低分辨率/码率,或者用更快的 preset。服务端6月1日 23:57
FFmpeg 怎么录屏和录制摄像头?macOS/Linux/Windows 命令详解FFmpeg 可以直接从屏幕或摄像头捕获视频,不需要额外安装录屏软件。命令行录制的好处是可以精确控制编码参数、方便脚本化、资源占用低。坏处是参数复杂,初次配置容易踩坑。
## macOS 录屏
macOS 用 avfoundation 设备采集:
```bash
# 列出可用设备(先看看屏幕和摄像头编号)
ffmpeg -f avfoundation -list_devices true -i ""
# 录制整个屏幕(假设屏幕编号是 1)
ffmpeg -f avfoundation -i "1" -r 30 -c:v libx264 -preset ultrafast -crf 23 output.mp4
# 录制屏幕 + 系统音频
ffmpeg -f avfoundation -i "1:0" -r 30 -c:v libx264 -preset ultrafast -crf 23 -c:a aac output.mp4
```
`-preset ultrafast` 是录屏必备——实时编码用 slow 的话 CPU 扛不住,画面会卡。录完之后如果觉得文件太大,可以再用 slow preset 重新转码一遍。
## Linux 录屏
Linux 用 x11grab 采集 X11 桌面:
```bash
# 录制 1080p 屏幕
ffmpeg -f x11grab -video_size 1920x1080 -framerate 30 -i :0.0 -c:v libx264 -preset ultrafast -crf 23 output.mp4
# 录制指定区域(从坐标 100,100 开始,宽 800 高 600)
ffmpeg -f x11grab -video_size 800x600 -framerate 30 -i :0.0+100,100 -c:v libx264 -preset ultrafast -crf 23 output.mp4
# Wayland 用户用 wf-recorder 更方便,ffmpeg 对 Wayland 支持不完善
```
`:0.0` 是 DISPLAY 环境变量的值,多显示器时可能需要调整。Wayland 下 x11grab 可能不工作,需要装 xdotool 或者换用 wf-recorder。
## Windows 录屏
Windows 用 gdigrab 或 dshow:
```bash
# gdigrab 录制整个桌面
ffmpeg -f gdigrab -framerate 30 -i desktop -c:v libx264 -preset ultrafast -crf 23 output.mp4
# 录制指定窗口
ffmpeg -f gdigrab -framerate 30 -i title="Window Title" -c:v libx264 -preset ultrafast -crf 23 output.mp4
```
Windows 10+ 也可以用 `-f dshow` 配合摄像头设备名录制。
## 摄像头录制
```bash
# macOS:列出设备后用摄像头编号(通常是 0)
ffmpeg -f avfoundation -i "0" -r 30 -c:v libx264 -preset ultrafast -crf 23 output.mp4
# Linux:用 v4l2
ffmpeg -f v4l2 -video_size 640x480 -framerate 30 -i /dev/video0 -c:v libx264 -preset ultrafast -crf 23 output.mp4
# 摄像头 + 麦克风
ffmpeg -f avfoundation -i "0:0" -r 30 -c:v libx264 -preset ultrafast -crf 23 -c:a aac -b:a 128k output.mp4
```
摄像头编号需要先 `-list_devices true` 查一下,不同机器编号可能不同。
## 录屏实战技巧
**1. 录制时显示光标**:默认录屏可能看不到鼠标光标。macOS 下加 `-capture_cursor 1`,Linux 下 x11grab 默认会包含光标。
**2. 控制录制时长**:`-t 60` 只录 60 秒。方便做定时录制,不用手动按 Ctrl+C。
**3. 停止录制**:按 `Ctrl+C`。有时候 ffmpeg 不会正常写入 MP4 尾部,导致文件打不开。解决办法:改用 MKV 容器(`output.mkv`),MKV 即使没正常关闭也能播放;或者事后修复 MP4:
```bash
ffmpeg -i broken.mp4 -c copy fixed.mp4
```
**4. 同时录屏和摄像头画中画**:高级场景,用 overlay 滤镜把摄像头画面叠到屏幕录制上。这比纯录屏复杂很多,如果需要画中画效果,OBS 可能更合适。服务端6月1日 23:56
FFmpeg 怎么批量转码?Shell 脚本和 Python 并行处理实战单个文件用一条 ffmpeg 命令就行,但处理几十上百个文件就需要脚本了。批量处理的核心思路:用 Shell 循环遍历文件,用 GNU Parallel 做并行加速,用 Python 处理更复杂的逻辑。
## Shell 脚本:最简单的批量转码
```bash
#!/bin/bash
# 批量把 AVI 转成 MP4
for file in *.avi; do
ffmpeg -i "$file" -c:v libx264 -crf 23 -c:a aac "${file%.avi}.mp4"
done
```
`${file%.avi}` 是 Shell 的字符串截断——去掉 `.avi` 后缀,换成 `.mp4`。这个模式够用 80% 的批量场景。
递归处理子目录里的文件:
```bash
find /path/to/videos -name "*.mkv" | while read file; do
ffmpeg -i "$file" -c:v libx264 -crf 23 -c:a aac "${file%.mkv}.mp4"
done
```
`find` 递归搜索,`while read` 逐行读取文件路径。比 `-exec` 更灵活,可以在循环里加更多逻辑。
## 并行加速:别让 CPU 闲着
单线程循环太慢——4 核 CPU 只用了 1 核。用 GNU Parallel 让多个 ffmpeg 同时跑:
```bash
# 4 个 ffmpeg 并行转码
find . -name "*.avi" | parallel -j 4 ffmpeg -i {} -c:v libx264 -crf 23 -c:a aac {.}.mp4
```
`{}` 是输入文件名,`{.}` 是去掉后缀的文件名。`-j 4` 限制并发数——设成 CPU 核心数就行,太多会抢 IO 反而变慢。
没有 GNU Parallel?用 xargs 也能并行:
```bash
find . -name "*.avi" | xargs -P 4 -I {} sh -c 'ffmpeg -i "$1" -c:v libx264 -crf 23 -c:a aac "${1%.avi}.mp4"' _ {}
```
`-P 4` 控制并发数,`-I {}` 指定占位符。
## Python:更灵活的批量处理
Shell 脚本能做的事 Python 都能做,而且更容易处理错误和复杂逻辑:
```python
import subprocess, os, glob
input_dir = "raw"
output_dir = "compressed"
os.makedirs(output_dir, exist_ok=True)
for path in glob.glob(f"{input_dir}/*.mp4"):
name = os.path.basename(path)
output = os.path.join(output_dir, name)
cmd = ["ffmpeg", "-i", path, "-c:v", "libx264",
"-crf", "23", "-c:a", "aac", "-b:a", "128k",
"-movflags", "+faststart", output]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
print(f"OK {name}")
else:
print(f"FAIL {name}: {result.stderr[:200]}")
```
Python 的优势:可以记录成功/失败、跳过已处理文件、按条件选不同参数。Shell 脚本做这些需要大量 if-else,可读性差。
## 进阶:按条件选择不同参数
不同类型的视频用不同的压缩策略——教学录屏用高 CRF + stillimage,电影用低 CRF + film:
```bash
#!/bin/bash
for file in *.mp4; do
duration=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$file" | cut -d. -f1)
if [ "$duration" -lt 300 ]; then
ffmpeg -i "$file" -c:v libx264 -crf 28 -tune stillimage -c:a aac "compressed_$file"
else
ffmpeg -i "$file" -c:v libx264 -crf 23 -tune film -c:a aac "compressed_$file"
fi
done
```
## 跳过已处理文件
批量中断后重跑,不想重复处理已经转好的文件:
```bash
for file in *.mp4; do
output="output/$file"
[ -f "$output" ] && continue
ffmpeg -i "$file" -c:v libx264 -crf 23 -c:a aac "$output"
done
```
一行判断搞定断点续传。Python 版可以用 `os.path.exists()` 做同样的事。服务端6月1日 23:26
FFmpeg 视频格式和编解码器怎么选?MP4、MKV、WebM 各适合什么场景?选视频格式就是选容器(封装格式),选编解码器就是选压缩算法。容器决定兼容性和功能(能不能塞字幕、多音轨),编解码器决定画质和文件大小。两者要分开想。
## 容器格式:MP4、MKV、WebM 怎么选
**MP4**:万能格式,所有设备都能播。缺点是只支持有限的编解码器,多音轨多字幕支持弱。**网络视频、手机分享、社交媒体一律选 MP4**,不会有兼容性问题。
**MKV**:万能容器,什么编解码器都能塞,支持无限音轨和字幕轨。缺点是某些老旧设备和播放器不支持(特别是智能电视和游戏机)。**本地收藏、多语言视频、高清片源选 MKV**。
**WebM**:Google 推的 Web 专用格式,基于 VP8/VP9/AV1 编码。所有主流浏览器支持,不需要插件。**网页嵌入视频选 WebM**,或者直接用 MP4(浏览器也支持)。
**MOV**:Apple 的格式,QuickTime 原生支持。在 Mac 生态里很方便,但 Windows 上不如 MP4 通用。**视频剪辑工作流里常见**(Final Cut Pro 默认输出 MOV)。
## 编解码器:H.264、H.265、AV1 怎么选
**H.264(AVC)**:兼容性之王,所有设备都支持。压缩效率中等,是目前的默认选择。如果你不确定该用什么,用 H.264 不会错。
```bash
ffmpeg -i input.avi -c:v libx264 -crf 23 -c:a aac output.mp4
```
**H.265(HEVC)**:比 H.264 同画质小 30-50%,但编码慢 3-5 倍。兼容性不如 H.264——老手机、老浏览器可能播不了。**4K 视频和存档用 H.265**,因为省的空间很可观。Windows 10+ 和近三年的手机基本都支持了。
```bash
ffmpeg -i input.mp4 -c:v libx265 -crf 28 -c:a aac output.mp4
```
**AV1**:新一代编码,比 H.265 还能再省 20-30%。开源免专利费,YouTube 和 Netflix 都在用。但编码极慢(libaom-av1 比 libx265 慢 10 倍以上),播放需要较新的硬件/软件。**2025 年的 AV1 已经可以在最新设备上流畅播放**,但离全面普及还有距离。如果追求极致压缩且目标平台新,可以试试。
```bash
ffmpeg -i input.mp4 -c:v libaom-av1 -crf 30 -c:a libopus output.webm
```
## 音频编解码器
视频里的音频也别忽略:
- **AAC**:通用选择,所有设备支持,128kbps 够听
- **Opus**:开源编码器,同码率音质优于 AAC,WebM 默认音频编码
- **MP3**:老旧但兼容性极好,新项目不需要用了
```bash
# 视频 H.264 + 音频 AAC(最通用组合)
ffmpeg -i input.mkv -c:v libx264 -crf 23 -c:a aac -b:a 128k output.mp4
# 只转音频,视频直接拷贝
ffmpeg -i input.mp4 -c:v copy -c:a aac -b:a 128k output.mp4
```
## 选择决策流程
1. 目标平台是什么?网页 → MP4/WebM,手机分享 → MP4,本地存档 → MKV
2. 需要兼容老设备吗?是 → H.264,否 → H.265/AV1
3. 对文件大小敏感吗?是 → H.265 CRF 28,否 → H.264 CRF 23
4. 需要多音轨/多字幕吗?是 → MKV,否 → MP4
最常见组合:**H.264 + AAC 封装成 MP4**,覆盖 99% 的播放场景。服务端6月1日 23:25
FFmpeg 怎么查看视频信息?ffprobe 命令和视频质量分析实战获取视频信息用 `ffprobe`(FFmpeg 自带的探测工具),不需要转码,秒出结果。分析视频质量用 PSNR/SSIM 等指标,对比编码前后的画质差异。
## ffprobe:一行命令看透视频
最常用的命令:
```bash
# 查看所有信息(人类可读)
ffprobe input.mp4
# JSON 格式输出(方便脚本解析)
ffprobe -v quiet -print_format json -show_format -show_streams input.mp4
```
日常只需要几个关键字段:
| 字段 | 含义 | 查看命令 |
|------|------|----------|
| 分辨率 | 视频宽高 | `ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 input.mp4` |
| 时长 | 视频长度(秒) | `ffprobe -v error -show_entries format=duration -of csv=p=0 input.mp4` |
| 码率 | 比特率 | `ffprobe -v error -show_entries format=bit_rate -of csv=p=0 input.mp4` |
| 帧率 | 每秒帧数 | `ffprobe -v error -select_streams v:0 -show_entries stream=r_frame_rate -of csv=p=0 input.mp4` |
| 编码器 | 编解码格式 | `ffprobe -v error -select_streams v:0 -show_entries stream=codec_name -of csv=p=0 input.mp4` |
这些命令看着长,但本质都是 `-show_entries` 指定要哪个字段 + `-of` 指定输出格式。记住模式就行,不用背命令。
## 提取关键信息的快捷脚本
```bash
# 一行获取:分辨率 帧率 编码 时长 码率
ffprobe -v error -select_streams v:0 \
-show_entries stream=width,height,codec_name,r_frame_rate \
-show_entries format=duration,bit_rate \
-of default=noprint_wrappers=1 input.mp4
```
在 Python 里调用 ffprobe:
```python
import subprocess, json
def get_video_info(path):
cmd = ["ffprobe", "-v", "quiet", "-print_format", "json",
"-show_format", "-show_streams", path]
result = subprocess.run(cmd, capture_output=True, text=True)
return json.loads(result.stdout)
info = get_video_info("input.mp4")
video = info["streams"][0] # 第一个视频流
print(f"分辨率: {video["width"]}x{video["height"]}")
print(f"编码: {video["codec_name"]}")
print(f"时长: {float(info["format"]["duration"]):.1f}秒")
```
## 视频质量分析:量化编码损失
压缩视频后想知道画质损失了多少,用 PSNR 和 SSIM 两个指标。
**PSNR**(峰值信噪比):数值越高画质越好,30dB 以下明显有损,40dB 以上视觉无损。
```bash
ffmpeg -i original.mp4 -i compressed.mp4 -lavfi psnr -f null -
```
**SSIM**(结构相似度):0-1 之间,越接近 1 越相似。SSIM 比 PSNR 更接近人眼感知——PSNR 只看像素差异,SSIM 看结构变化。实际评估画质优先看 SSIM。
```bash
ffmpeg -i original.mp4 -i compressed.mp4 -lavfi ssim -f null -
```
两个命令都会逐帧计算并输出平均值。VMAF 是 Netflix 提出的更先进的质量指标,更接近人类主观评分,但 FFmpeg 原生不支持(需要额外编译 libvmaf)。
## 常见问题排查
**视频打不开**:先跑 `ffprobe input.mp4`,看报错信息。常见原因:文件损坏、编码不支持、容器格式错误。`ffprobe` 比任何播放器都能更快定位问题。
**码率异常**:如果视频文件很大但画质一般,用 ffprobe 看码率——可能是编码效率低(用了老编码器)或码率设置过高。转成 H.264/265 + 合理 CRF 通常能大幅缩小。
**音视频不同步**:ffprobe 看 `start_time` 差异——音频和视频的起始时间不一致就会导致不同步。服务端6月1日 23:24
FFmpeg 视频压缩怎么选参数?CRF、preset 和 tune 实战指南FFmpeg 压缩视频的核心就三个参数:`-crf` 控制质量,`-preset` 控制编码速度和压缩率的平衡,`-tune` 针对特定内容类型优化。理解这三个参数,就能应对 90% 的压缩场景。
## CRF:画质旋钮
CRF(Constant Rate Factor)是 H.264/H.265 的质量控制参数,范围 0-51。数字越小质量越高文件越大,数字越大质量越低文件越小。
关键参考值:
- **18**:视觉无损,和原片几乎看不出差别,文件很大
- **23**:默认值,质量和体积的平衡点,大多数场景够用
- **28**:明显有损但能看,适合手机端播放
- **30+**:质量较差,只在极端节省空间时使用
```bash
ffmpeg -i input.mp4 -c:v libx264 -crf 23 output.mp4
```
CRF 是感知质量恒定——运动多的场景自动多分配码率,静态场景少分配。所以同一个 CRF 值,不同视频的文件大小可能差很多。如果需要精确控制文件大小,得用两遍编码(two-pass)或指定码率。
## Preset:时间换空间
Preset 控制编码器用多少计算时间来寻找更优的压缩方案。越慢的 preset 压缩率越高(同画质文件更小),但编码时间越长。
| Preset | 编码速度 | 同 CRF 文件大小 | 适用场景 |
|--------|----------|-----------------|----------|
| ultrafast | 最快 | 最大(约 2x) | 实时流媒体 |
| veryfast | 很快 | 较大 | 直播推流 |
| fast | 快 | 中等 | 日常快转 |
| medium | 中等(默认) | 基准 | 一般用途 |
| slow | 慢(约 3x 时间) | 较小(约 -15%) | 离线转码 |
| veryslow | 很慢(约 6x) | 更小(约 -25%) | 归档存储 |
实际建议:**离线处理用 slow,实时场景用 veryfast,不确定就用 medium**。ultraslow 不存在,veryslow 已经是极限了。从 medium 切到 slow,编码时间翻 3 倍但文件只小 15%——值不值看你的需求。
```bash
# 离线高质量压缩
ffmpeg -i input.mp4 -c:v libx264 -crf 20 -preset slow output.mp4
# 快速压缩(直播/即时预览)
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset veryfast output.mp4
```
## Tune:针对性优化
Tune 让编码器针对特定内容类型调整策略:
- **film**:电影/真人视频,去胶片颗粒感,优化暗部细节
- **animation**:动画片,优化大面积色块和平滑区域
- **stillimage**:PPT 录屏/幻灯片,优化静态画面
- **fastdecode**:牺牲一点压缩率换取更快解码速度(低端设备播放)
- **zerolatency**:零延迟,实时流媒体和直播专用
大部分情况不加 tune 就行。加错了反而比不加差——对真人视频用 animation,细节会糊掉。
```bash
# 压缩电影
ffmpeg -i movie.mkv -c:v libx264 -crf 20 -preset slow -tune film output.mp4
# 压缩动画
ffmpeg -i anime.mkv -c:v libx264 -crf 20 -preset slow -tune animation output.mp4
# 压缩录屏教程
ffmpeg -i screen.mp4 -c:v libx264 -crf 23 -preset medium -tune stillimage output.mp4
```
## H.265 压缩:省空间但要考虑兼容性
H.265(HEVC)比 H.264 同画质文件小 30-50%,但编码慢 3-5 倍,且老设备可能不支持播放。如果目标平台是手机/智能电视/浏览器,H.264 兼容性最好。如果目标是存档或只在 PC 上播放,H.265 更划算。
```bash
ffmpeg -i input.mp4 -c:v libx265 -crf 28 -preset slow output.mp4
```
注意 H.265 的 CRF 值不能和 H.264 直接对比——H.265 的 CRF 28 大致对应 H.264 的 CRF 23,因为 H.265 的范围和感知曲线不同。
## 两个常见坑
**1. 忘了 -movflags +faststart**:MP4 文件的元数据(moov atom)默认放在文件末尾,浏览器必须下载完整个文件才能开始播放。加 `-movflags +faststart` 把元数据移到开头,允许边下载边播放。网络视频必加。
**2. 音频不要忘**:压缩视频时音频默认也会被重编码。如果只是压缩视频不想动音频,加 `-c:a copy` 直接拷贝音频流。要压缩音频就用 `-c:a aac -b:a 128k`,128kbps 对大多数内容够用。前端5月29日 00:25
FFmpeg支持哪些常见的音视频格式?FFmpeg支持100+容器格式和200+编解码器,需区分**容器**和**编码**两层。容器层主流格式:MP4(兼容性最广)、MKV(多音轨/字幕)、WebM(Web优化)、MOV(Apple生态)、FLV(直播推流)、TS(HLS切片)。视频编码:H.264/AVC(最通用)、H.265/HEVC(同质量体积减半)、VP9(WebM默认)、AV1(开源下一代,FFmpeg 5.0+支持)。音频编码:AAC(流媒体标配)、Opus(低延迟实时通信最优)、MP3(兼容旧设备)、FLAC(无损)。关键认知:容器决定封装结构,编码决定压缩算法,同一容器可装不同编码(如MP4可装H.264或H.265)。用`ffmpeg -encoders`查看本地支持的编码器列表。
## 追问
- H.264和H.265在FFmpeg中用什么编码器?libx264和h264_nvenc性能差多少?
- MP4容器能装VP9视频吗?为什么WebM比MP4更适合Web场景?
- Opus相比AAC在延迟和码率上有什么优势?为什么WebRTC选Opus?
- FLV容器为什么逐步被淘汰?HLS的TS切片方案解决了FLV的什么问题?
- `ffmpeg -codecs`和`ffmpeg -encoders`输出有什么区别?
## 写段代码
```bash
# 查看本地支持的所有H.265编码器
ffmpeg -encoders 2>/dev/null | grep 265
# MP4转WebM(VP9+Opus)
ffmpeg -i in.mp4 -c:v libvpx-vp9 -crf 30 -b:v 0 -c:a libopus out.webm
```前端5月29日 00:24
FFmpeg的核心组件包括哪些?分别有什么作用?FFmpeg的核心组件分为库和命令行工具两大类。库层面:**libavcodec**负责音视频编解码(H.264/H.265/AAC等),**libavformat**处理容器封装与解封装(MP4/MKV/FLV),**libavfilter**实现滤镜链(缩放/旋转/叠加),**libswscale**做像素格式转换(YUV↔RGB),**libswresample**处理音频重采样与通道转换,**libavutil**提供通用数据结构(AVPacket/AVFrame)和工具函数,**libavdevice**抽象硬件设备交互。工具层面:**ffmpeg**是转码命令行入口,**ffprobe**探测媒体流信息,**ffplay**轻量播放器。转码流水线为:demux(libavformat)→ decode(libavcodec)→ filter(libavfilter)→ encode(libavcodec)→ mux(libavformat)。
## 追问
- libavcodec和libavformat的职责边界在哪?为什么要把编解码和容器处理拆成两个库?
- 转码时用`-c copy`跳过了流水线中哪些环节?为什么能实现无损且秒级完成?
- libswscale和libavfilter的scale滤镜功能重叠,实际项目中该用哪个?
- ffprobe如何快速获取视频时长和码率?底层调用了libavformat的哪个接口?
- 硬件加速编解码(NVENC/QSV)在组件架构中如何接入?是否绕过了libavcodec?
## 写段代码
```bash
# 查看视频流信息
ffprobe -v quiet -print_format json -show_streams input.mp4
# H.264转H.265,音频直接拷贝
ffmpeg -i input.mp4 -c:v libx265 -crf 28 -c:a copy output.mp4
```前端5月28日 02:01
FFmpeg是否提供API?如何在C/C++项目中集成FFmpeg?FFmpeg 提供了完整的 C 语言 API,这是面试中经常被问到的基础知识。核心 API 分布在 libavformat、libavcodec、libavutil、libswscale、libswresample 五个库中,C/C++ 项目可以直接链接这些库来调用编解码、封装解封装、格式转换等全部功能,无需通过命令行进程通信。
## FFmpeg 有哪些核心库?各自的职责是什么?
这是理解 FFmpeg API 的起点。FFmpeg 的模块化设计体现在每个库各司其职:
- **libavformat**:处理容器格式的读取与写入。MP4、MKV、FLV 等文件的打开、流信息解析、数据包读写都由它负责。核心函数包括 `avformat_open_input()`、`av_read_frame()`、`avformat_write_header()`。
- **libavcodec**:编解码器的核心。H.264、H.265、AAC、OPUS 等编解码器都封装在这里。`avcodec_find_decoder()`、`avcodec_send_packet()`、`avcodec_receive_frame()` 是解码的关键调用链。
- **libavutil**:公共工具库,提供内存管理(`av_malloc`/`av_free`)、数学运算(`av_clip`)、日志(`av_log`)、字典(`AVDictionary`)等基础设施。其他库都依赖它。
- **libswscale**:图像缩放和像素格式转换。将 YUV 数据转为 RGB、调整分辨率等场景必须用它,核心函数是 `sws_scale()`。
- **libswresample**:音频重采样、声道布局转换、采样格式转换。处理音频数据时不可或缺,核心函数是 `swr_convert()`。
面试追问:为什么 FFmpeg 要拆成这么多库而不是一个整体?答案是模块化链接——如果你的项目只需要解码不需要缩放,可以只链接 libavcodec 和 libavformat,不链接 libswscale,减小二进制体积。这在嵌入式和移动端尤其重要。
## 如何在 C/C++ 项目中集成 FFmpeg?
集成分为三步:安装开发包、配置构建系统、链接库文件。
### 安装开发包
不同平台的安装方式:
```bash
# Ubuntu/Debian
sudo apt install libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev
# macOS
brew install ffmpeg
# Windows(推荐 vcpkg)
vcpkg install ffmpeg
```
安装后确认头文件存在:`/usr/include/libavcodec/avcodec.h`(Linux)或 `$(brew --prefix ffmpeg)/include/libavcodec/avcodec.h`(macOS)。如果头文件找不到,说明装的是运行时包而非开发包。
### 配置 CMake 构建
CMake 是最常用的构建方式。关键在于 `find_package` 和正确的链接顺序:
```cmake
cmake_minimum_required(VERSION 3.10)
project(FFmpegApp)
# 方式一:使用 CMake 内置的 FindFFmpeg 模块
find_package(FFmpeg REQUIRED COMPONENTS avformat avcodec avutil swscale swresample)
add_executable(main main.cpp)
target_include_directories(main PRIVATE ${FFMPEG_INCLUDE_DIRS})
target_link_libraries(main PRIVATE
${FFMPEG_LIBAVFORMAT_LIBRARIES}
${FFMPEG_LIBAVCODEC_LIBRARIES}
${FFMPEG_LIBAVUTIL_LIBRARIES}
${FFMPEG_LIBSWSCALE_LIBRARIES}
${FFMPEG_LIBSWRESAMPLE_LIBRARIES}
)
```
如果你的 CMake 版本不支持 `FindFFmpeg`,可以用 `pkg-config`:
```cmake
find_package(PkgConfig REQUIRED)
pkg_check_modules(AVFORMAT REQUIRED libavformat)
pkg_check_modules(AVCODEC REQUIRED libavcodec)
pkg_check_modules(AVUTIL REQUIRED libavutil)
add_executable(main main.cpp)
target_compile_options(main PRIVATE ${AVFORMAT_CFLAGS} ${AVCODEC_CFLAGS})
target_link_libraries(main PRIVATE ${AVFORMAT_LIBRARIES} ${AVCODEC_LIBRARIES} ${AVUTIL_LIBRARIES})
```
### 链接顺序与常见错误
链接顺序是集成的最大坑。FFmpeg 库之间存在依赖关系,必须按依赖顺序从左到右排列:
```
-lavformat -lavcodec -lswscale -lswresample -lavutil -lm -lz -lpthread
```
`libavformat` 依赖 `libavcodec`,`libavcodec` 依赖 `libavutil`,所以 `libavformat` 必须在前面。如果顺序反了,会报 `undefined reference to avformat_open_input` 之类的错误。
Windows 上额外注意:需要把 FFmpeg 的 `bin` 目录加到 `PATH`,或在项目属性中设置 `LIBRARY_PATH` 和 `INCLUDE` 环境变量。
## 如何用 FFmpeg API 解码视频帧?
这是最常考的代码题。解码流程分五步:打开文件 → 查找流 → 打开解码器 → 读包解码 → 释放资源。
```c
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <input>\n", argv[0]);
return 1;
}
// 1. 打开输入文件
AVFormatContext *fmt_ctx = NULL;
if (avformat_open_input(&fmt_ctx, argv[1], NULL, NULL) < 0) {
fprintf(stderr, "Cannot open input\n");
return 1;
}
avformat_find_stream_info(fmt_ctx, NULL);
// 2. 查找视频流
int video_idx = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if (video_idx < 0) {
fprintf(stderr, "No video stream\n");
avformat_close_input(&fmt_ctx);
return 1;
}
// 3. 打开解码器
const AVCodec *codec = avcodec_find_decoder(fmt_ctx->streams[video_idx]->codecpar->codec_id);
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
avcodec_parameters_to_context(codec_ctx, fmt_ctx->streams[video_idx]->codecpar);
avcodec_open2(codec_ctx, codec, NULL);
// 4. 读包解码
AVPacket *pkt = av_packet_alloc();
AVFrame *frame = av_frame_alloc();
while (av_read_frame(fmt_ctx, pkt) >= 0) {
if (pkt->stream_index != video_idx) {
av_packet_unref(pkt);
continue;
}
if (avcodec_send_packet(codec_ctx, pkt) < 0) {
av_packet_unref(pkt);
continue;
}
while (avcodec_receive_frame(codec_ctx, frame) == 0) {
printf("Frame %d: %dx%d fmt=%d\n",
codec_ctx->frame_number, frame->width, frame->height, frame->format);
}
av_packet_unref(pkt);
}
// 5. 刷新解码器(处理缓存的帧)
avcodec_send_packet(codec_ctx, NULL);
while (avcodec_receive_frame(codec_ctx, frame) == 0) {
printf("Flushed frame %d\n", codec_ctx->frame_number);
}
// 6. 释放资源
av_frame_free(&frame);
av_packet_free(&pkt);
avcodec_free_context(&codec_ctx);
avformat_close_input(&fmt_ctx);
return 0;
}
```
几个关键细节:
- `avformat_find_stream_info()` 不能省略,它填充流信息,否则 `codecpar` 中的字段可能不完整。
- `av_find_best_stream()` 比手动遍历流更可靠,它能处理多视频流的情况。
- 解码结束后必须发送 NULL 包来刷新解码器,否则最后几帧会丢失。
- 使用 `av_packet_alloc()` 而不是栈上的 `AVPacket`,这是 FFmpeg 新版推荐的写法。
## 如何用 FFmpeg API 编码视频?
解码的反向过程——编码同样高频出现。核心差异在于需要手动设置编码参数、管理时间戳。
```c
// 编码器初始化
const AVCodec *encoder = avcodec_find_encoder(AV_CODEC_ID_H264);
AVCodecContext *enc_ctx = avcodec_alloc_context3(encoder);
enc_ctx->bit_rate = 400000;
enc_ctx->width = 1920;
enc_ctx->height = 1080;
enc_ctx->time_base = (AVRational){1, 25};
enc_ctx->framerate = (AVRational){25, 1};
enc_ctx->gop_size = 10;
enc_ctx->max_b_frames = 1;
enc_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
// H.264 特定选项
AVDictionary *opts = NULL;
av_dict_set(&opts, "preset", "medium", 0);
avcodec_open2(enc_ctx, encoder, &opts);
av_dict_free(&opts);
// 编码循环
AVPacket *pkt = av_packet_alloc();
for (int i = 0; i < frame_count; i++) {
// ... 准备 frame 数据 ...
frame->pts = i;
avcodec_send_frame(enc_ctx, frame);
while (avcodec_receive_packet(enc_ctx, pkt) == 0) {
// 将 pkt 写入输出文件
av_packet_unref(pkt);
}
}
// 刷新编码器
avcodec_send_frame(enc_ctx, NULL);
while (avcodec_receive_packet(enc_ctx, pkt) == 0) {
av_packet_unref(pkt);
}
```
编码时最容易踩的坑是 PTS(显示时间戳)。每一帧必须设置递增的 PTS,否则输出文件的时间轴会混乱。时间基 `time_base` 决定了 PTS 的单位,`{1, 25}` 表示每秒 25 个单位。
## 内存管理有哪些容易忽略的要点?
FFmpeg 的 API 是纯 C 设计,没有自动内存管理。每个分配操作都有对应的释放操作,遗漏任何一步都会导致内存泄漏。
| 分配函数 | 释放函数 | 说明 |
|---------|---------|------|
| `avformat_open_input()` | `avformat_close_input()` | 关闭输入并释放上下文 |
| `avcodec_alloc_context3()` | `avcodec_free_context()` | 释放解码器上下文 |
| `av_frame_alloc()` | `av_frame_free()` | 释放帧 |
| `av_packet_alloc()` | `av_packet_free()` | 释放包 |
| `av_malloc()` | `av_free()` | 通用内存分配 |
| `sws_getContext()` | `sws_freeContext()` | 释放缩放上下文 |
| `swr_alloc()` | `swr_free()` | 释放重采样上下文 |
特别容易忽略的是 `av_packet_unref()`。每次 `av_read_frame()` 后,包内部的数据缓冲区被引用计数加一,必须调用 `av_packet_unref()` 减引用,否则数据缓冲区永远不会被释放。这不是 C++ 的 RAII,必须手动管理。
另一个常见问题是 `avformat_close_input()` 会释放 `AVFormatContext`,之后不要再对它调用 `av_free()`,否则 double free。
## 多线程解码需要注意什么?
FFmpeg 支持线程级并行解码,但默认不开启。设置方式:
```c
enc_ctx->thread_count = 4; // 使用 4 个线程
enc_ctx->thread_type = FF_THREAD_FRAME; // 帧级并行
```
`thread_type` 有两个选项:
- `FF_THREAD_SLICE`:片级并行,一帧内多个 slice 并行解码。兼容性好但加速有限。
- `FF_THREAD_FRAME`:帧级并行,多帧同时解码。加速明显但延迟更高,需要更多内存缓存帧。
实际使用中,`FF_THREAD_FRAME` 加速效果更好,但实时场景(如视频会议)应选 `FF_THREAD_SLICE` 降低延迟。
注意:`thread_count` 的值不要超过 CPU 核心数,设置为 0 表示 FFmpeg 自动选择。
## 常见集成问题排查
**Q: 编译报 `undefined reference to av_xxx`**
A: 99% 是链接顺序问题。确保 `-lavformat` 在 `-lavcodec` 前面,`-lavcodec` 在 `-lavutil` 前面。用 `pkg-config --libs libavformat` 查看正确的链接顺序。
**Q: 运行时报 `Cannot open input file`**
A: 检查文件路径是否正确。Windows 上注意反斜杠问题,建议统一用正斜杠。另外确认文件格式是否被 FFmpeg 支持:`ffmpeg -formats | grep mp4`。
**Q: 解码出的帧颜色不对**
A: 缺少像素格式转换。解码输出通常是 YUV 格式,显示需要 RGB。用 `libswscale` 的 `sws_scale()` 转换。
**Q: 音视频不同步**
A: 时间戳管理问题。必须用 `av_packet_rescale_ts()` 在编码/复用时重新计算时间基,不能直接用解码帧的 PTS。
## 实际项目中的最佳实践
1. **错误处理不能偷懒**:每个 FFmpeg API 调用的返回值都要检查。生产环境建议封装统一的错误处理宏:
```c
#define CHECK_ERR(ret, msg) do { \
if (ret < 0) { \
char errbuf[128]; \
av_strerror(ret, errbuf, sizeof(errbuf)); \
fprintf(stderr, "%s: %s\n", msg, errbuf); \
goto cleanup; \
} \
} while(0)
```
2. **日志分级**:开发阶段设 `av_log_set_level(AV_LOG_DEBUG)`,生产环境设 `AV_LOG_WARNING` 或 `AV_LOG_ERROR`。FFmpeg 默认日志级别太低,会输出大量信息。
3. **资源释放用 goto 模式**:C 语言没有 defer,用 `goto cleanup` 是 FFmpeg 社区推荐的方式,确保任何错误路径都能正确释放已分配的资源。
4. **API 版本兼容**:FFmpeg 不同版本之间 API 有变化。用 `LIBAVCODEC_VERSION_MAJOR` 等宏做版本判断,或在 CMake 中检测。FFmpeg 6.0 之后 `avcodec_find_decoder()` 返回 `const AVCodec*`,之前是非 const。
5. **避免在热路径中分配内存**:`av_frame_alloc()` 和 `av_packet_alloc()` 应在循环外调用,循环内用 `av_frame_unref()` 和 `av_packet_unref()` 重置后复用。前端5月28日 01:59
如何用FFmpeg调整视频的码率、分辨率和帧率?在视频处理中,调整码率、分辨率和帧率是最常见的需求。无论是压缩视频体积、适配不同设备,还是优化流媒体传输,FFmpeg 都能通过命令行参数精确控制这三个核心参数。但参数设置不当容易导致画质劣化、播放卡顿甚至编码失败,所以需要理解每个参数的含义和适用场景。
## 码率调整:控制视频体积与画质的平衡
码率(bitrate)决定视频每秒的数据量,单位为 kbit/s 或 Mbit/s。码率越高画质越好,但文件体积也越大。FFmpeg 提供三种码率控制模式,适用场景各不相同。
### CBR:恒定码率
CBR 保持码率不变,适合直播等对带宽要求稳定的场景:
```bash
ffmpeg -i input.mp4 -c:v libx264 -b:v 5000k -minrate 5000k -maxrate 5000k -bufsize 10000k output.mp4
```
将 `-minrate`、`-maxrate` 设为相同值即为 CBR 模式。`-bufsize` 设为码率的 2 倍可以保证码率稳定。
### VBR:可变码率
VBR 根据画面复杂度动态调整码率,简单场景省码率、复杂场景多分配,适合点播和本地存储:
```bash
ffmpeg -i input.mp4 -c:v libx264 -b:v 5000k -maxrate 8000k -bufsize 10000k output.mp4
```
`-b:v` 设定平均码率,`-maxrate` 限制峰值码率防止突发流量。
### CRF:恒定质量(推荐)
CRF(Constant Rate Factor)是 libx264/libx265 最推荐的码率控制方式,它按目标画质自动分配码率,无需手动指定码率值:
```bash
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset medium output.mp4
```
CRF 取值范围 0-51,默认 23。常用范围:
- 18-22:高质量,接近视觉无损
- 23-28:质量与体积的平衡点,日常使用推荐
- 28-32:明显压缩,适合对体积敏感的场景
`-preset` 控制编码速度与压缩效率的平衡,从快到慢:`ultrafast` < `superfast` < `veryfast` < `faster` < `fast` < `medium` < `slow` < `slower` < `veryslow`。`slow` 压缩率更高但编码更慢,`fast` 编码快但文件更大。
### 两遍编码(Two-Pass)
对体积有严格要求的场景(如视频网站),使用两遍编码可以在精确控制文件大小的同时获得最佳画质:
```bash
# 第一遍:分析视频内容
ffmpeg -i input.mp4 -c:v libx264 -b:v 5000k -pass 1 -an -f null /dev/null
# 第二遍:基于分析结果编码
ffmpeg -i input.mp4 -c:v libx264 -b:v 5000k -pass 2 -c:a aac -b:a 128k output.mp4
```
### 码率参考值
不同分辨率下的推荐码率(H.264 编码):
| 分辨率 | 建议码率范围 | 适用场景 |
|--------|-------------|---------|
| 480p (854x480) | 1000-2000 kbit/s | 移动端低清 |
| 720p (1280x720) | 2500-5000 kbit/s | 移动端高清 |
| 1080p (1920x1080) | 5000-8000 kbit/s | PC 端高清 |
| 4K (3840x2160) | 15000-30000 kbit/s | 大屏/专业用途 |
## 分辨率调整:适配不同设备与带宽
分辨率即画面的宽高像素数,直接影响清晰度。调整分辨率有两种方式,推荐使用滤镜方式。
### 使用 scale 滤镜(推荐)
```bash
# 固定分辨率
ffmpeg -i input.mp4 -vf "scale=1280:720" -c:v libx264 -crf 23 output.mp4
# 指定高度,宽度自动计算(保持宽高比)
ffmpeg -i input.mp4 -vf "scale=-2:720" -c:v libx264 -crf 23 output.mp4
```
`-2` 表示自动计算,保证宽度为偶数(编码器要求)。推荐这种写法,避免画面变形。
### 使用 lanczos 算法提升缩放质量
```bash
ffmpeg -i input.mp4 -vf "scale=1280:720:flags=lanczos" -c:v libx264 -crf 23 output.mp4
```
`lanczos` 是高质量的缩放算法,下采样时比默认的 bicubic 更清晰,适合降低分辨率的场景。
### 保持宽高比并补黑边
目标容器有固定尺寸但不想裁剪画面时:
```bash
ffmpeg -i input.mp4 -vf "scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2" -c:v libx264 -crf 23 output.mp4
```
### 使用 -s 参数(不推荐)
```bash
ffmpeg -i input.mp4 -s 1280x720 -c:v libx264 -crf 23 output.mp4
```
`-s` 直接指定分辨率,但无法保持宽高比,容易导致画面拉伸变形。仅在源视频比例已知时使用。
## 帧率调整:匹配播放场景的需求
帧率(fps)影响画面流畅度。常见帧率:24fps(电影)、25fps(PAL 制式)、30fps(网络视频)、60fps(游戏/运动画面)。
### 直接设置帧率
```bash
ffmpeg -i input.mp4 -r 24 -c:v libx264 -crf 23 output.mp4
```
`-r` 直接指定输出帧率,FFmpeg 会自动丢帧或复制帧来匹配目标帧率。
### 使用 fps 滤镜(推荐降帧场景)
```bash
ffmpeg -i input.mp4 -vf "fps=24" -c:v libx264 -crf 23 output.mp4
```
`fps` 滤镜比 `-r` 更精确,会均匀选取帧而非简单丢弃,画面过渡更平滑。
### 使用 setpts 调整播放速度
```bash
# 1.5 倍速播放(帧率相应提高)
ffmpeg -i input.mp4 -vf "setpts=PTS/1.5" -c:v libx264 -crf 23 output.mp4
# 0.5 倍速播放(慢放)
ffmpeg -i input.mp4 -vf "setpts=2*PTS" -c:v libx264 -crf 23 output.mp4
```
`setpts` 改变帧的时间戳,实现变速效果。注意变速时音频也需要同步处理(使用 `atempo` 滤镜)。
### 使用 -vsync 控制同步模式
```bash
ffmpeg -i input.mp4 -r 24 -vsync cfr -c:v libx264 -crf 23 output.mp4
```
- `cfr`:恒定帧率,不足的帧会复制,多余的帧丢弃,输出帧率严格恒定
- `vfr`:可变帧率,保持原始时间戳,不插帧不丢帧
- `auto`:根据输入自动选择(默认)
直播和流媒体推荐 `cfr`,保证播放器解码稳定。
## 查看视频信息:调整前先了解源文件
调整参数前,先用 ffprobe 查看源视频信息:
```bash
# 查看完整流信息
ffprobe -v error -show_streams input.mp4
# 只看码率
ffprobe -v error -select_streams v:0 -show_entries stream=bit_rate -of default=noprint_wrappers=1 input.mp4
# 只看分辨率和帧率
ffprobe -v error -select_streams v:0 -show_entries stream=width,height,r_frame_rate -of default=noprint_wrappers=1 input.mp4
```
## 组合使用:常见场景的完整命令
### 压缩视频体积(保持画质)
```bash
ffmpeg -i input.mp4 -c:v libx264 -crf 28 -preset slow -c:a aac -b:a 128k output.mp4
```
### 适配移动端(720p + 适中码率)
```bash
ffmpeg -i input.mp4 -vf "scale=-2:720" -c:v libx264 -crf 26 -preset medium -c:a aac -b:a 128k output.mp4
```
### 流媒体推送(固定码率 + 720p + 30fps)
```bash
ffmpeg -i input.mp4 -vf "scale=-2:720" -c:v libx264 -b:v 3000k -maxrate 3500k -bufsize 6000k -r 30 -vsync cfr -c:a aac -b:a 128k output.mp4
```
### H.265 编码(同画质体积减半)
```bash
ffmpeg -i input.mp4 -c:v libx265 -crf 28 -preset medium -c:a aac -b:a 128k output.mp4
```
H.265/HEVC 比 H.264 在同等画质下节省约 40-50% 码率,但编码速度慢、兼容性稍差。
## 常见问题
### 调整后画质明显下降
码率不足是主因。如果是用 `-b:v` 控制码率,尝试提高码率或改用 CRF 模式(`-crf 23`)。降分辨率时指定 `lanczos` 算法可以减少模糊。
### 分辨率调整后画面变形
没有保持宽高比。用 `scale=-2:720` 替代 `scale=1280:720`,让宽度自动计算。`-2` 保证宽度为偶数,满足编码器要求。
### 帧率调整后播放抖动
源帧率与目标帧率不匹配导致丢帧不均匀。使用 `fps` 滤镜(`-vf "fps=24"`)替代 `-r` 参数,前者会均匀选帧;或加 `-vsync cfr` 强制恒定帧率输出。
### 编码速度太慢
降低 `-preset` 参数值(如从 `slow` 改为 `fast`),或使用硬件加速编码器(如 `-c:v h264_videotoolbox` macOS / `-c:v h264_nvenc` NVIDIA / `-c:v h264_qsv` Intel)。
### -vbr 参数不生效
libx264 不支持 `-vbr` 参数。码率控制应使用 `-crf`(推荐)、`-b:v` + `-maxrate`(VBR 限峰)或两遍编码(`-pass`)。`-vbr` 仅适用于部分编码器(如 libvpx)。前端5月28日 01:59
如何用FFmpeg实现直播推流?需要哪些命令和参数?FFmpeg 推流的核心就三步:指定输入源、设置编码参数、指向推流地址。掌握这几个环节的组合方式,就能应对绝大多数直播推流场景。
## FFmpeg 推流的基本命令结构
一条完整的推流命令长这样:
```bash
ffmpeg -i input.mp4 -c:v libx264 -preset fast -crf 23 -c:a aac -b:a 128k -f flv rtmp://server/live/stream
```
拆开来看:
- **`-i`**:输入源,可以是本地文件、摄像头设备或网络流
- **`-c:v` / `-c:a`**:视频/音频编码器,直播场景下视频常用 `libx264`,音频常用 `aac`
- **`-f flv`**:输出封装格式,RTMP 推流必须用 FLV
- **最后的 URL**:推流地址,格式为 `rtmp://服务器地址/应用名/流名`
> `-re` 参数在推本地文件时必须加,它让 FFmpeg 按原始帧率读取输入,否则会以最快速度推完。推摄像头或 RTSP 等实时源时不需要加。
## 视频编码参数怎么选?
### 编码器与预设
```bash
-c:v libx264 -preset veryfast -tune zerolatency
```
- **`-preset`** 控制编码速度与压缩率的平衡,从慢到快依次为 `slow` → `medium` → `fast` → `veryfast` → `ultrafast`。直播场景建议 `veryfast` 或 `ultrafast`,优先保证低延迟
- **`-tune zerolatency`** 关闭前瞻分析,进一步降低延迟,互动直播必加
### 码率与质量控制
两种控制方式选其一:
**方式一:CRF 恒定质量**(适合带宽充足的场景)
```bash
-crf 23 -maxrate 2500k -bufsize 5000k
```
CRF 值越低质量越高,直播推荐 18-28。配合 `-maxrate` 和 `-bufsize` 设置上限,防止码率飙升导致卡顿。
**方式二:CBR 恒定码率**(适合带宽受限的场景)
```bash
-b:v 1500k -maxrate 1500k -bufsize 3000k
```
码率固定,网络波动时更稳定。`bufsize` 通常设为 `maxrate` 的 2 倍。
### 分辨率与帧率
```bash
-s 1280x720 -r 25 -g 50 -keyint_min 25
```
- **`-g`** 设置 GOP 大小(关键帧间隔),建议等于帧率的 2 倍,方便客户端随时切入
- **`-keyint_min`** 设置最小关键帧间隔,与 `-g` 保持一致可确保关键帧间隔均匀
## 音频编码参数怎么配?
```bash
-c:a aac -b:a 128k -ar 44100 -ac 2
```
- **`-b:a 128k`**:音频码率,语音直播 96k 足够,音乐直播建议 128-192k
- **`-ar 44100`**:采样率,44100Hz 是标准值
- **`-ac 2`**:双声道,单声道直播可设为 1
> 如果遇到音画不同步,加 `-async 1` 强制音频同步,或用 `-vsync cfr` 固定视频帧率。
## 常见推流场景的完整命令
### 本地文件推流到 RTMP 服务器
```bash
ffmpeg -re -i input.mp4 -c:v libx264 -preset veryfast -crf 23 -maxrate 2500k -bufsize 5000k -c:a aac -b:a 128k -f flv rtmp://your-server.com/live/stream
```
关键点:`-re` 按原始帧率推流,`-crf 23` 平衡质量与码率。
### 摄像头实时推流(低延迟)
```bash
ffmpeg -f v4l2 -i /dev/video0 -f alsa -i default -c:v libx264 -preset ultrafast -tune zerolatency -crf 28 -c:a aac -b:a 128k -f flv rtmp://server/live/low-latency
```
关键点:`-f v4l2` 捕获 Linux 摄像头,macOS 用 `-f avfoundation -i "0"`,Windows 用 `-f dshow -i video="摄像头名称"`。`ultrafast` + `zerolatency` 追求最低延迟。
### 循环推流(24小时轮播)
```bash
ffmpeg -re -stream_loop -1 -i input.mp4 -c:v libx264 -preset fast -b:v 1500k -c:a aac -b:a 128k -f flv rtmp://server/live/loop
```
关键点:`-stream_loop -1` 无限循环,适合轮播场景。
### RTSP 转 RTMP 推流
```bash
ffmpeg -rtsp_transport tcp -i rtsp://camera-ip/stream -c:v libx264 -preset veryfast -b:v 2000k -c:a aac -b:a 128k -f flv rtmp://server/live/camera
```
关键点:`-rtsp_transport tcp` 用 TCP 拉取 RTSP 流,避免 UDP 丢包。如果 RTSP 源已经是 H.264 编码,可以用 `-c:v copy` 直接复制视频流,省去重编码开销。
### SRT 协议推流
```bash
ffmpeg -re -i input.mp4 -c:v libx264 -preset fast -b:v 2500k -c:a aac -b:a 128k -f mpegts 'srt://server:9000?streamid=live/stream'
```
关键点:SRT 在弱网环境下比 RTMP 更稳定,支持加密传输。输出格式用 `mpegts` 而非 `flv`。
### 多路推流(同时推到多个平台)
```bash
ffmpeg -re -i input.mp4 -c:v libx264 -preset fast -b:v 2000k -c:a aac -b:a 128k -f flv rtmp://platform-a.com/live/stream1 -c:v libx264 -preset fast -b:v 1500k -c:a aac -b:a 128k -f flv rtmp://platform-b.com/live/stream2
```
关键点:每个输出地址前指定独立的编码参数,可实现不同平台推不同画质。
## 硬件加速推流
CPU 编码吃满时可以用 GPU 加速:
```bash
# NVIDIA GPU
ffmpeg -hwaccel cuda -i input.mp4 -c:v h264_nvenc -preset p4 -b:v 2500k -c:a aac -b:a 128k -f flv rtmp://server/live/gpu
# Intel GPU (VAAPI,Linux)
ffmpeg -hwaccel vaapi -hwaccel_device /dev/dri/renderD128 -i input.mp4 -c:v h264_vaapi -b:v 2500k -c:a aac -b:a 128k -f flv rtmp://server/live/gpu
# macOS (VideoToolbox)
ffmpeg -hwaccel videotoolbox -i input.mp4 -c:v h264_videotoolbox -b:v 2500k -c:a aac -b:a 128k -f flv rtmp://server/live/gpu
```
硬件编码延迟更低、吞吐更高,但画质略逊于 `libx264` 的 `slow` 预设。实际选择取决于业务优先级:画质选软编,性能选硬编。
## 推流常见问题排查
### 推流失败:Connection refused
检查 RTMP 服务器地址和端口是否正确,确认服务器防火墙放行了 1935 端口。用 `telnet server 1935` 快速验证网络连通性。
### 视频卡顿:帧率不稳或画面跳跃
- 降低视频码率(`-b:v` 从 2500k 降到 1500k)
- 增大缓冲区(`-bufsize` 设为 `-maxrate` 的 2-3 倍)
- 换更快的编码预设(`-preset ultrafast`)
### 音画不同步
- 加 `-async 1` 强制音频同步
- 加 `-vsync cfr` 固定视频帧率
- 检查输入源本身是否同步(用 `ffprobe` 查看 PTS 信息)
### 推流延迟过高
- 加 `-tune zerolatency` 关闭前瞻
- 减小 GOP 大小(`-g 25`)
- 用 `-preset ultrafast` 加快编码
- 考虑换 SRT 协议,弱网下延迟更低
### 推流过程中断流
加自动重连参数:
```bash
ffmpeg -re -rtmp_live live -timeout 10000000 -i input.mp4 -c:v libx264 -preset fast -b:v 1500k -c:a aac -b:a 128k -f flv rtmp://server/live/stream
```
配合 shell 脚本实现断线自动重启:
```bash
while true; do
ffmpeg -re -i input.mp4 -c:v libx264 -preset fast -b:v 1500k -c:a aac -b:a 128k -f flv rtmp://server/live/stream
sleep 2
done
```
## 推流参数速查表
| 场景 | 编码预设 | 码率 | CRF | GOP | 特殊参数 |
|------|---------|------|-----|-----|---------|
| 互动直播 | ultrafast + zerolatency | 1500-2500k | 28 | 50 | `-tune zerolatency` |
| 高清直播 | fast | 3000-6000k | 23 | 50 | `-maxrate` + `-bufsize` |
| 弱网推流 | veryfast | 800-1500k | 28 | 25 | SRT 协议 |
| 轮播推流 | fast | 1500-2500k | 23 | 50 | `-stream_loop -1` |
| GPU 加速 | N/A (硬件编码) | 2500-4000k | N/A | 50 | `-hwaccel cuda/vaapi` |
实际推流时没有万能参数组合,需要根据网络带宽、服务器配置和画质要求调整。建议先在测试环境用 `ffmpeg -loglevel verbose` 跑一遍,观察实际码率和丢帧情况再上线。前端5月28日 00:29
遇到FFmpeg转码失败,如何定位和排查问题?FFmpeg转码失败是视频工程中最头疼的问题之一——报错信息往往一大堆,但真正有用的线索却很难找。这篇文章整理了一套从快速定位到深层排查的实战方法,覆盖输入文件异常、编码器限制、资源瓶颈、硬件加速冲突等常见场景,帮你把排查时间从小时级压缩到分钟级。
## 转码失败的三大典型原因
转码失败看似千奇百怪,但归类下来逃不出这三类:
- **输入文件有问题**:容器格式损坏(比如MP4里嵌了非标准时间戳)、编码参数冲突(H.264流里包含不支持的B帧)、文件权限不足。跑一下`ffmpeg -i corrupt.mp4`,如果输出`Invalid data found when processing input`,就是文件结构本身有问题。
- **编码器不兼容**:不同编码器对输入码流有硬性要求。输入视频是10bit YUV420,目标编码器只支持8bit,就会报`Encoder init failed`。再比如输入是H.265流但系统没装libx265,也会直接报错。
- **系统资源不够**:低内存服务器跑4K转码,容易出现`Out of memory`或CPU过载。Docker容器里还可能遇到GPU设备未正确挂载的问题。
先用`ffprobe`做个快速预检:
```bash
ffprobe -v error -show_streams -show_format input.mp4
```
如果`Stream #0:0`显示`codec_name=unknown`,容器大概率损坏了;如果SAR/DAR值为负数,得先修元数据再转码。
## 四步排查法:从快到慢定位问题
### 第一步:看错误日志,锁定方向
别用默认日志级别,信息太多反而干扰。直接开error级别:
```bash
ffmpeg -v error -i input.mp4 -c:v libx264 output.mp4
```
常见错误信号:
| 错误信息 | 含义 |
|---------|------|
| `Invalid NAL unit` | H.264流损坏 |
| `1 output(s) and 0 input(s) are available` | 滤镜链配置错误 |
| `Encoder init failed` | 编码器不支持输入格式 |
| `frame size mismatch` | 输入帧大小不一致 |
| `Permission denied` | 文件路径或写入权限问题 |
### 第二步:隔离输入文件,确认源是否正常
用`ffplay`试播一下:
```bash
ffplay -v error -i input.mp4
```
播不了就先解决输入文件的问题。能播但转码失败,问题大概率在编码器参数或资源限制上。
### 第三步:最小化命令测试,排除参数干扰
把复杂参数全去掉,先跑最基础的转码:
```bash
ffmpeg -v error -i input.mp4 -c:v copy -c:a aac output.mp4
```
成功了说明输入文件没问题,故障在编码器参数上。这时逐步加参数,每次加一个,出错了就知道是哪个参数惹的祸。
如果连`-c:v copy`都失败,那就是输入文件本身有问题,回到第二步。
### 第四步:开debug日志,深挖根因
前三步还没定位到?上debug级别日志:
```bash
ffmpeg -loglevel debug -report -i input.mp4 -c:v libx264 -crf 23 output.mp4
```
`-report`会生成详细日志文件,里面记录了每一步的处理过程。日志里出现`encoding pass 1`但后面没有`pass 2`,说明输入流中途断了。
用`grep`快速过滤关键错误:
```bash
cat ffmpeg-*.log | grep -i 'error\|fail\|invalid'
```
## 生产环境高频踩坑场景
### 硬件加速转码失败
用VAAPI或NVENC做硬件加速转码时,失败率比纯软件编码高得多:
- **VAAPI初始化失败**:检查`/sys/kernel/debug/dri/`路径是否存在,Docker容器里需要把GPU设备正确挂载进去(`--device /dev/dri:/dev/dri`)
- **NVENC报错**:确认驱动版本和nvidia-container-toolkit是否匹配,驱动太老会直接初始化失败
- **Intel低功耗编码**:GuC和HuC固件是否正常运行,`sudo cat /sys/kernel/debug/dri/0/i915_guc_info`可以确认
**排查思路**:先用纯软件编码测试,成功后再切换硬件加速,这样能快速判断是硬件环境的问题还是命令参数的问题。
### Docker容器里转码异常
Docker是转码问题的高发地带,常见坑:
- GPU设备没挂载,硬件加速不可用
- 容器内FFmpeg版本和宿主机不一致,编码器支持列表不同
- `/etc/ld.so.conf.d/`里缺少库路径配置,动态链接找不到编解码库
- 容器内存限制太紧,转码4K视频时OOM
建议在Dockerfile里明确安装需要的编解码库,别依赖基础镜像自带的。
### 版本兼容性问题
FFmpeg不同版本差异很大:
- 老版本的`-autorotate`参数在新版本被废弃了,会直接报错
- 某些编码器只在特定编译配置下可用(`ffmpeg -encoders`查看当前支持的编码器列表)
- 从源码编译时如果没加`--enable-libx265`,HEVC编码就不可用
**关键操作**:转码前先确认环境,跑一下`ffmpeg -version`和`ffmpeg -encoders | grep libx265`。
## 自动化排查脚本
日常巡检可以跑这个脚本,批量检查输入文件是否有效:
```bash
#!/bin/bash
for file in *.mp4; do
if ! ffprobe -v error -i "$file" &>/dev/null; then
echo "[INVALID] $file"
else
codec=$(ffprobe -v error -select_streams v:0 -show_entries stream=codec_name -of csv=p=0 "$file")
echo "[OK] $file - codec: $codec"
fi
done
```
转码任务建议加上资源监控,内存超过80%就该报警了:
```bash
ffmpeg -i input.mp4 -c:v libx264 output.mp4 &
PID=$!
while kill -0 $PID 2>/dev/null; do
mem=$(ps -o %mem -p $PID --no-headers | tr -d ' ')
if (( $(echo "$mem > 80" | bc -l) )); then
echo "WARNING: Memory usage ${mem}%"
fi
sleep 5
done
```
## 常见错误速查表
| 报错信息 | 原因 | 解决方法 |
|---------|------|---------|
| `Invalid data found when processing input` | 输入文件损坏 | 用ffprobe检查,尝试用mkvtoolnix修复 |
| `Encoder init failed` | 编码器不支持输入格式 | 安装对应编码库或转换输入格式 |
| `Invalid NAL unit` | H.264流损坏 | 尝试`-c:v copy`跳过重编码 |
| `frame size mismatch` | 输入帧大小不一致 | 加`-s 1920x1080`强制统一 |
| `Out of memory` | 内存不足 | 降低分辨率或增加交换空间 |
| `Unknown encoder 'libx265'` | 编码库未安装 | `sudo apt install libx265-dev`重新编译 |
| `Permission denied` | 文件权限问题 | 检查路径权限和磁盘空间 |
| `SAR/DAR negative value` | 元数据异常 | 用mkvtoolnix重封装修复 |
排查FFmpeg转码问题,核心就是"先隔离再定位":先确认输入文件正常,再用最小命令验证基础链路,最后才上复杂参数。生产环境里,日志监控(比如ELK Stack采集FFmpeg日志)和资源告警比事后排查更重要——转码失败往往不是单一原因,而是输入格式、编码器配置、系统资源多维叠加的结果,实时监控能在问题扩散前就抓住线索。