标签

FFmpeg

FFmpeg 是一个开源的跨平台解决方案,用于录制、转换和流式传输音频和视频。它提供了强大的命令行工具,可以处理多种多样的多媒体格式,并提供了一组丰富的库和组件,使得开发者能够在自己的应用程序中集成音视频处理功能。

FFmpeg
查看更多相关内容
服务端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` 加快迭代,确认效果后再用原始分辨率输出
服务端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: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日志)和资源告警比事后排查更重要——转码失败往往不是单一原因,而是输入格式、编码器配置、系统资源多维叠加的结果,实时监控能在问题扩散前就抓住线索。