FFmpeg的libswscale模块提供图像缩放、图像格式转换功能。其中贯穿整个模块的是SwsContext结构体,方法包括sws_alloc_context分配、sws_init_context初始化、sws_getContext获取上下文、sws_get_cachedContext获取缓存,sws_freeContext释放上下文的方法。
SwsContext结构体位于libswscale模块的swscale_internal.h头文件,具体如下:
typedef struct SwsContext {
const AVClass *av_class;
SwsFunc swscale;
int srcW; // Width of source luma/alpha planes.
int srcH; // Height of source luma/alpha planes.
int dstH; // Height of destination luma/alpha planes.
int chrSrcW; // Width of source chroma planes.
int chrSrcH; // Height of source chroma planes.
int chrDstW; // Width of destination chroma planes.
int chrDstH; // Height of destination chroma planes.
// 级联上下文,可以把scaler分解为几步操作
struct SwsContext *cascaded_context[3];
int cascaded_tmpStride[4];
uint8_t *cascaded_tmp[4];
int cascaded1_tmpStride[4];
uint8_t *cascaded1_tmp[4];
int cascaded_mainindex;
double gamma_value;
int gamma_flag;
int is_internal_gamma;
uint16_t *gamma;
uint16_t *inv_gamma;
int numDesc;
int descIndex[2];
int numSlice;
struct SwsSlice *slice;
struct SwsFilterDescriptor *desc;
uint32_t pal_yuv[256];
uint32_t pal_rgb[256];
float uint2float_lut[256];
// 垂直缩放的环形缓冲区
int lastInLumBuf;
int lastInChrBuf;
uint8_t *formatConvBuffer;
int needAlpha;
// 水平/垂直滤波器
int16_t *hLumFilter;
int16_t *hChrFilter;
int16_t *vLumFilter;
int16_t *vChrFilter;
int32_t *hLumFilterPos;
int32_t *hChrFilterPos;
int32_t *vLumFilterPos;
int32_t *vChrFilterPos;
int hLumFilterSize;
int hChrFilterSize;
int vLumFilterSize;
int vChrFilterSize;
int canMMXEXTBeUsed;
int warned_unuseable_bilinear;
int dstY;
// 选择算法、优化、子采样的flag
int flags;
// 指向yuv->rgb表的起始位置
void *yuvTable;
// 表格包含C和SIMD指令
DECLARE_ALIGNED(16, int, table_gV)[256 + 2*YUVRGB_TABLE_HEADROOM];
uint8_t *table_rV[256 + 2*YUVRGB_TABLE_HEADROOM];
uint8_t *table_gU[256 + 2*YUVRGB_TABLE_HEADROOM];
uint8_t *table_bU[256 + 2*YUVRGB_TABLE_HEADROOM];
DECLARE_ALIGNED(16, int32_t, input_rgb2yuv_table)[16+40*4];
int *dither_error[4];
//颜色空间参数
int contrast, brightness, saturation;
int srcColorspaceTable[4];
int dstColorspaceTable[4];
int srcRange; // 0 = MPG YUV range, 1 = JPG YUV range
int dstRange; // 0 = MPG YUV range, 1 = JPG YUV range
const uint8_t *chrDither8, *lumDither8;
int use_mmx_vfilter;
int16_t *xyzgamma;
int16_t *rgbgamma;
int16_t *xyzgammainv;
int16_t *rgbgammainv;
int16_t xyz2rgb_matrix[3][4];
int16_t rgb2xyz_matrix[3][4];
//swscale()方法的函数指针
yuv2planar1_fn yuv2plane1;
yuv2planarX_fn yuv2planeX;
yuv2interleavedX_fn yuv2nv12cX;
yuv2packed1_fn yuv2packed1;
yuv2packed2_fn yuv2packed2;
yuv2packedX_fn yuv2packedX;
yuv2anyX_fn yuv2anyX;
void (*lumToYV12)(uint8_t *dst, const uint8_t *src, const uint8_t *src2,
const uint8_t *src3, int width, uint32_t *pal);
void (*alpToYV12)(uint8_t *dst, const uint8_t *src, const uint8_t *src2,
const uint8_t *src3, int width, uint32_t *pal);
void (*chrToYV12)(uint8_t *dstU, uint8_t *dstV,const uint8_t *src1,
const uint8_t *src2, const uint8_t *src3,
int width, uint32_t *pal);
// 读取输入plane,比如RGB
void (*readLumPlanar)(uint8_t *dst, const uint8_t *src[4], int width, int32_t *rgb2yuv);
void (*readChrPlanar)(uint8_t *dstU, uint8_t *dstV, const uint8_t *src[4],
int width, int32_t *rgb2yuv);
void (*readAlpPlanar)(uint8_t *dst, const uint8_t *src[4], int width, int32_t *rgb2yuv);
// 使用bilinear滤波器进行缩放
void (*hyscale_fast)(struct SwsContext *c,
int16_t *dst, int dstWidth,
const uint8_t *src, int srcW, int xInc);
void (*hcscale_fast)(struct SwsContext *c,
int16_t *dst1, int16_t *dst2, int dstWidth,
const uint8_t *src1, const uint8_t *src2,
int srcW, int xInc);
// 使用滤波器进行水平缩放
void (*hyScale)(struct SwsContext *c, int16_t *dst, int dstW,
const uint8_t *src, const int16_t *filter,
const int32_t *filterPos, int filterSize);
void (*hcScale)(struct SwsContext *c, int16_t *dst, int dstW,
const uint8_t *src, const int16_t *filter,
const int32_t *filterPos, int filterSize);
// luma平面的色彩空间转换
void (*lumConvertRange)(int16_t *dst, int width);
// chroma平面的色彩空间转换
void (*chrConvertRange)(int16_t *dst1, int16_t *dst2, int width);
int needs_hcscale;
SwsDither dither;
SwsAlphaBlend alphablend;
} SwsContext;
用于分配图像转换的上下文,首先是调用av_mallocz()分配结构体内存,然后赋值av_class,初始化默认options参数:
SwsContext *sws_alloc_context(void)
{
SwsContext *c = av_mallocz(sizeof(SwsContext));
av_assert0(offsetof(SwsContext, redDither) + DITHER32_INT == offsetof(SwsContext, dither32));
if (c) {
c->av_class = &ff_sws_context_class;
av_opt_set_defaults(c);
}
return c;
}
初始化图像转换的上下文,位于libswscale模块的utils.c。步骤包括:检查输入输出的像素格式是否支持;判断有没设置正确的滤波算法,如果没有就指定为SWS_BICUBIC算法;检查源图像宽高、目标图像宽高是否有效;获取图像转换函数;初始化滤波器。代码如下:
int sws_init_context(SwsContext *c, SwsFilter *srcFilter,
SwsFilter *dstFilter)
{
......
// 检查是否支持输入输出像素格式
if (!(unscaled && sws_isSupportedEndiannessConversion(srcFormat) &&
av_pix_fmt_swap_endianness(srcFormat) == dstFormat)) {
if (!sws_isSupportedInput(srcFormat)) {
return AVERROR(EINVAL);
}
if (!sws_isSupportedOutput(dstFormat)) {
return AVERROR(EINVAL);
}
}
// 判断flags有没设置为其中一种算法
i = flags & (SWS_POINT |
SWS_AREA |
SWS_BILINEAR |
SWS_FAST_BILINEAR |
SWS_BICUBIC |
SWS_X |
SWS_GAUSS |
SWS_LANCZOS |
SWS_SINC |
SWS_SPLINE |
SWS_BICUBLIN);
// 如果没有设置算法,提供默认的算法
if (!i) {
if (dstW < srcW && dstH < srcH)
flags |= SWS_BICUBIC;
else if (dstW > srcW && dstH > srcH)
flags |= SWS_BICUBIC;
else
flags |= SWS_BICUBIC;
c->flags = flags;
} else if (i & (i - 1)) {
return AVERROR(EINVAL);
}
// 检查参数是否有效
if (srcW < 1 || srcH < 1 || dstW < 1 || dstH < 1) {
return AVERROR(EINVAL);
}
if (flags & SWS_FAST_BILINEAR) {
if (srcW < 8 || dstW < 8) {
flags ^= SWS_FAST_BILINEAR | SWS_BILINEAR;
c->flags = flags;
}
}
......
/* unscaled special cases */
if (unscaled && !usesHFilter && !usesVFilter &&
(c->srcRange == c->dstRange || isAnyRGB(dstFormat) ||
isFloat(srcFormat) || isFloat(dstFormat))){
ff_get_unscaled_swscale(c);
if (c->swscale) {
return 0;
}
}
// 获取swscale函数
c->swscale = ff_getSwsFunc(c);
// 初始化滤波器
return ff_init_filters(c);
nomem:
ret = AVERROR(ENOMEM);
fail:
.....
return ret;
}
其中,ff_getSwsFunc()位于swscale.c,根据不同平台来初始化swscale:
SwsFunc ff_getSwsFunc(SwsContext *c)
{
sws_init_swscale(c);
if (ARCH_PPC)
ff_sws_init_swscale_ppc(c);
if (ARCH_X86)
ff_sws_init_swscale_x86(c);
if (ARCH_AARCH64)
ff_sws_init_swscale_aarch64(c);
if (ARCH_ARM)
ff_sws_init_swscale_arm(c);
return swscale;
}
ff_init_filters()负责对滤波器进行初始化,滤波器算法包括:均值滤波、双三次插值滤波、亮度双三次插值/色度双线性插值、双线性滤波、快速双线性滤波高斯滤波、正交相似变换滤波、最近邻滤波、正弦滤波、三次样本插值滤波、内部滤波。具体列表如下:
static const ScaleAlgorithm scale_algorithms[] = {
{ SWS_AREA, "area averaging", 1 },
{ SWS_BICUBIC, "bicubic", 4 },
{ SWS_BICUBLIN, "luma bicubic / chroma bilinear", -1 },
{ SWS_BILINEAR, "bilinear", 2 },
{ SWS_FAST_BILINEAR, "fast bilinear", -1 },
{ SWS_GAUSS, "Gaussian", 8 },
{ SWS_LANCZOS, "Lanczos", -1 },
{ SWS_POINT, "nearest neighbor / point", -1 },
{ SWS_SINC, "sinc", 20 },
{ SWS_SPLINE, "bicubic spline", 20 },
{ SWS_X, "experimental", 8 },
};
获取图像转换的上下文,首先设置相关参数,然后调用sws_init_context()来初始化上下文:
SwsContext *sws_getContext(int srcW, int srcH, enum AVPixelFormat srcFormat,
int dstW, int dstH, enum AVPixelFormat dstFormat,
int flags, SwsFilter *srcFilter,
SwsFilter *dstFilter, const double *param)
{
SwsContext *c;
c = sws_alloc_set_opts(srcW, srcH, srcFormat,
dstW, dstH, dstFormat,
flags, param);
if (!c)
return NULL;
if (sws_init_context(c, srcFilter, dstFilter) < 0) {
sws_freeContext(c);
return NULL;
}
return c;
}
获取缓存的图像转换上下文。首先校验参数是否一致,如果校验不通过就释放资源;然后判断上下文是否存在,如果存在直接复用,如不存在进行分配、初始化操作:
struct SwsContext *sws_getCachedContext(struct SwsContext *context, int srcW,
int srcH, enum AVPixelFormat srcFormat,
int dstW, int dstH,
enum AVPixelFormat dstFormat, int flags,
SwsFilter *srcFilter,
SwsFilter *dstFilter,
const double *param)
{
static const double default_param[2] = { SWS_PARAM_DEFAULT,
SWS_PARAM_DEFAULT };
int64_t src_h_chr_pos = -513, dst_h_chr_pos = -513,
src_v_chr_pos = -513, dst_v_chr_pos = -513;
if (!param)
param = default_param;
// 校验参数,如果不通过直接释放资源
if (context &&
(context->srcW != srcW ||
context->srcH != srcH ||
context->srcFormat != srcFormat ||
context->dstW != dstW ||
context->dstH != dstH ||
context->dstFormat != dstFormat ||
context->flags != flags ||
context->param[0] != param[0] ||
context->param[1] != param[1])) {
av_opt_get_int(context, "src_h_chr_pos", 0, &src_h_chr_pos);
av_opt_get_int(context, "src_v_chr_pos", 0, &src_v_chr_pos);
av_opt_get_int(context, "dst_h_chr_pos", 0, &dst_h_chr_pos);
av_opt_get_int(context, "dst_v_chr_pos", 0, &dst_v_chr_pos);
sws_freeContext(context);
context = NULL;
}
// 如果上下文不存在,进行分配、初始化操作
if (!context) {
if (!(context = sws_alloc_context()))
return NULL;
context->srcW = srcW;
context->srcH = srcH;
context->srcFormat = srcFormat;
context->dstW = dstW;
context->dstH = dstH;
context->dstFormat = dstFormat;
context->flags = flags;
context->param[0] = param[0];
context->param[1] = param[1];
av_opt_set_int(context, "src_h_chr_pos", src_h_chr_pos, 0);
av_opt_set_int(context, "src_v_chr_pos", src_v_chr_pos, 0);
av_opt_set_int(context, "dst_h_chr_pos", dst_h_chr_pos, 0);
av_opt_set_int(context, "dst_v_chr_pos", dst_v_chr_pos, 0);
if (sws_init_context(context, srcFilter, dstFilter) < 0) {
sws_freeContext(context);
return NULL;
}
}
return context;
}
用于释放图像转换上下文,各种释放操作:
void sws_freeContext(SwsContext *c)
{
int i;
if (!c)
return;
for (i = 0; i < 4; i++)
av_freep(&c->dither_error[i]);
av_freep(&c->vLumFilter);
av_freep(&c->vChrFilter);
av_freep(&c->hLumFilter);
av_freep(&c->hChrFilter);
av_freep(&c->vLumFilterPos);
av_freep(&c->vChrFilterPos);
av_freep(&c->hLumFilterPos);
av_freep(&c->hChrFilterPos);
av_freep(&c->yuvTable);
av_freep(&c->formatConvBuffer);
sws_freeContext(c->cascaded_context[0]);
sws_freeContext(c->cascaded_context[1]);
sws_freeContext(c->cascaded_context[2]);
memset(c->cascaded_context, 0, sizeof(c->cascaded_context));
av_freep(&c->cascaded_tmp[0]);
av_freep(&c->cascaded1_tmp[0]);
av_freep(&c->gamma);
av_freep(&c->inv_gamma);
ff_free_filters(c);
av_free(c);
}