ffmpeg源码简析(十)libswscale中的SwsContext,sws_scale()

libswscale是一个主要用于处理图片像素数据的类库。可以完成图片像素格式的转换,图片的拉伸等工作。 
libswscale常用的函数数量很少,一般情况下就3个:

sws_getContext():初始化一个SwsContext。

sws_scale():处理图像数据。
sws_freeContext():释放一个SwsContext。

其中sws_getContext()也可以用sws_getCachedContext()取代。

尽管libswscale从表面上看常用函数的个数不多,它的内部却有一个大大的“世界”。做为一个几乎“万能”的图片像素数据处理类库,它的内部包含了大量的代码。

ibswscale处理数据有两条最主要的方式:unscaled和scaled。unscaled用于处理不需要拉伸的像素数据(属于比较特殊的情况),scaled用于处理需要拉伸的像素数据。Unscaled只需要对图像像素格式进行转换;而Scaled则除了对像素格式进行转换之外,还需要对图像进行缩放。Scaled方式可以分成以下几个步骤:

XXX to YUV Converter:首相将数据像素数据转换为8bitYUV格式;
Horizontal scaler:水平拉伸图像,并且转换为15bitYUV;
Vertical scaler:垂直拉伸图像;
Output converter:转换为输出像素格式。

SwsContext 
SwsContext是使用libswscale时候一个贯穿始终的结构体。但是我们在使用FFmpeg的类库进行开发的时候,是无法看到它的内部结构的。在libswscale\swscale.h中只能看到一行定义.它的定义位于libswscale\swscale_internal.h中

struct SwsContext; 
  • 1

sws_getContext()

sws_getContext()是初始化SwsContext的函数。sws_getContext()的声明位于libswscale\swscale.h,如下所示。

struct 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); 
  • 1
  • 2
  • 3
  • 4

该函数包含以下参数:

srcW:源图像的宽
srcH:源图像的高
srcFormat:源图像的像素格式
dstW:目标图像的宽
dstH:目标图像的高
dstFormat:目标图像的像素格式
flags:设定图像拉伸使用的算法

成功执行的话返回生成的SwsContext,否则返回NULL。 
sws_getContext()的定义位于libswscale\utils.c,如下所示。

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;  

    if (!(c = sws_alloc_context()))  
        return NULL;  

    c->flags     = flags;  
    c->srcW      = srcW;  
    c->srcH      = srcH;  
    c->dstW      = dstW;  
    c->dstH      = dstH;  
    c->srcFormat = srcFormat;  
    c->dstFormat = dstFormat;  

    if (param) {  
        c->param[0] = param[0];  
        c->param[1] = param[1];  
    }  

    if (sws_init_context(c, srcFilter, dstFilter) < 0) {  
        sws_freeContext(c);  
        return NULL;  
    }  

    return c;  
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

从sws_getContext()的定义中可以看出,它首先调用了一个函数sws_alloc_context()用于给SwsContext分配内存。然后将传入的源图像,目标图像的宽高,像素格式,以及标志位分别赋值给该SwsContext相应的字段。最后调用一个函数sws_init_context()完成初始化工作。

sws_init_context()除了对SwsContext中的各种变量进行赋值之外,主要按照顺序完成了以下一些工作:

1.  通过sws_rgb2rgb_init()初始化RGB转RGB(或者YUV转YUV)的函数(注意不包含RGB与YUV相互转换的函数)。
2.  通过判断输入输出图像的宽高来判断图像是否需要拉伸。如果图像需要拉伸,那么unscaled变量会被标记为1。
3.  通过sws_setColorspaceDetails()初始化颜色空间。
4.  一些输入参数的检测。例如:如果没有设置图像拉伸方法的话,默认设置为SWS_BICUBIC;如果输入和输出图像的宽高小于等于0的话,也会返回错误信息。
5.  初始化Filter。这一步根据拉伸方法的不同,初始化不同的Filter。
6.  如果flags中设置了“打印信息”选项SWS_PRINT_INFO,则输出信息。
7.  如果不需要拉伸的话,调用ff_get_unscaled_swscale()将特定的像素转换函数的指针赋值给SwsContext中的swscale指针。
8.  如果需要拉伸的话,调用ff_getSwsFunc()将通用的swscale()赋值给SwsContext中的swscale指针(这个地方有点绕,但是确实是这样的)。

sws_scale()

sws_scale()是用于转换像素的函数。它的声明位于libswscale\swscale.h,如下所示。

int sws_scale(struct SwsContext *c, const uint8_t *const srcSlice[],  
              const int srcStride[], int srcSliceY, int srcSliceH,  
              uint8_t *const dst[], const int dstStride[]);
  • 1
  • 2
  • 3

