FFmpeg source code analysis: SwsContext image conversion context

FFmpeg's libswscale module provides image scaling and image format conversion functions. The SwsContext structure runs through the entire module. The methods include sws_alloc_context allocation, sws_init_context initialization, sws_getContext to obtain context, sws_get_cachedContext to obtain cache, and sws_freeContext to release context.

1、SwsContext

The SwsContext structure is located in the swscale_internal.h header file of the libswscale module, as follows:

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

The context for allocating image conversion is to first call av_mallocz() to allocate structure memory, then assign av_class, and initialize the default options parameters:

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

Initialize the context for image transformation, located in utils.c of the libswscale module. The steps include: checking whether the pixel format of the input and output is supported; judging whether the correct filtering algorithm is set, and if not, specifying the SWS_BICUBIC algorithm; checking whether the width and height of the source image and the width and height of the target image are valid; obtaining the image conversion function; initializing the filter . code show as below:

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

Among them, ff_getSwsFunc() is located in swscale.c, and swscale is initialized according to different platforms:

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() is responsible for initializing the filter. The filter algorithms include: mean filtering, bicubic interpolation filtering, luminance bicubic interpolation/chroma bilinear interpolation, bilinear filtering, fast bilinear filtering Gaussian filtering, quadrature Similarity transform filtering, nearest neighbor filtering, sinusoidal filtering, cubic sample interpolation filtering, internal filtering. The specific list is as follows:

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

To get the context of image conversion, first set the relevant parameters, and then call sws_init_context() to initialize the 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

Get the cached image transform context. First check whether the parameters are consistent, if the check fails, release the resources; then judge whether the context exists, if there is direct reuse, if not, perform allocation and initialization operations:

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

Used to release the image transition context, various release operations:

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

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324448253&siteId=291194637