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

来源:互联网 发布:知安安全 编辑:程序博客网 时间:2024/06/07 05:48

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; 

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

该函数包含以下参数:

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

从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[]);

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

从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[]); 

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

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

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

1 0
原创粉丝点击