sws_scale()的定义位于libswscale\swscale.c,如下所示。

    /** 
     * swscale wrapper, so we don't need to export the SwsContext. 
     * Assumes planar YUV to be in YUV order instead of YVU. 
     */  
    int sws_scale(struct SwsContext *c,  
                                      const uint8_t * const srcSlice[],  
                                      const int srcStride[], int srcSliceY,  
                                      int srcSliceH, uint8_t *const dst[],  
                                      const int dstStride[])  
    {  
        int i, ret;  
        const uint8_t *src2[4];  
        uint8_t *dst2[4];  
        uint8_t *rgb0_tmp = NULL;  
        //检查输入参数  
        if (!srcStride || !dstStride || !dst || !srcSlice) {  
            av_log(c, AV_LOG_ERROR, "One of the input parameters to sws_scale() is NULL, please check the calling code\n");  
            return 0;  
        }  
        if (c->cascaded_context[0] && srcSliceY == 0 && srcSliceH == c->cascaded_context[0]->srcH) {  
            ret = sws_scale(c->cascaded_context[0],  
                            srcSlice, srcStride, srcSliceY, srcSliceH,  
                            c->cascaded_tmp, c->cascaded_tmpStride);  
            if (ret < 0)  
                return ret;  
            ret = sws_scale(c->cascaded_context[1],  
                            (const uint8_t * const * )c->cascaded_tmp, c->cascaded_tmpStride, 0, c->cascaded_context[0]->dstH,  
                            dst, dstStride);  
            return ret;  
        }  

        memcpy(src2, srcSlice, sizeof(src2));  
        memcpy(dst2, dst, sizeof(dst2));  

        // do not mess up sliceDir if we have a "trailing" 0-size slice  
        if (srcSliceH == 0)  
            return 0;  
        //检查  
        if (!check_image_pointers(srcSlice, c->srcFormat, srcStride)) {  
            av_log(c, AV_LOG_ERROR, "bad src image pointers\n");  
            return 0;  
        }  
        if (!check_image_pointers((const uint8_t* const*)dst, c->dstFormat, dstStride)) {  
            av_log(c, AV_LOG_ERROR, "bad dst image pointers\n");  
            return 0;  
        }  

        if (c->sliceDir == 0 && srcSliceY != 0 && srcSliceY + srcSliceH != c->srcH) {  
            av_log(c, AV_LOG_ERROR, "Slices start in the middle!\n");  
            return 0;  
        }  
        if (c->sliceDir == 0) {  
            if (srcSliceY == 0) c->sliceDir = 1; else c->sliceDir = -1;  
        }  
        //使用调色板palette的特殊处理?应该不常见  
        if (usePal(c->srcFormat)) {  
            for (i = 0; i < 256; i++) {  
                int r, g, b, y, u, v, a = 0xff;  
                if (c->srcFormat == AV_PIX_FMT_PAL8) {  
                    uint32_t p = ((const uint32_t *)(srcSlice[1]))[i];  
                    a = (p >> 24) & 0xFF;  
                    r = (p >> 16) & 0xFF;  
                    g = (p >>  8) & 0xFF;  
                    b =  p        & 0xFF;  
                } else if (c->srcFormat == AV_PIX_FMT_RGB8) {  
                    r = ( i >> 5     ) * 36;  
                    g = ((i >> 2) & 7) * 36;  
                    b = ( i       & 3) * 85;  
                } else if (c->srcFormat == AV_PIX_FMT_BGR8) {  
                    b = ( i >> 6     ) * 85;  
                    g = ((i >> 3) & 7) * 36;  
                    r = ( i       & 7) * 36;  
                } else if (c->srcFormat == AV_PIX_FMT_RGB4_BYTE) {  
                    r = ( i >> 3     ) * 255;  
                    g = ((i >> 1) & 3) * 85;  
                    b = ( i       & 1) * 255;  
                } else if (c->srcFormat == AV_PIX_FMT_GRAY8 || c->srcFormat == AV_PIX_FMT_GRAY8A) {  
                    r = g = b = i;  
                } else {  
                    av_assert1(c->srcFormat == AV_PIX_FMT_BGR4_BYTE);  
                    b = ( i >> 3     ) * 255;  
                    g = ((i >> 1) & 3) * 85;  
                    r = ( i       & 1) * 255;  
                }  
    #define RGB2YUV_SHIFT 15  
    #define BY ( (int) (0.114 * 219 / 255 * (1 << RGB2YUV_SHIFT) + 0.5))  
    #define BV (-(int) (0.081 * 224 / 255 * (1 << RGB2YUV_SHIFT) + 0.5))  
    #define BU ( (int) (0.500 * 224 / 255 * (1 << RGB2YUV_SHIFT) + 0.5))  
    #define GY ( (int) (0.587 * 219 / 255 * (1 << RGB2YUV_SHIFT) + 0.5))  
    #define GV (-(int) (0.419 * 224 / 255 * (1 << RGB2YUV_SHIFT) + 0.5))  
    #define GU (-(int) (0.331 * 224 / 255 * (1 << RGB2YUV_SHIFT) + 0.5))  
    #define RY ( (int) (0.299 * 219 / 255 * (1 << RGB2YUV_SHIFT) + 0.5))  
    #define RV ( (int) (0.500 * 224 / 255 * (1 << RGB2YUV_SHIFT) + 0.5))  
    #define RU (-(int) (0.169 * 224 / 255 * (1 << RGB2YUV_SHIFT) + 0.5))  

                y = av_clip_uint8((RY * r + GY * g + BY * b + ( 33 << (RGB2YUV_SHIFT - 1))) >> RGB2YUV_SHIFT);  
                u = av_clip_uint8((RU * r + GU * g + BU * b + (257 << (RGB2YUV_SHIFT - 1))) >> RGB2YUV_SHIFT);  
                v = av_clip_uint8((RV * r + GV * g + BV * b + (257 << (RGB2YUV_SHIFT - 1))) >> RGB2YUV_SHIFT);  
                c->pal_yuv[i]= y + (u<<8) + (v<<16) + ((unsigned)a<<24);  

                switch (c->dstFormat) {  
                case AV_PIX_FMT_BGR32:  
    #if !HAVE_BIGENDIAN  
                case AV_PIX_FMT_RGB24:  
    #endif  
                    c->pal_rgb[i]=  r + (g<<8) + (b<<16) + ((unsigned)a<<24);  
                    break;  
                case AV_PIX_FMT_BGR32_1:  
    #if HAVE_BIGENDIAN  
                case AV_PIX_FMT_BGR24:  
    #endif  
                    c->pal_rgb[i]= a + (r<<8) + (g<<16) + ((unsigned)b<<24);  
                    break;  
                case AV_PIX_FMT_RGB32_1:  
    #if HAVE_BIGENDIAN  
                case AV_PIX_FMT_RGB24:  
    #endif  
                    c->pal_rgb[i]= a + (b<<8) + (g<<16) + ((unsigned)r<<24);  
                    break;  
                case AV_PIX_FMT_RGB32:  
    #if !HAVE_BIGENDIAN  
                case AV_PIX_FMT_BGR24:  
    #endif  
                default:  
                    c->pal_rgb[i]=  b + (g<<8) + (r<<16) + ((unsigned)a<<24);  
                }  
            }  
        }  
        //Alpha的特殊处理?  
        if (c->src0Alpha && !c->dst0Alpha && isALPHA(c->dstFormat)) {  
            uint8_t *base;  
            int x,y;  
            rgb0_tmp = av_malloc(FFABS(srcStride[0]) * srcSliceH + 32);  
            if (!rgb0_tmp)  
                return AVERROR(ENOMEM);  

            base = srcStride[0] < 0 ? rgb0_tmp - srcStride[0] * (srcSliceH-1) : rgb0_tmp;  
            for (y=0; y<srcSliceH; y++){  
                memcpy(base + srcStride[0]*y, src2[0] + srcStride[0]*y, 4*c->srcW);  
                for (x=c->src0Alpha-1; x<4*c->srcW; x+=4) {  
                    base[ srcStride[0]*y + x] = 0xFF;  
                }  
            }  
            src2[0] = base;  
        }  
        //XYZ的特殊处理?  
        if (c->srcXYZ && !(c->dstXYZ && c->srcW==c->dstW && c->srcH==c->dstH)) {  
            uint8_t *base;  
            rgb0_tmp = av_malloc(FFABS(srcStride[0]) * srcSliceH + 32);  
            if (!rgb0_tmp)  
                return AVERROR(ENOMEM);  

            base = srcStride[0] < 0 ? rgb0_tmp - srcStride[0] * (srcSliceH-1) : rgb0_tmp;  

            xyz12Torgb48(c, (uint16_t*)base, (const uint16_t*)src2[0], srcStride[0]/2, srcSliceH);  
            src2[0] = base;  
        }  

        if (!srcSliceY && (c->flags & SWS_BITEXACT) && c->dither == SWS_DITHER_ED && c->dither_error[0])  
            for (i = 0; i < 4; i++)  
                memset(c->dither_error[i], 0, sizeof(c->dither_error[0][0]) * (c->dstW+2));  


        // copy strides, so they can safely be modified  
        // sliceDir: 1 = top-to-bottom; -1 = bottom-to-top;  
        if (c->sliceDir == 1) {  
            // slices go from top to bottom  
            int srcStride2[4] = { srcStride[0], srcStride[1], srcStride[2],  
                                  srcStride[3] };  
            int dstStride2[4] = { dstStride[0], dstStride[1], dstStride[2],  
                                  dstStride[3] };  

            reset_ptr(src2, c->srcFormat);  
            reset_ptr((void*)dst2, c->dstFormat);  

            /* reset slice direction at end of frame */  
            if (srcSliceY + srcSliceH == c->srcH)  
                c->sliceDir = 0;  
            //关键:调用  
            ret = c->swscale(c, src2, srcStride2, srcSliceY, srcSliceH, dst2,  
                              dstStride2);  
        } else {  
            // slices go from bottom to top => we flip the image internally  
            int srcStride2[4] = { -srcStride[0], -srcStride[1], -srcStride[2],  
                                  -srcStride[3] };  
            int dstStride2[4] = { -dstStride[0], -dstStride[1], -dstStride[2],  
                                  -dstStride[3] };  

            src2[0] += (srcSliceH - 1) * srcStride[0];  
            if (!usePal(c->srcFormat))  
                src2[1] += ((srcSliceH >> c->chrSrcVSubSample) - 1) * srcStride[1];  
            src2[2] += ((srcSliceH >> c->chrSrcVSubSample) - 1) * srcStride[2];  
            src2[3] += (srcSliceH - 1) * srcStride[3];  
            dst2[0] += ( c->dstH                         - 1) * dstStride[0];  
            dst2[1] += ((c->dstH >> c->chrDstVSubSample) - 1) * dstStride[1];  
            dst2[2] += ((c->dstH >> c->chrDstVSubSample) - 1) * dstStride[2];  
            dst2[3] += ( c->dstH                         - 1) * dstStride[3];  

            reset_ptr(src2, c->srcFormat);  
            reset_ptr((void*)dst2, c->dstFormat);  

            /* reset slice direction at end of frame */  
            if (!srcSliceY)  
                c->sliceDir = 0;  
            //关键:调用  
            ret = c->swscale(c, src2, srcStride2, c->srcH-srcSliceY-srcSliceH,  
                              srcSliceH, dst2, dstStride2);  
        }  


        if (c->dstXYZ && !(c->srcXYZ && c->srcW==c->dstW && c->srcH==c->dstH)) {  
            /* replace on the same data */  
            rgb48Toxyz12(c, (uint16_t*)dst2[0], (const uint16_t*)dst2[0], dstStride[0]/2, ret);  
        }  

        av_free(rgb0_tmp);  
        return ret;  
    }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218

