FFmpeg源码分析:SwsContext图像转换上下文

FFmpeg的libswscale模块提供图像缩放、图像格式转换功能。其中贯穿整个模块的是SwsContext结构体,方法包括sws_alloc_context分配、sws_init_context初始化、sws_getContext获取上下文、sws_get_cachedContext获取缓存,sws_freeContext释放上下文的方法。

1、SwsContext

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;

2、sws_alloc_context

用于分配图像转换的上下文,首先是调用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;
}

3、sws_init_context

初始化图像转换的上下文,位于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 },
};

4、sws_getContext

获取图像转换的上下文,首先设置相关参数,然后调用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;
}

5、sws_getCachedContext

获取缓存的图像转换上下文。首先校验参数是否一致,如果校验不通过就释放资源;然后判断上下文是否存在,如果存在直接复用,如不存在进行分配、初始化操作:

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;
}

6、sws_freeContext

用于释放图像转换上下文,各种释放操作:

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);
}

你可能感兴趣的:(音视频开发,FFmpeg源码分析与实践,图像转换上下文)