ffmpeg命令分析-print - 弦外之音

/ 0评 / 0

本系列 以 ffmpeg4.2 源码为准,下载地址:链接:百度网盘 提取码:g3k8

本系列主要分析各种 ffmpeg 命令参数 在代码里是如何实现的。a.mp4下载链接:百度网盘,提取码:nl0s 。


命令如下:ffmpeg -i a.mp4 -vcodec libx264 -preset placebo output.mp4

本文不讲上面的命令行参数实现,而是打算讲解,ffmpeg 转码过程中输出的 frame ,fps,q,size, bitrate ,speed 的意义,如下图。

最重要的是speed,转码速度

从ffmpeg.c 里可以看出,上面的红色圈出来的是 print_report() 函数打印的,下面就来分析 print_report() 的实现,流程图如下:

print_report() 入口会做一个时间判断,每 隔0.5 秒才打印一次日志,代码如下:

if (!is_last_report) {
    if (last_time == -1) {
        last_time = cur_time;
        return;
    }
    //50万微妙,0.5秒统计一次。
    if ((cur_time - last_time) < 500000) //注意这里
        return;
    last_time = cur_time;
}

后续的代码没有什么好分析,逻辑比较简单,自行查看即可。

frame= 160 fps= 37 q=28.0 size= 0kB time=00:00:07.23 bitrate= 0.1kbits/s speed=1.68x

打印日志的参数解析如下:

1,frame ,用的是 ost->frame_number 赋值,代表当前编码到视频流的第几帧。

2,fps,用上面的 frame 除以时间得到,代表一秒编码了多少个视频帧。

3,q,编码质量,也是针对视频的。q 的计算方式如下:

q = ost->quality / (float) FF_QP2LAMBDA;

可以看到,q 是 从ost->quality 来的,那 ost->quality 的值是在哪里赋值的呢?代码如下:

ffmpeg.c 742行
uint8_t *sd = av_packet_get_side_data(pkt, AV_PKT_DATA_QUALITY_STATS,NULL);
ost->quality = sd ? AV_RL32(sd) : -1;

可以看到,流的质量quality,应该是一个累计的值。我个人猜测编码器会根据以往编码的Frame 不断计算这个值,然后放进去 AVPacket 的side_data 里面。

可以 av_packet_get_side_data() 获取到 side_data,side_data 的前32位就是 quality,AV_PKT_DATA_QUALITY_STATS 这个side_data的定义如下。

 libavcodec\avcodec.h 1266行
 /**
     * This side data contains quality related information from the encoder.
     * @code
     * u32le quality factor of the compressed frame. Allowed range is between 1 (good) and FF_LAMBDA_MAX (bad).
     * u8    picture type
     * u8    error count
     * u16   reserved
     * u64le[error count] sum of squared differences between encoder in and output
     * @endcode
     */
 AV_PKT_DATA_QUALITY_STATS,


前面 3个参数都是针对 视频流 的分析

4,size,写入文件的数据大小,通过 avio_size(oc->pb) 获得。

5,bitrate,比特率,这个bitrate 的单位是 kbits/s ,不是 kb/s ,要注意。 kbits/s 是指每秒传输多少千比特,一个字节有8个比特。

所以源码里 bitrate 的计算方式如下:

ffmpeg.c 1769行
bitrate = pts && total_size >= 0 ? total_size * 8 / (pts / 1000.0) : -1;

total_size 是avio_size() 返回的,单位是字节,所以乘以 8,pts 的单位是微妙,除以1000后等于 千分之一秒。千分之一秒能传输 ( total_size * 8) 大小的比特。那1秒就能够传输 ( total_size * 8) * 1000 大小的比特。但是输出的单位不是比特,而是千比特,kbits,前面有个k,所以这个乘以1000就可以抵消掉。就形成源码的计算方式。

6,speed,编码速度。用输出流的最后一个AVPacket的pts 除以程序运行时间。哪个流的最后AVPacket的pts最大就以哪个流的pts为准,输出流最后一个AVPacket的pts通过函数 av_stream_get_end_pts() 获得。

这样就比较好理解了,如果现在已经编码到视频流的第10秒的数据帧,但是转码程序只运行了2秒,那speed转码速度就是 10 /2 ,speed等于5.

ffmpeg.c 1770行
speed = t != 0.0 ? (double)pts / AV_TIME_BASE / t : -1;

print_report 里面 的qp_hist 跟 AV_CODEC_FLAG_PSNR 本次不讲解,因为我也不知道是做什么的


扩展知识:

print_report 里面有个非常有用的函数,avio_size(oc->pb), 可以很方便得获取 ffmpeg 已经往文件写了多少数据进去。


版权所属:知识星球:弦外之音,QQ:2338195090。 由于笔者的水平有限, 加之编写的同时还要参与开发工作,文中难免会出现一些错误或者不准确的地方,恳请读者批评指正。如果读者有任何宝贵意见,可以加我微信 Loken1。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注