经常需要做测试以及功能验证,每次都要找个视频文件或者线上拉一路流推流。在某些测试场景下,视频文件和线上的流都存在一定的码率波动,对于实际的测试数据有一定的影响。所以需要一路码率、帧率、GOP等参数可调且尽可能标准的测试流,并且使用方式要尽可能的简答,适用大多数的场景。

一般机器都安装FFmpeg,所以打算用FFmpeg实现测试流的生成。查阅资料发现FFmpeglavfi自带简单的测试视频,利用ffmpeg本身的一堆编码参数便可以得到想要的标准测试流。

FFmpeg安装

FFmpeg的安装,参考官网的指南足矣。其他的安装指南写得再好,还是没有官方的好,而且附带中文版,集成了几乎平时用到的所有lib。

CentOS安装FFmpeg指南

我在一台VPS上安装X265库的时候出了个问题:

X265 make error

我通过以下操作解决:

卸载yum原本的yasm

yum remove yasm

将按照官方指南中安装的高版本的yasm链接到PATH

ln -s /root/bin/yasm/usr/bin/yasm

安装完毕

FFmpeg

测试流生成

视频

利用lavfi生成测试视频,查到以下说明,按图索骥即可生成测试图像。

http://www.bogotobogo.com/FFMpeg/ffmpeg_video_test_patterns_src.php

此处我采用的参数如下,使用testsrc这个图像,很有小时候周二下午的回忆。

-f lavfi -i "testsrc=size=1920x1080:rate=30:duration=5"

参数含义:

参数 含义
testsrc 使用testsrc作为测试画面
size 分辨率
rate 帧率
duration 持续时间

音频

测试音频采用sine正弦波。可条件频率,采样率等参数。具体参考官网说明

https://lists.ffmpeg.org/pipermail/ffmpeg-cvslog/2013-March/062382.html

此处我采用的参数是:

-f lavfi -i "sine=frequency=440:sample_rate=44100:beep_factor=4:duration=5"

参数含义:

参数 含义
sine 使用正弦波信号作为输入
frequency 正弦波频率
sample_rate 音频采样率
beep_factor 每秒产生一个蜂鸣,蜂鸣频率为正弦波频率的四倍
duration 持续时间

合成测试流

将以上视频和音频作为FFmpeg的输入,通过ffmpeg自带的一些编码参数和码率控制参数输出。

ffmpeg -f lavfi -i testsrc=duration=5:size=1920x1080:rate=300 \
	-f lavfi -i sine=frequency=440:sample_rate=44100:beep_factor=4:duration=5 \
	-acodec libfdk_aac \
	-vcodec libx264  -bf 0 -b:a 100k  -b:v 1900k -bufsize 1000k \
	-r 30 -g 30 -sc_threshold 0 \
	-profile baseline -pix_fmt yuv420p \
	-f flv test.flv

输出画面如下:

test stream

观察生成的test.flv,发生其码率并不是指定的音频100kbps,视频1900kbps,而是只有730kbps。

# ffmpeg -i test.flv 
...
Input #0, flv, from 'test.flv':
  Metadata:
    encoder         : Lavf58.3.100
  Duration: 00:00:05.08, start: 0.000000, bitrate: 730 kb/s
    Stream #0:0: Video: h264 (Constrained Baseline), yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], 1900 kb/s, 30 fps, 30 tbr, 1k tbn, 60 tbc
    Stream #0:1: Audio: aac (LC), 44100 Hz, mono, fltp, 100 kb/s

无论如何修改指定的码率的值,输出码率总是明显小于指定值。

查阅资料后,使用x264的参数,启用CBR模式,解决问题,码率稳定在2000kbps左右:

ffmpeg -f lavfi -i testsrc=duration=5:size=1920x1080:rate=30 \
	-f lavfi -i sine=frequency=440:sample_rate=44100:beep_factor=4:duration=5 \
	-acodec libfdk_aac -b:a 100k \
	-vcodec libx264 -x264opts bitrate=1900:vbv-maxrate=1900:vbv-bufsize=1000:nal_hrd=cbr \
	-r 30 -g 30 -sc_threshold 0 \
	-profile baseline -pix_fmt yuv420p \
	-f flv test.flv

以上FFmpeg参数含义:

参数 含义
-acodec libfdk_aac 指定音频编码为aac格式
-b:a 100k 指定音频编码输出码率为100kbps
-vcodec libx264 指定视频编码为h264格式
-x264opts 启用x264参数
-x264opts bitrate=1900 x264参数:指定视频输出码率为1900kbps
-x264opts vbv-maxrate=1900 x264参数:指定视频输出的最高码率为1900kbps
-x264opts vbv-bufsize=1000 x264参数:指定视频编码的buffer为1000kb
-x264opts nal_hrd=cbr x264参数:设置HRD为CBR,严格按照指定码率转码
-r 30 指定输出帧率为30fps
-g 30 指定输出视频GOP为30帧
-sc_threshold 0 视频编码关闭 scenecut模式,用以不再自动生成I帧
-profile baseline 指定视频编码规则为baseline
-pix_fmt yuv420p 指定输出数据格式为yuv420pbaseline只支持这种
-f flv 指定输出封装为flv格式
# ffmpeg -i test1.flv 
...
Input #0, flv, from 'test1.flv':
  Metadata:
    encoder         : Lavf58.3.100
  Duration: 00:00:05.05, start: 0.000000, bitrate: 1975 kb/s
    Stream #0:0: Video: h264 (Constrained Baseline), yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], 1900 kb/s, 30 fps, 30 tbr, 1k tbn, 60 tbc
    Stream #0:1: Audio: aac (LC), 44100 Hz, mono, fltp, 100 kb/s
At least one output file must be specified
2000kbps stream

此处观察FFmpeg打印的日志发现,两次转码只在x264的参数上差了一个指标,如下两图,下图指定了nal_hrd=cbr

nal_hrd=none
nal_hrd=cbr

那么这个nal_hrd=cbr又是什么鬼呢?

HRD?没搞明白

上述两者的唯一区别便是nal_hrd是否有设置成CBR,CBR(Constant Bitrate, 常数比特率 ) ABR(Average Bitrate)
VBR(Variable Bitrate)只是x264按下不表。并没有查到相关资料,没有搞明白这些跟HRD有啥关系。

假想码流调度器(hypothetical reference decoder, HRD),wiki上说在264标准中,HRD已经被VBV代替了,也就是设置的vbv-maxratevbv-bufsize两个参数。不清楚为什么还需要设置HRD为CBR模式。

关于VBV中vbv-maxratevbv-bufsize两个参数的关系,这个文章中有所解释:http://www.bretl.com/mpeghtml/VBV.HTM

实际使用

ffmpeg -re -f lavfi -i testsrc=duration=5:size=1920x1080:rate=30 \
	-re -f lavfi -i sine=frequency=440:sample_rate=44100:beep_factor=4:duration=5 \
	-acodec libfdk_aac -b:a 100k \
	-vcodec libx264 -x264opts bitrate=1900:vbv-maxrate=1900:vbv-bufsize=1000:nal_hrd=cbr \
	-r 30 -g 30 -sc_threshold 0 \
	-profile baseline -pix_fmt yuv420p \
	-f flv rtmp://127.0.0.1:1935/xujk.test.com/live/stream