FFmpeg开发指南:从核心概念到实战录制
ssk-wh Lv5

注意:本指南默认您已掌握C语言基础和Linux命令行操作。若不会使用gcc,建议先学习《C语言:从入门到内存泄漏》。


一、FFmpeg简介:音视频界的瑞士军刀

FFmpeg集成了600+编解码器100+封装格式的超级工具链:

image

核心组件功能

  • libavcodec:编解码器大本营(H.264/HEVC/AAC/MP3)
  • libavformat:封装/解封装指挥官(MP4/FLV/TS等)
  • libavdevice:硬件设备特工(摄像头/麦克风/屏幕捕获)
  • libswscale:像素魔术师(YUV↔RGB转换)
  • libavutil:工具百宝箱(哈希/数学/日志)

官网金句:FFmpeg is the future, but you can type ffmpeg today

下面用「一句话 + 命令/思路」的形式,把 FFmpeg 能做的「日常必备」到「高阶黑科技」一次性列给你。直接复制命令即可跑;带 的表示进阶/易踩坑。


1.常用操作(90% 场景覆盖)

目的 一句话示例
查看媒体信息 ffprobe -hide_banner -i input.mp4
无损合并 ts 分段 `ffmpeg -i “concat:1.ts
提取音频 ffmpeg -i in.mp4 -vn -c:a copy a.aac
提取视频 ffmpeg -i in.mp4 -an -c:v copy v.mp4
裁剪片段 ffmpeg -ss 00:01:00 -to 00:01:30 -i in.mp4 -c copy clip.mp4
转码 H.264 → H.265 ffmpeg -i in.mp4 -c:v libx265 -crf 28 -c:a copy out.mp4
调整分辨率 ffmpeg -i in.mp4 -vf scale=1280:-2 -c:a copy out.mp4
加水印 ffmpeg -i in.mp4 -i logo.png -filter_complex "overlay=W-w-10:H-h-10" out.mp4
GIF 转 MP4 ffmpeg -f gif -i anim.gif -pix_fmt yuv420p out.mp4
合并音视频 ffmpeg -i v.mp4 -i a.m4a -c copy va.mp4

2. 高级玩法(性能/画质/实时向)

目的 一句话示例 / 思路
4K60 实时 NVENC 硬编‼ ffmpeg -hwaccel cuda -i in.mp4 -c:v h264_nvenc -preset p7 -b:v 50M out.mp4
录屏+摄像头画中画‼ ffmpeg -f x11grab -s 1920x1080 -i :0.0 -f v4l2 -video_size 320x240 -i /dev/video0 -filter_complex "[0:v][1:v]overlay=W-w-10:H-h-10" -c:v libx264 out.mkv
直播推流 RTMP‼ ffmpeg -re -i in.mp4 -c:v libx264 -preset veryfast -f flv rtmp://server/app/stream
10-bit HDR → SDR tonemap‼ ffmpeg -i hdr.mp4 -vf zscale=transfer=linear,tonemap=clip,zscale=transfer=bt709,format=yuv420p -c:v libx265 sdr.mp4
多线程极速转码‼ ffmpeg -i in.mp4 -c:v libx264 -preset ultrafast -threads 0 -c:a aac out.mp4
抽帧做缩略图网格‼ ffmpeg -i in.mp4 -vf "select=not(mod(n\,30)),scale=320:-1,tile=4x4" -frames 1 grid.jpg
实时音频重采样‼ ffmpeg -f pulse -i default -af aresample=44100,aformat=fltp -c:a aac out.m4a
无重新编码快速倒放‼ ffmpeg -i in.mp4 -vf reverse -af areverse -c copy reverse.mp4
生成测试信号(彩条+噪声) ffmpeg -f lavfi -i testsrc=duration=10:size=1920x1080:rate=30 -f lavfi -i sine=f=1000 -shortest test.mp4
提取 RAW YUV ffmpeg -i in.mp4 -c:v rawvideo -pix_fmt yuv420p out.yuv

3.黑科技天花板(需要二次开发/脚本)

目的 一句话思路
帧级精确剪辑‼ ffprobe -select_streams v -show_frames -print_format csv 解析 pts → ss/to 毫秒级切割
AI 超分前处理‼ ffmpeg -i in.mp4 -vf zscale=1920:1080:filter=lanczos -c:v png frame%06d.png → 超分 → 再编码
低延迟云游戏采集‼ ffmpeg -f kmsgrab -i - -vf hwdownload,format=nv12 -c:v h264_nvenc -tune zerolatency -f rtsp rtsp://host/live
音视频自动对齐‼ 先用 ffprobestart_time → 计算偏移 → itsoffset 参数
DRM 直接抓帧‼ ffmpeg -f kmsgrab -crtc_id 63 -framerate 60 -i - ... (需 root 权限)

4.速查表

场景 关键参数
最高压缩 -c:v libx265 -crf 28 -preset slow
实时/直播 -preset ultrafast -tune zerolatency
无损备份 -c:v ffv1 -level 3 -c:a flac
仅复制流 -c copy
精确毫秒 -ss 00:01:23.456 -to 00:01:25.000

一句话总结:
从「剪个 10 秒小视频」到「4K60 帧低延迟直播」,FFmpeg 一条命令行就能搞定;再往上,用脚本/代码把 FFmpeg 当「底层 GPU 音视频引擎」用,天花板几乎没有。


二、核心数据结构:四大天王

1. AVFormatContext - 容器指挥官

1
2
AVFormatContext* fmt_ctx = NULL;
avformat_alloc_output_context2(&fmt_ctx, NULL, "mp4", "output.mp4");

职责

  • 管理文件/流的封装信息
  • 关键字段:
    • nb_streams:流数量
    • pb:I/O上下文指针

类比:快递分拣中心,管理所有包裹(音视频流)

2. AVCodecContext - 编解码大脑

1
2
3
4
AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_H264);
AVCodecContext* codec_ctx = avcodec_alloc_context3(codec);
codec_ctx->bit_rate = 4000000; // 4Mbps
codec_ctx->framerate = (AVRational){30, 1}; // 30FPS
参数 视频示例 音频示例
分辨率 1920x1080 -
采样率 - 44100 Hz
像素格式 YUV420P -
声道布局 - AV_CH_LAYOUT_STEREO

避坑指南:未设置帧率会导致视频加速播放

3. AVPacket - 压缩数据包

1
2
3
4
5
AVPacket* pkt = av_packet_alloc();
while (av_read_frame(fmt_ctx, pkt) >= 0) {
// 处理H.264 NAL单元
av_packet_unref(pkt); // 关键!防内存泄漏
}

特点

  • 携带编码后的音视频数据
  • 使用引用计数管理内存

4. AVFrame - 原始数据帧

1
2
3
AVFrame* frame = av_frame_alloc();
frame->format = AV_PIX_FMT_YUV420P;
av_frame_get_buffer(frame, 0); // 显式分配内存

数据存储

  • 视频:data[0](Y), data[1](U), data[2](V)
  • 音频:data[0](左声道), data[1](右声道)

冷知识:人眼对亮度敏感度是色度的6倍,故YUV420P更省带宽!


三、核心API详解

1.设备操作三剑客

函数 作用 返回值 经典错误
avdevice_register_all() 注册设备 void 未调用导致设备找不到
av_find_input_format() 查找设备格式 AVInputFormat* 返回NULL表示格式不支持
avformat_open_input() 打开设备 int 负数需用av_strerror解析

2.编解码关键流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 查找编解码器
AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);

// 创建编解码上下文
AVCodecContext *ctx = avcodec_alloc_context3(codec);
ctx->width = 1920;
ctx->height = 1080;
ctx->time_base = (AVRational){1, 30}; // 必须设置!

// 打开编解码器
if (avcodec_open2(ctx, codec, NULL) < 0) {
char err_buf[256];
av_strerror(ret, err_buf, sizeof(err_buf)); // 错误解析
}

// 编码循环
avcodec_send_frame(ctx, frame); // 送入原始帧
while (avcodec_receive_packet(ctx, pkt) == 0) {
// 处理编码包
}

3.数据转换核心

函数 作用 适用场景
sws_getContext() 图像缩放/格式转换 摄像头YUV→编码器YUV
swr_alloc_set_opts() 音频重采样 麦克风PCM→AAC编码
av_frame_make_writable() 避免内存拷贝 高性能处理

四、深入PulseAudio

1.核心架构

image

2.设备类型详解

类型 功能 FFmpeg标识 查询命令
Source 音频输入 -f pulse -i default pactl list sources
Sink 音频输出 不支持直接访问 pactl list sinks
Monitor 监听输出 .monitor
后缀
pactl list sources

3.实战场景

1
2
3
4
5
6
// 录制麦克风
avformat_open_input(&ctx, "default", av_find_input_format("pulse"), NULL);

// 录制系统声音(关键!)
avformat_open_input(&ctx, "alsa_output.pci-0000_00_1f.3.analog-stereo.monitor",
av_find_input_format("pulse"), NULL);

系统救星pulseaudio -k && pulseaudio --start 解决90%音频问题


五、音视频录制实战

1. 摄像头录制(Linux V4L2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
avdevice_register_all();
AVInputFormat* input_fmt = av_find_input_format("v4l2");
avformat_open_input(&cam_ctx, "/dev/video0", input_fmt, NULL);

// 转码决策树
if (codecpar->codec_id == AV_CODEC_ID_MJPEG) {
// 直接流复制
avcodec_parameters_copy(out_stream->codecpar, codecpar);
} else {
// 完整转码流水线
AVCodec *decoder = avcodec_find_decoder(codecpar->codec_id);
AVCodec *encoder = avcodec_find_encoder(AV_CODEC_ID_H264);
// ...构建解码->处理->编码流水线
}

2. 麦克风录制(PulseAudio)

1
2
3
4
5
6
7
SwrContext* swr = swr_alloc_set_opts(NULL, 
AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_FLTP, 44100, // 输出格式
src_ch_layout, src_sample_fmt, src_sample_rate, // 输入格式
0, NULL);
swr_init(swr); // 必须初始化!

// 音频冷知识:AAC编码器强制要求浮点平面格式(AV_SAMPLE_FMT_FLTP)

3. 音视频同步(核心!)

1
2
3
4
5
6
7
8
9
10
// 统一时间基准(微秒级精度)
int64_t video_pts = av_rescale_q(frame->pts, video_time_base, AV_TIME_BASE_Q);
int64_t audio_pts = av_rescale_q(samples->pts, audio_time_base, AV_TIME_BASE_Q);

// 同步写入决策
if (video_pts <= audio_pts) {
av_interleaved_write_frame(fmt_ctx, video_pkt); // 优先写视频
} else {
av_interleaved_write_frame(fmt_ctx, audio_pkt); // 优先写音频
}

同步要诀:使用av_rescale_q而非手动计算,避免精度丢失

4.音频录制+重采样实战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 1. 打开麦克风
avformat_open_input(&audio_fmt_ctx, "default", av_find_input_format("pulse"), NULL);

// 2. 创建音频编码器
AVCodec *audio_codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
AVCodecContext *a_codec_ctx = avcodec_alloc_context3(audio_codec);
a_codec_ctx->sample_rate = 44100;
a_codec_ctx->channel_layout = AV_CH_LAYOUT_STEREO;

// 3. 重采样设置(解决格式不匹配)
SwrContext *swr = swr_alloc();
swr_alloc_set_opts2(&swr,
&a_codec_ctx->ch_layout, a_codec_ctx->sample_fmt, a_codec_ctx->sample_rate,
&in_ch_layout, in_sample_fmt, in_sample_rate,
0, NULL);
swr_init(swr);

// 4. 处理循环
while (1) {
av_read_frame(audio_fmt_ctx, pkt);
swr_convert(swr, out_samples, out_sample_count,
(const uint8_t**)pkt->data, pkt->size/4);
// 编码处理...
}

六、高级开发技巧

1. 性能优化三连击

1
2
3
4
5
6
7
8
9
// CUDA硬件加速(5x+速度提升)
ctx->hw_device_ctx = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_CUDA);

// 多线程编码(榨干CPU)
ctx->thread_count = 16;
ctx->thread_type = FF_THREAD_FRAME;

// 零拷贝优化
av_frame_make_writable(frame); // 避免隐式拷贝

2. 调试神器

工具 命令示例 功能
ffprobe ffprobe -show_streams input.mp4 分析文件结构
ffplay ffplay -f v4l2 /dev/video0 实时预览设备流

3.必备诊断命令

1
2
3
4
5
6
7
8
# 检查设备支持
ffmpeg -f pulse -list_devices true -i dummy

# 测试摄像头
ffplay -f v4l2 -video_size 1280x720 /dev/video0

# 分析输出文件
ffprobe -show_streams output.mp4

4.经典错误表

错误码 含义 解决方案
AVERROR(EAGAIN) 资源暂时不可用 重试或等待
AVERROR(ENOMEM) 内存不足 检查资源泄露
AVERROR(EINVAL) 无效参数 检查参数合法性
AVERROR_PATCHWELCOME 功能未实现 升级FFmpeg版本

5. 常见问题急救包

问题:视频花屏
原因

  • 关键帧丢失(I帧被狗吃了)
  • PTS时间戳不连续
    解决方案
1
2
3
4
5
6
// 强制插入关键帧
frame->key_frame = 1;
frame->pict_type = AV_PICTURE_TYPE_I;

// 修复时间戳(避免跳跃)
frame->pts = last_pts + av_rescale_q(1, time_base, stream_time_base);

6. 跨平台兼容方案

功能 Linux Windows macOS
摄像头访问 v4l2 dshow avfoundation
音频录制 pulse/alsa dshow avfoundation
屏幕录制 x11grab gdigrab avfoundation
硬件加速 VAAPI/NVDEC DXVA2/NVDEC VideoToolbox

注意事项

  1. Linux (PulseAudio)
    • 需要安装libpulse-dev
    • 设备名称为default或特定设备名称
  2. Windows (DirectShow)
    • 修改设备名称为audio=麦克风名称
    • 可能需要先列出可用设备
  3. macOS (AVFoundation)
    • 修改设备名称为:0或特定设备索引
    • 输入格式应为avfoundation

七、终极Q&A

1. 为什么摄像头录制中同时需要解码器和编码器

  • 摄像头输出是压缩数据(如MJPEG)
  • 目标格式需要重新编码(如H.264)
  • 转码流水线:MJPEG -> YUV -> H.264

2.AVPacket 和 AVFrame 之间的关系

  1. 解码过程:
    • 从多媒体文件中读取 AVPacket。
    • 使用解码器将 AVPacket 中的压缩数据解码到 AVFrame 中。
    • 处理(例如,显示或进一步处理)AVFrame 中的未压缩数据。
  2. 编码过程:
    • 从原始数据(例如,摄像头捕获的视频帧或麦克风捕获的音频样本)创建 AVFrame。
    • 使用编码器将 AVFrame 中的未压缩数据编码到 AVPacket 中。
    • 将 AVPacket 写入到多媒体文件中。
  3. 转换过程:
    • 读取 AVPacket,解码到 AVFrame。
    • 对 AVFrame 进行格式转换或处理。
    • 将处理后的 AVFrame 重新编码到 AVPacket。
    • 写入新的 AVPacket 到输出文件。

通过这种方式,AVPacket 和 AVFrame 在 FFmpeg 的多媒体处理流程中起到了桥梁的作用,确保数据能够在不同的处理阶段之间正确传递和转换。

3.一些性能优化思路(待验证)

1.从DRM(Direct Rendering Manager)直接获取帧数据可以显著提高屏幕捕获效率,避免通过 X11 的额外开销—缺点是通过 drm 需要 root 权限,仅支持 linux,实现起来较为复杂。

2.优化转码算法,录屏耗时一般都在捕获和转码阶段,参考开源录屏应用 simplescreenrecorder(ssr) 的算法实现,可有效较少转码耗时—转码本质上是数学运算,一些 cpu 指令集对特定数学运算也有较好的加速效果。

3.RGB 到 YUV 的转换可以使用更高效的 SIMD 指令优化

4.录制音频时怎么查找系统的默认音频设备

1
2
3
$ pactl list sources short
0 alsa_output.pci-0000_00_1f.3.analog-stereo.monitor module-alsa-card.c s16le 2ch 44100Hz SUSPENDED
1 alsa_input.pci-0000_00_1f.3.analog-stereo module-alsa-card.c s16le 2ch 44100Hz SUSPENDED

选 **带 ****monitor** 的那一行即可。

也可以到操作系统的设置中查看输出设备是否一致,命令可能会查询到多个输出设备

5.官方示例

以 linux 操作系统为例,如果是基于 Debian 的发行版,运行以下命令安装示例:

1
sudo apt install ffmpeg-doc

image

6.录制的视频有时候会花屏

1.帧间预测依赖链断裂-视频编码基于 I帧(关键帧)P帧(前向预测)B帧(双向预测)(见附2,先了解下 I P B 帧的概念)

2.时间戳不连续,也就是帧的 pts

7.视频播放速度是怎么控制的

播放行为本质上是:

  1. 以PTS为绝对时间基准
  2. 以预设帧率为初始化参考
  3. 通过动态同步算法实时调整

八、结语:通往音视频大师之路

当你:

  1. 能30分钟写出录制程序 → 青铜
  2. 搞定音视频同步 → 白银
  3. 实现4K60帧编码 → 黄金
  4. 修复花屏/卡顿问题 → 王者
  5. 给FFmpeg提交补丁 → 封神

FFmpeg不是一天学会的,每个崩溃的程序都让你离大师更近一步。

附0:ffmpeg重点接口

1.编解码相关接口

avcodec_find_encoder/avcodec_find_decoder 查找指定的编解码器。 在编码或解码之前,需要通过这些函数获取正确的编解码器。
avcodec_open2 打开一个编解码器实例,初始化编解码器上下文。 在开始编解码操作之前,必须调用此函数以确保编解码器准备好。
avcodec_send_frame/avcodec_receive_packet 分别用于将解码后的帧发送给编码器,以及从编码器接收编码后的数据包。 在编码过程中,用于将帧数据传递给编码器并获取编码结果。
avcodec_send_packet/avcodec_receive_frame 分别用于将编码后的数据包发送给解码器,以及从解码器接收解码后的帧。 在解码过程中,用于处理编码数据并获取解码后的帧数据。

2.格式上下文相关接口

avformat_open_input 打开一个媒体文件输入流。 读取媒体文件时使用,用于初始化 AVFormatContext。
avformat_write_header 写入媒体文件的文件头信息。 在创建媒体文件时,用于写入必要的格式和流信息。
av_interleaved_write_frame 将编码后的数据包写入文件,支持交错写入。 在录制或转码过程中,用于将编码后的数据写入目标文件。
avformat_close_input/avio_closep 关闭媒体文件输入流和输出流。 在完成读写操作后,清理资源。

3.图像转换相关接口

sws_getContext 创建一个图像转换上下文,用于在不同像素格式之间进行转换。 在处理屏幕捕获或视频帧格式转换时使用。
sws_scale 执行图像转换,将一个图像从一种像素格式转换为另一种格式。 在需要调整图像大小或格式时使用,例如将捕获的屏幕图像从 RGB 转换为 YUV 格式。

4.其他重要接口

avformat_alloc_output_context2 分配一个输出上下文,用于创建输出文件。 在录制或转码时初始化输出文件的格式和流。
avcodec_parameters_from_context 将编解码器上下文的参数复制到流参数中。 在创建输出流时,用于设置流的编解码器参数。
av_packet_alloc/av_packet_unref 分配和释放 AVPacket 的内存。 在处理编码数据包时,用于管理数据包的生命周期。

5.高级功能

AVFilterGraph 用于构建和处理复杂的音视频滤镜链。 在需要对音视频数据进行复杂的实时处理(如裁剪、旋转、添加水印等)时使用。
avfilter_graph_create_filter 在滤镜图中创建一个新的滤镜实例。 用于初始化和配置滤镜链中的各个滤镜

附1:FFmpeg编解码器与封装格式大全

温馨提示:本清单仅展示FFmpeg支持的部分代表性格式(完整列表超600+),建议保存备用。当有人质疑FFmpeg能力时,请优雅地甩出此表😎


1.视频编解码器

主流编解码器 专有格式 创新编码 遗留格式
H.264/AVC Apple ProRes AV1 MJPEG
H.265/HEVC DNxHD VP9 Motion JPEG
AV1 CineForm Daala DV (Digital Video)
VP8 GoPro CineForm Thor HuffYUV
MPEG-2 Windows Media Video Dirac Sorenson Video
MPEG-4 Part 2 RealVideo Snow Indeo
Theora Flash Screen Video FFV1 (无损) MSU Screen Capture

2.音频编解码器

通用音频 语音编码 专业音频 创新格式
AAC Opus FLAC (无损) Dolby Atmos
MP3 Speex ALAC (Apple无损) DTS:X
AC-3 AMR-NB/WB DSD (Direct Stream) CELT
Vorbis G.711 (A-law/μ-law) PCM (各种位深) Siren
WMA GSM Dolby TrueHD AptX

3.视频容器格式

主流容器 流媒体专用 专业制作 设备专用
MP4 HLS (m3u8) MXF 3GP
MKV DASH GXF 3G2
AVI RTMP MOV (ProRes版) F4V
WebM RTSP XDCAM VOB (DVD)
FLV SRT AVC-Intra MOD (摄像机)

4.音频容器格式

通用音频 无损音频 广播专用 元数据载体
MP3 FLAC ADTS ID3v2
AAC WAV (PCM) LATM APE Tag
Ogg AIFF RTP Vorbis Comment
WMA DSF (DSD) AU Xiph Comment

5.特殊格式

类别 代表成员 特殊技能
图像序列 PNG, JPEG, TIFF, DPX 支持帧序列导出
游戏专用 Bink Video, Smacker 游戏过场动画专用
闭路电视 CCTV H.264+ 监控设备专用格式
光碟格式 Blu-ray, HD-DVD 蓝光碟片支持
字幕格式 SRT, ASS, WebVTT, DVB-SUB 支持硬字幕+软字幕

冷知识:FFmpeg甚至支持解析 .gif动图.ico图标文件,堪称格式界的”瑞士军刀”


6.结语:

当看到如此庞大的格式支持列表时,请记住FFmpeg作者的名言:

“我们不是选择支持什么格式,而是选择不支持什么格式——因为实在太少了”

(注:完整列表可通过 ffmpeg -codecsffmpeg -formats 查看)

附2:视频编码中的 I P B帧

庆幸的是,在 ffmpeg 的音视频编程中,我们只需要知道 IPB 帧 的概念即可,ffmpeg会自动处理这些帧。

1.I帧(Intra-coded Frame,关键帧)

  • 定义:完全独立编码的帧,不依赖其他帧即可解码。类似静态JPEG图像,仅使用帧内压缩(空间冗余压缩)。
  • 特点
    • 包含完整图像数据,解码时无需参考其他帧。
    • 压缩率最低(因无时间冗余利用),但可作为随机访问点(如快进、seek操作)。
    • 通常定期插入(如每秒1次),用于错误恢复(若P/B帧丢失,可从下一个I帧重新开始解码)。
  • 应用场景
    • 视频开头(首个帧必须是I帧)。
    • 场景切换时(新旧场景无关联,需重新生成完整帧)。
    • 广播流媒体(支持随机换台)。

2.P帧(Predicted Frame,前向预测帧)

  • 定义:通过前向预测编码的帧,仅参考过去的I帧或P帧
  • 工作原理
    • 基于运动估计(Motion Estimation),在参考帧中查找相似块(宏块),记录运动矢量(位移数据)和残差(预测误差)。
    • 例如:若背景静止,P帧只需编码移动物体的变化,大幅节省数据量。
  • 特点
    • 压缩率高于I帧(利用时间冗余),但解码依赖参考帧(若参考帧丢失会出错)。
    • 无法处理未来帧的遮挡关系(如物体从后方出现)。
  • 应用场景
    • 大多数连续运动的视频片段(如人物行走)。

3.B帧(Bidirectional Frame,双向预测帧)

  • 定义:通过双向预测编码的帧,可同时参考过去和未来的I/P帧
  • 工作原理
    • 综合前后参考帧进行插值预测。例如:若物体从左侧移动到右侧,B帧可结合前一帧(物体在左)和后一帧(物体在右)生成中间状态。
    • 支持直接模式(Direct Mode):复用运动矢量,进一步减少数据量。
  • 特点
    • 压缩率最高(因时间冗余利用最充分),但引入编码延迟(需等待未来帧到达)。
    • 解码顺序与显示顺序可能不同(如先解码未来的P帧再解码B帧)。
  • 应用场景
    • 非实时场景(如点播视频、蓝光光盘)。
    • 高速运动场景(如体育赛事),通过双向预测减少残差数据。

4.三者的协同工作流程

以典型的 IBBPBBP… GOP(Group of Pictures)结构为例:

  1. 解码顺序:I₀ → P₃ → B₁ → B₂ → P₆ → B₄ → B₅…
  2. 显示顺序:I₀ → B₁ → B₂ → P₃ → B₄ → B₅ → P₆…
    • 解码器会缓存未来帧(如P₃),以便解码B₁/B₂时使用。

5.关键对比表

属性 I帧 P帧 B帧
参考帧 过去的I/P帧 过去+未来的I/P帧
压缩率 最低 中等 最高
解码依赖 独立 依赖前向帧 依赖双向帧
延迟 高(需未来帧)
用途 随机访问、错误恢复 主流预测 高压缩优化

6.实际应用中的权衡

  • 直播场景:通常减少B帧(或不用)以降低延迟(如RTMP协议常用IPPP结构)。
  • 存储场景:大量使用B帧(如H.264的IBBBP... 结构),节省50%以上码率。
  • 错误恢复:若P/B帧丢失,解码器会跳过到下一个I帧(可能出现短暂花屏)。

理解这三类帧的机制,对优化编码参数(如GOP长度、B帧数量)和排查播放问题(如花屏、卡顿)至关重要。

网络上也有比较好的文章可以参考:https://zhuanlan.zhihu.com/p/614236013

附3: YUV 格式与 RGB 图像格式介绍

在YUV文件中,Y、U、V分别代表亮度(Luma)和两个色差(Chroma)分量。与一张真正的RGB图片相比,它们的对应关系如下:


1.Y (Luma) - 亮度分量

  • 作用:表示图像的黑白(灰度)信息,即每个像素的明暗程度。
  • 类比:相当于将彩色照片去色后的灰度图,单独看Y分量时,是一张完整的黑白图片。
  • 示例:一张彩色照片中的白色区域Y值高,黑色区域Y值低。

2.U/V (Chroma) - 色差分量

  • 作用:表示颜色信息(色调和饱和度),通过两个色差信号(U和V)描述颜色与亮度的差异。
    • U (Cb):蓝色与亮度的差值(B - Y)。
    • V (Cr):红色与亮度的差值(R - Y)。
  • 类比:相当于给灰度图“上色”的指令。单独看U或V分量时,是模糊的色度图(类似低分辨率的彩色蒙版)。
  • 示例:红色区域V值高,蓝色区域U值高,绿色通过U和V的组合间接表示。

3.与RGB图片的对比

RGB图片 YUV文件
每个像素由R、G、B三个值表示 Y、U、V可能以不同采样率存储(如YUV420)
直接描述颜色 分离亮度和色度,便于压缩
文件较大 色度可下采样(如U/V分辨率减半),节省空间

4.实际存储示例(YUV420格式)

  • Y:每像素一个值(分辨率=原图)。
  • U/V:每2x2像素共享一个U和V值(分辨率=原图的1/4)。
  • 效果:人眼对亮度更敏感,这种压缩对视觉影响小。

5.总结

  • Y:黑白细节,决定清晰度。
  • U/V:颜色信息,决定色调。
  • 关系:YUV通过牺牲部分色度精度,用更小体积存储接近RGB的视觉内容。

注:以下是一个将 rgb 的图像格式转换为 yuv420 图像格式的 demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/*
* gcc make_yuv.c -o make_yuv
* ./make_yuv
* 生成文件: yellow_red_64x64.yuv
* @note 用于了解 yuv420p 文件格式,以及和 rgb 图像元素的对应关系
* @note: ffplay -f rawvideo -pixel_format yuv420p -video_size 64x64 yellow_red_64x64.yuv 可查看 yuv 图片内容
*/

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#define W 64
#define H 64
#define HALF_W (W/2)
#define HALF_H (H/2)

/* 标准 BT.601 转换系数 */
static inline uint8_t RGB_TO_Y(uint8_t r, uint8_t g, uint8_t b)
{
return (uint8_t)( ( 66 * r + 129 * g + 25 * b + 128) >> 8) + 16;
}
static inline uint8_t RGB_TO_U(uint8_t r, uint8_t g, uint8_t b)
{
return (uint8_t)( (-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;
}
static inline uint8_t RGB_TO_V(uint8_t r, uint8_t g, uint8_t b)
{
return (uint8_t)( (112 * r - 94 * g - 18 * b + 128) >> 8) + 128;
}

int main(void)
{
/* 申请 Y,U,V 三个平面 */
uint8_t *y = malloc(W * H);
uint8_t *u = malloc(HALF_W * HALF_H);
uint8_t *v = malloc(HALF_W * HALF_H);

/* 1. 填充 Y */
for (int row = 0; row < H; ++row) {
for (int col = 0; col < W; ++col) {
if (col < W/2) { /* 左侧黄色 */
y[row * W + col] = RGB_TO_Y(255, 255, 0);
} else { /* 右侧红色 */
y[row * W + col] = RGB_TO_Y(255, 0, 0);
}
}
}

/* 2. 填充 U/V(2×2 平均后下采样) */
for (int row = 0; row < HALF_H; ++row) {
for (int col = 0; col < HALF_W; ++col) {
int even_row = row * 2;
int even_col = col * 2;

/* 取 2×2 块左上角像素的颜色做近似 */
uint8_t r, g, b;
if (even_col < W/2) { /* 黄色 */
r = 255; g = 255; b = 0;
} else { /* 红色 */
r = 255; g = 0; b = 0;
}

u[row * HALF_W + col] = RGB_TO_U(r, g, b);
v[row * HALF_W + col] = RGB_TO_V(r, g, b);
}
}

/* 3. 写入文件 */
FILE *fp = fopen("yellow_red_64x64.yuv", "wb");
if (!fp) { perror("fopen"); return 1; }

fwrite(y, 1, W * H, fp);
fwrite(u, 1, HALF_W * HALF_H, fp);
fwrite(v, 1, HALF_W * HALF_H, fp);
fclose(fp);

free(y); free(u); free(v);
puts("yellow_red_64x64.yuv 已生成");
return 0;
}

附4:PulseAudio 中的 Sink 和 Source 详解

在 PulseAudio 音频系统中,sinksource 是两个核心概念,它们代表了音频流的两个不同方向:

1.Sink(接收器)

  • 定义:音频输出设备(播放设备)
  • 功能:接收音频流并将其播放出来
  • 对应设备
    • 扬声器(内置或外接)
    • 耳机
    • HDMI/DisplayPort 音频输出
    • 蓝牙音频设备
  • 命令
1
pacmd list-sinks  # 列出所有可用的音频输出设备
  • 示例输出
1
name: <alsa_output.pci-0000_00_1f.3.analog-stereo>

2.Source(源)

  • 定义:音频输入设备(录制设备)
  • 功能:捕获音频流并将其发送到系统
  • 对应设备
    • 麦克风(内置或外接)
    • 线路输入
    • 蓝牙麦克风
    • 系统音频捕获(监视器)
  • 命令
1
pacmd list-sources  # 列出所有可用的音频输入设备
  • 示例输出
1
name: <alsa_input.pci-0000_00_1f.3.analog-stereo>

3.特殊类型:监视器(Monitor)

在 PulseAudio 中,还有一个重要的概念是监视器(Monitor)

  • 定义:一种特殊的 source(输入设备)
  • 功能:捕获从 sink(输出设备)播放的音频
  • 命名规则<sink名称>.monitor
  • 用途
    • 录制系统声音(如音乐、通知音)
    • 创建虚拟音频设备
  • 示例
1
name: <alsa_output.pci-0000_00_1f.3.analog-stereo.monitor>

4.实际应用场景

1. 设置默认输出设备(sink)

1
pacmd set-default-sink alsa_output.pci-0000_00_1f.3.analog-stereo

2. 设置默认输入设备(source)

1
pacmd set-default-source alsa_input.pci-0000_00_1f.3.analog-stereo

3. 录制系统声音(使用监视器)

1
ffmpeg -f pulse -i alsa_output.pci-0000_00_1f.3.analog-stereo.monitor output.wav

4. 查看所有设备状态

1
2
3
4
5
# 完整输出设备列表
pacmd list-sinks

# 完整输入设备列表
pacmd list-sources

5.常见问题解决

如果您遇到 未指定有效的命令 错误,可能是以下原因:

  1. PulseAudio 未运行
1
pulseaudio --start
  1. 命令语法错误
1
2
3
# 正确命令
pactl get-default-sink
pactl get-default-source
  1. 权限问题
1
sudo usermod -aG audio $USER
  1. PulseAudio 配置问题
1
2
3
rm ~/.config/pulse/*
pulseaudio -k
pulseaudio --start

理解 sink 和 source 的概念对于在 Linux 系统上管理音频设备至关重要,特别是在开发音频应用程序或进行多媒体录制时。

附5:简单 demo

在学习 ffmpeg 的过程中,我尽可能将一些简单的功能抽取了单独的 demo。See:https://github.com/ssk-wh/ffmpeg-demo

 Comments
Comment plugin failed to load
Loading comment plugin