libyuv是一个关于yuv格式图像的处理库.包括rgb和yuv格式的转换,图像的缩放,叠加混合等功能.与ffmpeg的libswscale库的功能类似.
libswscale库支持的格式更加广泛,但使用起来需要创建一个上下文结构体,再进行接口调用, 并且没有叠加功能,需要ffmpeg的libavfilter库支持, libavfilter库的使用则更加繁琐.
在色彩空间转换和图像缩放方面,libyuv的使用相比ffmpeg库更加方便直观,色彩空间转换,缩放叠加处理,接口都很直观,没有上下文结构,使用方便.
两者的效率,我实测是差不多的,都是纯cpu处理,都有汇编优化.
实际使用中的一个问题
libyuv提供了一个方便的整合接口:
|
|
这个接口是将任何libyuv支持的图像格式转换为I420格式的图像.
实际使用中我要将一个ffmpeg的AVFrame做颜色空间转换,这个AVFrame是BGR格式的,内存分配时是32位对齐的.
转换接口这样填写:
|
|
当src_frame
的长宽是1920x1080
时,程序工作得很好,没有任何问题,但是一旦src_frame
换了一个分辨率时,比如766x360
这种,转换出来
的图像就变成了花屏.
接口参数review
看到这个问题,很自然的想到应该是图像数据在内存中分配对齐导致的问题,应该是接口调用时某些参数传入有误,需要重新审视一下libyuv::ConvertToI420
这个接口的参数.
它的前两个参数sample
和sample_size
是指向源数据的内存的,如果是一个766x360
这样分辨率的图像,并且分配AVFrame
时使用了32位对齐方式,那么实际内存布局应该是每行3 * ((766-1+32)/32)
, 看接口第一第二个参数好像没什么问题
dst_frame
的六个参数分别是yuv分量的起始指针和长度容量,也没有问题.
crop_x
和crop_y
当然应该填0.
src_frame
的宽高也没有问题,crop_width
和crop_height
也没有问题,我需要整幅图像.
那么问题出在哪呢,再回头细想一下关于内存对齐,内存对齐是ffmpeg在分配AVFrame时处理的,libyuv并不知道接口传过来的内存布局是怎样对齐的,从这个实例来看,libyuv处理转换获取了源的sample
和sample_size
,然后根据src_width
和src_height
去读取真正的源像素,libyuv读取了一行像素后,也就是766*3
个字节之后,它是不知道要跳过多少对齐字节去读取下一行像素点的.那么它是怎么处理的呢?
使用这组参数转换宽为32倍数的图像是没有问题的,例如1920x1080
的,因为这种图像每一行像素点占用的内存是32的倍数,刚好对齐,不用分配额外的内存,从这里推断,libyuv::ConvertToI420
读取下一行像素应该是不进行跳跃,直接接着前一行读取.
正确的参数
这样想来,问题就是出在这个地方了,那么怎么解决这个问题呢,这个接口并没有给出一个类似linesize
或者src_stride
的参数,
而使用单独的转换接口libyuv::RGB24ToI420
是有这样的参数的:src_stride_rgb24
,那么只能使用特定格式的转换接口吗?
当然不是,再仔细看一下它的参数列表,可以这样使用
|
|
将src_width
参数填写为src_frame->linesize[0]/3
就可以了,这样填写表示我需要处理一个宽为src_frame->linesize[0]/3
的图像,
而不是宽为766的图像,然后通过crop_width
参数,真正转换的图像的宽依然是766
.
好了,至此这个接口的使用问题就解决了.