对比 libswscale 和 libyuv

一、为什么做测试?

FFmpeg libswscale和libyuv是最常用的两个做图像缩放、格式转换的开源库。做视频图像处理经常会用到它们,但很少有人对它们做过详细的比较。我想尝试回答以下几个问题:

1. libyuv比FFmpeg libswscale快几倍?

2. libyuv什么情况下比libswscale快,什么情况下优势不明显,或者更慢?

3. libswscale慢在哪里?如何提速?

4. libyuv的劣势是什么?

有了具体的测试数据,我们就能回答,某个场景适合用libyuv还是libswscale。

需要提前声明,因时间精力有限,我只能挑选最有代表性的场景做少量的测试,数据难免有偏颇。如果读者有更多场景数据信息,欢迎补充。

二、测试环境

拿来做测试的硬件设备是macbook pro M1款。之所以选择ARM架构CPU做测试

  1. 因为在ARM架构上有libyuv vs libswscale的技术选型问题,x86特别是服务端,往往沿用FFmpeg全家桶同属ARM架构的大量
  2. ARM架构的CPU,一定程度上可以用来推测在Android/iOS移动端的表现

x86平台也简单运行了一下,和libswscale ARM优化后的数据基本一致。

三、测试结果

1. RGB转YUV:测试4096×4096分辨率RGB转同分辨率YUV

  • libyuv:耗时5 ms
  • libswscale:耗时65 ms

libyuv的速度是libswscale的11倍

2. RGB下采样:测试4096×4096分辨率RGB转2048×2048分辨率RGB

  • libyuv:耗时11 ms
  • libswscale:耗时66 ms

libswscale依旧很慢。

libyuv RGB下采样耗时有点久,因为libyuv先把RGB转成BGRA,再对RGB做下采样,最后把BGRA转回RGB。如果直接用BGRA做下采样,速度会更快。

3. RGB转YUV并下采样:测试4096×4096分辨率RGB转2048×2048分辨率YUV

  • libyuv:耗时12 ms
  • libswscale:耗时47 ms

libyuv的耗时增加了,因为libyuv缩放和格式转换是分开的操作,需要用户控制。我先做下采样,用RGBScale把分辨率缩小一半,再对2048×2048分辨率图像做格式转换。后一步操作libyuv耗时只有1 ms左右,前一步下采样占了11 ms。

由此可见,libyuv做了优化的地方,速度非常快。而像RGB缩放先转BGRA的操作,属于糊弄事。关于缩放,后面还有更详细解读。

libswscale的速度变快了,因为libswscale格式转换和缩放是合并到一起的操作,主要有三个操作,对应源码的libswscale/input.c, libswscale/hscale.c, libswscale/vscale.c。这里非常晦涩,暂时不展开来讲了。

4. 多线程加速:测试4096×4096分辨率RGB转2048×2048分辨率YUV

  • libyuv不支持多线程加速,耗时不变(12 ms)
  • libswscale 4线程:耗时14 ms

libswscale终于靠堆算力,达到了和libyuv接近的性能。继续堆算力,libswscale的速度可以超过libyuv,比如8线程耗时9 ms多。线程过多收益递减。在服务器上,算力充足而libswscale单线程处理成为瓶颈时,别忘记多线程加速的杀手锏。

四、详细解读与libswscale提速

libswscale比libyuv慢,首先是libswscale缺少ARM架构的汇编优化。与之相反,libyuv在ARM上的汇编优化覆盖全面。

插个题外话,libyuv用的内联汇编的方式,我在x86平台遇到了GCC把内敛汇编给优化掉了,导致程序空跑没有对数据做处理的情况。clang编译没问题。大概Google只在意clang。

我最近给libswscale补了一点aarch64的优化,见https://github.com/FFmpeg/FFmpeg/tree/master/libswscale/aarch64 。优化前后的耗时对比。

RGB转YUVRGB转YUV + 下采样多线程
优化前65 ms47 ms14 ms
优化后29 ms17 ms5 m 

可见速度提高了一倍以上。

但是,简单的RGB转YUV的速度,libswscale还是远远落后于libyuv。libyuv的操作,基本上就是你能想到的最简单的操作。libswscale就是魔法了,下面的方法是libswscale RGB转YUV的其中一个步骤。


static void hScale16To15_c(SwsContext *c, int16_t *dst, int dstW,
                           const uint8_t *_src, const int16_t *filter,
                           const int32_t *filterPos, int filterSize)
{
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(c->srcFormat);
    int i;
    const uint16_t *src = (const uint16_t *) _src;
    int sh              = desc->comp[0].depth - 1;

    if (sh<15) {
        sh = isAnyRGB(c->srcFormat) || c->srcFormat==AV_PIX_FMT_PAL8 ? 13 : (desc->comp[0].depth - 1);
    } else if (desc->flags & AV_PIX_FMT_FLAG_FLOAT) { /* float input are process like uint 16bpc */
        sh = 16 - 1;
    }

    for (i = 0; i < dstW; i++) {
        int j;
        int srcPos = filterPos[i];
        int val    = 0;

        for (j = 0; j < filterSize; j++) {
            val += src[srcPos + j] * filter[filterSize * i + j];
        }
        // filter=14 bit, input=16 bit, output=30 bit, >> 15 makes 15 bit
        dst[i] = FFMIN(val >> sh, (1 << 15) - 1);
    }
}

注意这里的filter本身并不是用来做RGB转YUV的。如果有下采样操作,这个filter才真正起作用,没有下采样就空算一遍。循环体内部不是顺序直接访问内存,要索引来索引去,导致汇编优化效率变低,不好批量顺序的加载和计算

最终结果是,libswscale复杂设计的副作用,导致在简单场景的性能无论如何追不上libyuv(多线程加速除外)。如果是下采样加格式转换这样的操作,libswscale设计上的overhead会降低下来。libswscale甚至允许你塞个自定义的kernel进去,属实超前设计了,牺牲的是性能。

libswscale提速,最简单粗暴的是加线程,其次是增加汇编优化,最难的是架构上的优化设计。最难的这一步,社区在推进中了。

五、libyuv的劣势

在缩放算法上,libyuv支持的缩放模式很少:

typedef enum FilterMode {
  kFilterNone = 0,      // Point sample; Fastest.
  kFilterLinear = 1,    // Filter horizontally only.
  kFilterBilinear = 2,  // Faster than box, but lower quality scaling down.
  kFilterBox = 3        // Highest quality.
} FilterModeEnum;

libswscale支持的就多了。对比性能的时候,注意要用同样的算法来对比。

在计算方式上,libyuv精度差。libswscale设计上,对于8 bit的格式,用15 bit来保存中间的计算结果(计算本身可能是16 bit或32 bit的)。其他高精度格式用更高精度来保存中间结果。15 bit是保证不会整型溢出的情况下的最高精度(这里又是libswscale的黑魔法)。

看下简单RGB转YUV的PSNR对比:

RGBavg
libyuv37.5841.9834.5037.04
libswscale37.3141.9835.4537.49

测试图片:

对比 libswscale 和 libyuv

六、结论

  • 复杂操作,两者性能差异较小,越简单的操作,性能差异越大
  • 算力充足、特别在意图像质量时,选择libswscale
  • 算力敏感的情况下,选择libyuv

作者:quink
来源:Fun With FFmpeg
原文:https://mp.weixin.qq.com/s/tAJvoTUh0VlP4jfBbp1G6w

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(0)

相关推荐

发表回复

登录后才能评论