注意:本指南默认您已掌握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 | 
| 音视频自动对齐‼ | 先用 ffprobe读start_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 - 容器指挥官
| 12
 
 | AVFormatContext* fmt_ctx = NULL;avformat_alloc_output_context2(&fmt_ctx, NULL, "mp4", "output.mp4");
 
 | 
职责:
- 管理文件/流的封装信息
- 关键字段: 
- nb_streams:流数量
- pb:I/O上下文指针
 
类比:快递分拣中心,管理所有包裹(音视频流)
2. AVCodecContext - 编解码大脑
| 12
 3
 4
 
 | AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_H264);AVCodecContext* codec_ctx = avcodec_alloc_context3(codec);
 codec_ctx->bit_rate = 4000000;
 codec_ctx->framerate = (AVRational){30, 1};
 
 | 
| 参数 | 视频示例 | 音频示例 | 
| 分辨率 | 1920x1080 | - | 
| 采样率 | - | 44100 Hz | 
| 像素格式 | YUV420P | - | 
| 声道布局 | - | AV_CH_LAYOUT_STEREO | 
避坑指南:未设置帧率会导致视频加速播放
3. AVPacket - 压缩数据包
| 12
 3
 4
 5
 
 | AVPacket* pkt = av_packet_alloc();while (av_read_frame(fmt_ctx, pkt) >= 0) {
 
 av_packet_unref(pkt);
 }
 
 | 
特点:
4. AVFrame - 原始数据帧
| 12
 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.编解码关键流程
| 12
 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.实战场景
| 12
 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)
| 12
 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)
| 12
 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);
 
 
 
 | 
3. 音视频同步(核心!)
| 12
 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.音频录制+重采样实战
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 
 | avformat_open_input(&audio_fmt_ctx, "default", av_find_input_format("pulse"), NULL);
 
 
 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;
 
 
 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);
 
 
 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. 性能优化三连击
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | ctx->hw_device_ctx = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_CUDA);
 
 
 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.必备诊断命令
| 12
 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时间戳不连续
 解决方案:
| 12
 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 | 
注意事项
- Linux (PulseAudio):
- 需要安装libpulse-dev
- 设备名称为default或特定设备名称
 
- Windows (DirectShow):
- 修改设备名称为audio=麦克风名称
- 可能需要先列出可用设备
 
- macOS (AVFoundation):
- 修改设备名称为:0或特定设备索引
- 输入格式应为avfoundation
 
七、终极Q&A
1. 为什么摄像头录制中同时需要解码器和编码器
- 摄像头输出是压缩数据(如MJPEG)
- 目标格式需要重新编码(如H.264)
- 转码流水线:MJPEG -> YUV -> H.264
2.AVPacket 和 AVFrame 之间的关系
- 解码过程:
- 从多媒体文件中读取 AVPacket。
- 使用解码器将 AVPacket 中的压缩数据解码到 AVFrame 中。
- 处理(例如,显示或进一步处理)AVFrame 中的未压缩数据。
 
- 编码过程:
- 从原始数据(例如,摄像头捕获的视频帧或麦克风捕获的音频样本)创建 AVFrame。
- 使用编码器将 AVFrame 中的未压缩数据编码到 AVPacket 中。
- 将 AVPacket 写入到多媒体文件中。
 
- 转换过程:
- 读取 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.录制音频时怎么查找系统的默认音频设备
| 12
 3
 
 | $ pactl list sources short0	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.视频播放速度是怎么控制的
播放行为本质上是:
- 以PTS为绝对时间基准
- 以预设帧率为初始化参考
- 通过动态同步算法实时调整
八、结语:通往音视频大师之路
当你:
- 能30分钟写出录制程序 → 青铜
- 搞定音视频同步 → 白银
- 实现4K60帧编码 → 黄金
- 修复花屏/卡顿问题 → 王者
- 给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 -codecs 和 ffmpeg -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)结构为例:
- 解码顺序:I₀ → P₃ → B₁ → B₂ → P₆ → B₄ → B₅…
- 显示顺序: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:
| 12
 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
 
 | 
 
 
 
 
 
 
 #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)
 
 
 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)
 {
 
 uint8_t *y = malloc(W * H);
 uint8_t *u = malloc(HALF_W * HALF_H);
 uint8_t *v = malloc(HALF_W * HALF_H);
 
 
 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);
 }
 }
 }
 
 
 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;
 
 
 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);
 }
 }
 
 
 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 音频系统中,sink 和 source 是两个核心概念,它们代表了音频流的两个不同方向:
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. 查看所有设备状态
| 12
 3
 4
 5
 
 | # 完整输出设备列表pacmd list-sinks
 
 # 完整输入设备列表
 pacmd list-sources
 
 | 
5.常见问题解决
如果您遇到 未指定有效的命令 错误,可能是以下原因:
- PulseAudio 未运行:
- 命令语法错误:
| 12
 3
 
 | # 正确命令pactl get-default-sink
 pactl get-default-source
 
 | 
- 权限问题:
| 1
 | sudo usermod -aG audio $USER
 | 
- PulseAudio 配置问题:
| 12
 3
 
 | rm ~/.config/pulse/*pulseaudio -k
 pulseaudio --start
 
 | 
理解 sink 和 source 的概念对于在 Linux 系统上管理音频设备至关重要,特别是在开发音频应用程序或进行多媒体录制时。
附5:简单 demo
在学习 ffmpeg 的过程中,我尽可能将一些简单的功能抽取了单独的 demo。See:https://github.com/ssk-wh/ffmpeg-demo