4、FFmpeg入门:架构、命令、编译与视频流处理

说实话,做HLS摄像头采集方案,FFmpeg是绕不开的工具。我最早接触它是在一个安防项目里,客户要求把RTSP流转成HLS,那时候我还傻乎乎地想自己写协议栈……后来被同事一句话点醒:“用FFmpeg啊,一行命令搞定。”嗯,从那以后我就再也没离开过它。

4.1 FFmpeg架构:别被吓到,其实很简单

FFmpeg的架构,说白了就是“输入→处理→输出”的流水线。你想想看,摄像头采集视频,本质上就是把数据从一个地方搬到另一个地方,中间可能还要转个码、改个封装。

它的核心组件就这几个:

  • libavformat:负责封装和解封装。比如把H.264裸流塞进MP4盒子里,或者从RTSP流里把数据扒出来。我在项目中遇到过一个问题,某个摄像头输出的RTSP流格式不规范,就是靠libavformat里的自定义选项硬调过来的。
  • libavcodec:编解码器。H.264、H.265、AAC……所有你听过的音视频编码格式,它基本都支持。我个人习惯是优先用硬件编码器(比如NVIDIA的NVENC),能省不少CPU。
  • libavfilter:滤镜。缩放、裁剪、加水印、调色……这些操作都在这里完成。我记得有一次客户要求给视频流叠加时间戳,就是用filter搞定的。
  • libswscale:图像缩放和色彩空间转换。比如把YUV420P转成RGB,或者把1080p缩到720p。
  • libswresample:音频重采样。采样率、声道数、格式转换,都归它管。

核心逻辑图:FFmpeg处理视频流的流水线

输入源 (摄像头/文件/RTSP) 解封装 (libavformat) 解码 (libavcodec) 滤镜处理 (libavfilter) 输出 (HLS/文件/RTMP) 图:FFmpeg处理视频流的标准流水线

4.2 FFmpeg常用命令:够用就行

命令这东西,记太多没用。我平时常用的也就那么十几条,剩下的都是现查。下面这几个是做HLS方案必须掌握的:

4.2.1 查看信息

# 查看摄像头支持的格式(Linux下)
ffmpeg -f v4l2 -list_formats all -i /dev/video0

# 查看媒体文件信息
ffprobe -v quiet -print_format json -show_streams input.mp4

ffprobe是个好东西。我曾经排查过一个HLS切片不同步的问题,就是用ffprobe看两个视频流的PTS和DTS,发现其中一个的time_base设置错了。

4.2.2 采集与转码

# 从摄像头采集,直接输出HLS
ffmpeg -f v4l2 -i /dev/video0 -c:v libx264 -f hls -hls_time 4 -hls_list_size 10 output.m3u8

# 从RTSP流转HLS
ffmpeg -i rtsp://192.168.1.100:554/stream1 -c:v copy -c:a aac -f hls -hls_time 2 output.m3u8

我的经验:如果网络不稳定,RTSP转HLS时建议加个 -re 参数,按原始帧率读取输入,避免推流速度过快导致丢帧。

4.2.3 滤镜操作

# 缩放视频并叠加时间戳
ffmpeg -i input.mp4 -vf "scale=640:480,drawtext=text='%{localtime}':x=10:y=10:fontsize=24:fontcolor=white" output.mp4

嗯,这里要注意:drawtext滤镜需要编译时开启libfreetype支持,否则会报错。我第一次用的时候就踩了这个坑。

4.3 FFmpeg编译与安装:自己动手,丰衣足食

为什么我建议你自己编译?因为官方预编译包往往缺少某些功能。比如你要用硬件编码器、要支持某些私有协议,就得自己来。

4.3.1 编译前的准备

我个人习惯在Ubuntu上编译,依赖装起来方便:

sudo apt update
sudo apt install -y \
  autoconf automake build-essential cmake git \
  libass-dev libfreetype6-dev libsdl2-dev libtool \
  libva-dev libvdpau-dev libvorbis-dev libxcb1-dev \
  libxcb-shm0-dev libxcb-xfixes0-dev pkg-config \
  texinfo wget zlib1g-dev

4.3.2 编译FFmpeg

# 下载源码
wget https://ffmpeg.org/releases/ffmpeg-6.0.tar.bz2
tar -xjf ffmpeg-6.0.tar.bz2
cd ffmpeg-6.0

# 配置(开启常用功能)
./configure \
  --prefix=/usr/local \
  --enable-gpl \
  --enable-nonfree \
  --enable-libx264 \
  --enable-libx265 \
  --enable-libfdk-aac \
  --enable-libmp3lame \
  --enable-libvorbis \
  --enable-libfreetype \
  --enable-libass \
  --enable-ffplay \
  --enable-ffprobe

# 编译(-j后面的数字根据CPU核心数调整)
make -j4
sudo make install

注意:如果你要开启硬件编码支持(比如NVENC),需要额外安装NVIDIA的驱动和CUDA工具包,然后在configure时加上 --enable-cuda --enable-cuvid --enable-nvenc。我曾经因为驱动版本不匹配,折腾了一整天……

4.4 FFmpeg处理视频流:实战中的那些坑

理论说完了,咱们聊聊实战。做HLS摄像头采集,最常遇到的就是下面这几个问题:

4.4.1 时间戳问题

摄像头输出的原始流往往没有正确的时间戳。你想想看,如果PTS乱跳,HLS切片器怎么知道从哪里切?

# 强制设置输入帧率为25fps,并生成时间戳
ffmpeg -f v4l2 -framerate 25 -i /dev/video0 -vsync 1 -r 25 -c:v libx264 -f hls output.m3u8

-vsync 1 这个参数,说白了就是让FFmpeg帮你把时间戳对齐到整数帧边界。我遇到过一些摄像头,输出的帧率标称30fps,实际只有29.97,不加这个参数时间戳就会慢慢漂移。

4.4.2 编码参数调优

做HLS直播,编码参数直接影响延迟和画质。我个人习惯这样配:

参数 推荐值 说明
-preset ultrafast / veryfast 直播场景用ultrafast,延迟最低;录制场景用medium
-crf 23 画质与码率的平衡点,数值越小画质越好
-tune zerolatency 零延迟模式,直播必备
-g 帧率×2 GOP大小,影响HLS切片对齐
# 一个完整的直播推流命令
ffmpeg -f v4l2 -framerate 30 -i /dev/video0 \
  -c:v libx264 -preset ultrafast -tune zerolatency -crf 23 -g 60 \
  -c:a aac -b:a 128k \
  -f hls -hls_time 2 -hls_list_size 5 -hls_flags delete_segments \
  output.m3u8

避坑指南:我曾经在某个项目里把 -g 设成了帧率×10,结果HLS切片器切出来的ts文件时长忽长忽短,播放器频繁缓冲。后来改成帧率×2,问题就解决了。GOP大小最好能被切片时长整除,这样每个切片都从关键帧开始。

4.4.3 音频同步问题

很多USB摄像头不带音频,或者音频采样率不标准。我的做法是:

# 从ALSA设备采集音频,重采样到44.1kHz
ffmpeg -f v4l2 -i /dev/video0 \
  -f alsa -i hw:0,0 -ar 44100 -ac 2 \
  -c:v libx264 -c:a aac -b:a 128k \
  -f hls output.m3u8

如果音频和视频不同步,试试 -af "aresample=async=1",这个滤镜会自动调整音频采样点来匹配视频的时间戳。

好了,FFmpeg入门这块就聊这么多。记住一句话:FFmpeg是个工具箱,不是魔法棒。遇到问题多查文档、多看日志,慢慢就熟练了。