从sws_scale()的定义可以看出,它封装了SwsContext中的swscale()(注意这个函数中间没有“_”)。函数最重要的一句代码就是“c->swscale()”。除此之外,函数还做了一些增加“兼容性”的一些处理。

函数的主要步骤如下所示。 
1.检查输入的图像参数的合理性。 
2.如果输入像素数据中使用了“调色板”(palette),则进行一些相应的处理。这一步通过函数usePal()来判定。 
3.其它一些特殊格式的处理,比如说Alpha,XYZ等的处理(这方面没有研究过)。 
4.如果输入的图像的扫描方式是从底部到顶部的(一般情况下是从顶部到底部),则将图像进行反转。
5.调用SwsContext中的swscale()。

SwsContext中的swscale()

swscale这个变量的类型是SwsFunc,实际上就是一个函数指针。它是整个类库的核心。当我们从外部调用swscale()函数的时候,实际上就是调用了SwsContext中的这个名称为swscale的变量(注意外部函数接口和这个内部函数指针的名字是一样的,但不是一回事)。 
可以看一下SwsFunc这个类型的定义:

typedef int (*SwsFunc)(struct SwsContext *context, const uint8_t *src[],  
                       int srcStride[], int srcSliceY, int srcSliceH,  
                       uint8_t *dst[], int dstStride[]); 
  • 1
  • 2
  • 3

可以看出SwsFunc的定义的参数类型和libswscale类库外部接口函数swscale()的参数类型一模一样。 
在libswscale中,该指针的指向可以分成2种情况:

1.图像没有伸缩的时候,指向专有的像素转换函数

2.图像有伸缩的时候,指向swscale()函数。

在调用sws_getContext()初始化SwsContext的时候,会在其子函数sws_init_context()中对swscale指针进行赋值。如果图像没有进行拉伸,则会调用ff_get_unscaled_swscale()对其进行赋值;如果图像进行了拉伸,则会调用ff_getSwsFunc()对其进行赋值。下面分别看一下这2种情况。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值