FFmpeg在libswscale模块提供图像缩放与图像转换功能,比如1080P图像缩放为720P,或者YUV422P转换为YUV420P。图像缩放函数有个SwsContext结构体作为上下文,上一篇文章有介绍:SwsContext图像转换上下文。

一、像素格式

我们先看下常见的像素格式,位于libavutil/pixfmt.h,存储于AVPixelFormat枚举类型中:

enum AVPixelFormat {AV_PIX_FMT_NONE = -1,AV_PIX_FMT_YUV420P,   ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)AV_PIX_FMT_YUYV422,   ///< packed YUV 4:2:2, 16bpp, Y0 Cb Y1 CrAV_PIX_FMT_RGB24,     ///< packed RGB 8:8:8, 24bpp, RGBRGB...AV_PIX_FMT_BGR24,     ///< packed RGB 8:8:8, 24bpp, BGRBGR...AV_PIX_FMT_YUV422P,   ///< planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)AV_PIX_FMT_YUV444P,   ///< planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)AV_PIX_FMT_YUV410P,   ///< planar YUV 4:1:0,  9bpp, (1 Cr & Cb sample per 4x4 Y samples)AV_PIX_FMT_YUV411P,   ///< planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples)AV_PIX_FMT_GRAY8,     ///<        Y        ,  8bppAV_PIX_FMT_MONOWHITE, ///<        Y        ,  1bpp, 0 is white, 1 is blackAV_PIX_FMT_MONOBLACK, ///<        Y        ,  1bpp, 0 is black, 1 is whiteAV_PIX_FMT_PAL8,      ///< 8 bits with AV_PIX_FMT_RGB32 paletteAV_PIX_FMT_YUVJ420P,  ///< planar YUV 4:2:0, 12bpp, full scale (JPEG)AV_PIX_FMT_YUVJ422P,  ///< planar YUV 4:2:2, 16bpp, full scale (JPEG)AV_PIX_FMT_YUVJ444P,  ///< planar YUV 4:4:4, 24bpp, full scale (JPEG)AV_PIX_FMT_NV12,      ///< planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UVAV_PIX_FMT_NV21,      ///< as above, but U and V bytes are swappedAV_PIX_FMT_ARGB,      ///< packed ARGB 8:8:8:8, 32bpp, ARGBARGB...AV_PIX_FMT_RGBA,      ///< packed RGBA 8:8:8:8, 32bpp, RGBARGBA...AV_PIX_FMT_ABGR,      ///< packed ABGR 8:8:8:8, 32bpp, ABGRABGR...AV_PIX_FMT_BGRA,      ///< packed BGRA 8:8:8:8, 32bpp, BGRABGRA...AV_PIX_FMT_RGB565BE,  ///< packed RGB 5:6:5, 16bpp, big-endianAV_PIX_FMT_RGB565LE,  ///< packed RGB 5:6:5, 16bpp, little-endianAV_PIX_FMT_RGB555BE,  ///< packed RGB 5:5:5, 16bpp, big-endianAV_PIX_FMT_RGB555LE,  ///< packed RGB 5:5:5, 16bpp, little-endianAV_PIX_FMT_YUV420P10BE,///< planar YUV 4:2:0, 15bpp, big-endianAV_PIX_FMT_YUV420P10LE,///< planar YUV 4:2:0, 15bpp, little-endianAV_PIX_FMT_YUV422P10BE,///< planar YUV 4:2:2, 20bpp, big-endianAV_PIX_FMT_YUV422P10LE,///< planar YUV 4:2:2, 20bpp, little-endianAV_PIX_FMT_YUV444P10BE,///< planar YUV 4:4:4, 30bpp, big-endianAV_PIX_FMT_YUV444P10LE,///< planar YUV 4:4:4, 30bpp, little-endianAV_PIX_FMT_YUV420P12BE, ///< planar YUV 4:2:0,18bpp, big-endianAV_PIX_FMT_YUV420P12LE, ///< planar YUV 4:2:0,18bpp, little-endianAV_PIX_FMT_YUV422P12BE, ///< planar YUV 4:2:2,24bpp, big-endianAV_PIX_FMT_YUV422P12LE, ///< planar YUV 4:2:2,24bpp, little-endianAV_PIX_FMT_YUV444P12BE, ///< planar YUV 4:4:4,36bpp, big-endianAV_PIX_FMT_YUV444P12LE, ///< planar YUV 4:4:4,36bpp, little-endianAV_PIX_FMT_VIDEOTOOLBOX,///< hardware decoding through VideotoolboxAV_PIX_FMT_MEDIACODEC,  ///< hardware decoding through MediaCodecAV_PIX_FMT_NB         ///< number of pixel formats
};

如上面代码所示,像素格式主要分为两大系列:YUV和RGB。其中,YUV系列有YUV420P、YUV422P、YUV444P,RGB系列有RGBA、RGB、BGR、GRAY。再细分,YUV420P子系列包括YUV420P10LE、YUV420P10BE、YUV420P12LE、YUV420P12BE等,RGB子系列包括RGB24、RGB565LE、RGB565BE、RGB555LE、RGB555BE等。

以YUV420P10LE为例,对各部分参数进行分析。420P代表4:2:0 planar,一个UV对应4个Y。10代表10bpp,其中bpp为bits per pixel每个像素点占多少位。LE代表Little Endian,小端存储,与之相反的是BE大端存储。

另外,NV12和NV21一般为Android平台存储格式。NV21与YUV420SP类似,都是4:2:0 Semi Planar,区别是YUV420SP为1个UV对应4个Y,NV21为1个VU对应4个Y。

二、sws_scale图像转换

调用sws_scale()函数进行图像转换:

int attribute_align_arg sws_scale(struct SwsContext *c,const uint8_t * const srcSlice[],const int srcStride[], int srcSliceY,int srcSliceH, uint8_t *const dst[],const int dstStride[])
{/******************* 参数初始化 ***********************/// 级联context的处理if (c->gamma_flag && c->cascaded_context[0]) {// 调用内部图像转换函数ret = sws_scale(c->cascaded_context[0],srcSlice, srcStride, srcSliceY, srcSliceH,c->cascaded_tmp, c->cascaded_tmpStride);if (ret < 0)return ret;if (c->cascaded_context[2]) {ret = sws_scale(c->cascaded_context[1], (const uint8_t * const *)c->cascaded_tmp, c->cascaded_tmpStride, srcSliceY, srcSliceH, c->cascaded1_tmp, c->cascaded1_tmpStride);} else {ret = sws_scale(c->cascaded_context[1], (const uint8_t * const *)c->cascaded_tmp, c->cascaded_tmpStride, srcSliceY, srcSliceH, dst, dstStride);}if (ret < 0)return ret;if (c->cascaded_context[2]) {ret = sws_scale(c->cascaded_context[2], (const uint8_t * const *)c->cascaded1_tmp, c->cascaded1_tmpStride, c->cascaded_context[1]->dstY - ret, c->cascaded_context[1]->dstY, dst, dstStride);}return ret;}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));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;}......// 调用内部图像转换函数ret = c->swscale(c, src2, srcStride2, srcSliceY_internal, srcSliceH, dst2, dstStride2);......return ret;
}

调用内部的sws_scale()进行真正的图像转换:

static int swscale(SwsContext *c, const uint8_t *src[],int srcStride[], int srcSliceY,int srcSliceH, uint8_t *dst[], int dstStride[])
{/******************* 参数初始化 ***********************/if (dstStride[0]&15 || dstStride[1]&15 ||dstStride[2]&15 || dstStride[3]&15) {static int warnedAlready = 0;// dstStride没有内存对齐,不能进行对齐访问if (flags & SWS_PRINT_INFO && !warnedAlready) {warnedAlready = 1;}}if (   (uintptr_t)dst[0]&15 || (uintptr_t)dst[1]&15 || (uintptr_t)dst[2]&15|| (uintptr_t)src[0]&15 || (uintptr_t)src[1]&15 || (uintptr_t)src[2]&15|| dstStride[0]&15 || dstStride[1]&15 || dstStride[2]&15 || dstStride[3]&15|| srcStride[0]&15 || srcStride[1]&15 || srcStride[2]&15 || srcStride[3]&15) {static int warnedAlready=0;int cpu_flags = av_get_cpu_flags();//数据没有对齐,可能导致cpu访问速率降低if (HAVE_MMXEXT && (cpu_flags & AV_CPU_FLAG_SSE2) && !warnedAlready){warnedAlready=1;}}// 分别初始化垂直缩放函数、源slice函数、目标slice函数ff_init_vscale_pfn(c, yuv2plane1, yuv2planeX, yuv2nv12cX,yuv2packed1, yuv2packed2, yuv2packedX, yuv2anyX, c->use_mmx_vfilter);ff_init_slice_from_src(src_slice, (uint8_t**)src, srcStride, c->srcW,srcSliceY, srcSliceH, chrSrcSliceY, chrSrcSliceH, 1);ff_init_slice_from_src(vout_slice, (uint8_t**)dst, dstStride, c->dstW,dstY, dstH, dstY >> c->chrDstVSubSample,AV_CEIL_RSHIFT(dstH, c->chrDstVSubSample), 0);for (; dstY < dstH; dstY++) {// 旋转sliceff_rotate_slice(hout_slice, lastPosY, lastCPosY);if (posY < lastLumSrcY + 1) {for (i = lumStart; i < lumEnd; ++i)desc[i].process(c, &desc[i], firstPosY, lastPosY - firstPosY + 1);}lastInLumBuf = lastLumSrcY;if (cPosY < lastChrSrcY + 1) {for (i = chrStart; i < chrEnd; ++i)desc[i].process(c, &desc[i], firstCPosY, lastCPosY - firstCPosY + 1);}lastInChrBuf = lastChrSrcY;if (!enough_lines)break;if (should_dither) {c->chrDither8 = ff_dither_8x8_128[chrDstY & 7];c->lumDither8 = ff_dither_8x8_128[dstY    & 7];}if (dstY >= dstH - 2) {// 初始化输出函数ff_sws_init_output_funcs(c, &yuv2plane1, &yuv2planeX, &yuv2nv12cX,&yuv2packed1, &yuv2packed2, &yuv2packedX, &yuv2anyX);use_mmx_vfilter= 0;ff_init_vscale_pfn(c, yuv2plane1, yuv2planeX, yuv2nv12cX,yuv2packed1, yuv2packed2, yuv2packedX, yuv2anyX, use_mmx_vfilter);}{for (i = vStart; i < vEnd; ++i)desc[i].process(c, &desc[i], dstY, 1);}}if (isPlanar(dstFormat) && isALPHA(dstFormat) && !needAlpha) {int length = dstW;int height = dstY - lastDstY;if (is16BPS(dstFormat) || isNBPS(dstFormat)) {const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(dstFormat);fillPlane16(dst[3], dstStride[3], length, height, lastDstY,1, desc->comp[3].depth,isBE(dstFormat));} else if (is32BPS(dstFormat)) {const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(dstFormat);fillPlane32(dst[3], dstStride[3], length, height, lastDstY,1, desc->comp[3].depth,isBE(dstFormat), desc->flags & AV_PIX_FMT_FLAG_FLOAT);} elsefillPlane(dst[3], dstStride[3], length, height, lastDstY, 255);}c->dstY         = dstY;c->lastInLumBuf = lastInLumBuf;c->lastInChrBuf = lastInChrBuf;return dstY - lastDstY;
}

三、图像工具类

图像相关判断工具类位于libswscale/swscale_internal.h,包括判断是否为16位深度、是否为YUV、是否为平面YUV、是否为RGB、是否为任意RGB。
1、判断16位深度
判断是否为16位深度,通过比较depth参数:

static av_always_inline int is16BPS(enum AVPixelFormat pix_fmt)
{const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);av_assert0(desc);return desc->comp[0].depth == 16;
}

2、判断YUV
判断是否为YUV格式,读取flag为非RGB且nb_components参数大于等于2:

static av_always_inline int isYUV(enum AVPixelFormat pix_fmt)
{const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);av_assert0(desc);return !(desc->flags & AV_PIX_FMT_FLAG_RGB) && desc->nb_components >= 2;
}

3、判断平面YUV
判断是否为平面YUV格式,读取flag为PLANAR且为YUV格式:

static av_always_inline int isPlanarYUV(enum AVPixelFormat pix_fmt)
{const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);av_assert0(desc);return ((desc->flags & AV_PIX_FMT_FLAG_PLANAR) && isYUV(pix_fmt));
}

4、判断RGB
判断是否为RGB格式,读取flag为RGB:

static av_always_inline int isRGB(enum AVPixelFormat pix_fmt)
{const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);av_assert0(desc);return (desc->flags & AV_PIX_FMT_FLAG_RGB);
}

5、判断任意RGB
判断是否为任意RGB格式,读取flag为RGB,或pix_fmt为MONOBLACK,或pix_fmt为MONOWHITE:

static av_always_inline int isAnyRGB(enum AVPixelFormat pix_fmt)
{const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);av_assert0(desc);return (desc->flags & AV_PIX_FMT_FLAG_RGB) ||pix_fmt == AV_PIX_FMT_MONOBLACK || pix_fmt == AV_PIX_FMT_MONOWHITE;
}

四、图像转换实例

根据指定输出像素格式进行转换。先判断SwsContext是否为空,如果为空从缓存获取;然后调用sws_scale()进行图像转换:

int convertPixelFormat(AVCodecContext *context, AVFrame *frame) {int outputLineSize[4];int outputFormat = frame->format;av_image_fill_linesizes(outputLineSize, (enum AVPixelFormat)(outputFormat), frame->width);if (!context->opaque) {// 获取缓存contextstruct SwsContext *cacheContext = sws_getCachedContext(NULL, frame->width, frame->height, (enum AVPixelFormat) frame->format,frame->width, frame->height, (enum AVPixelFormat) outputFormat,SWS_BICUBIC, NULL, NULL, NULL);context->opaque = cacheContext;}struct SwsContext *swsContext = context->opaque;uint8_t *dst_data[4];av_image_fill_pointers(dst_data,(enum AVPixelFormat) outputFormat,frame->height,(uint8_t *) data,outputLineSize);// 像素格式转换sws_scale(swsContext, (const uint8_t **) frame->data, frame->linesize, 0,frame->height, dst_data, outputLineSize);
}

FFmpeg源码分析:sws_scale图像缩放与图像转换相关推荐

  1. FFMPEG源码分析(二)

    ffmpeg源码分析之数据流 本文主要介绍ffmpeg的数据流,在ffmpeg中主要分有三个主要用途用于媒体流的解码播放,媒体流的转换(解码之后再编码)和媒体流录制. 媒体流的解码播放 在ffmpeg ...

  2. FFMPEG 源码分析

    FFMPEG基本概念: ffmpeg是一个开源的编解码框架,它提供了一个音视频录制,解码和编码库.FFMPEG是在linux下开发的,但也有windows下的编译版本. ffmpeg项目由以下几部分组 ...

  3. FFmpeg源码分析-直播延迟-内存泄漏

    FFmpeg源码分析-直播延迟-内存泄漏|FFmpeg源码分析方法|ffmpeg播放为什么容易产生延迟|解复用.解码内存泄漏分析 专注后台服务器开发,包括C/C++,Linux,Nginx,ZeroM ...

  4. ffmpeg源码分析-parse_optgroup

    本系列 以 ffmpeg4.2 源码为准,下载地址:链接:百度网盘 提取码:g3k8 ffmpeg 源码分析系列以一条简单的命令开始,ffmpeg -i a.mp4 b.flv,分析其内部逻辑. a. ...

  5. ffmpeg源码分析-ffmpeg_parse_options

    本系列 以 ffmpeg4.2 源码为准,下载地址:链接:百度网盘 提取码:g3k8 ffmpeg 源码分析系列以一条简单的命令开始,ffmpeg -i a.mp4 b.flv,分析其内部逻辑. a. ...

  6. ffmpeg源码分析-transcode_step

    本系列 以 ffmpeg4.2 源码为准,下载地址:链接:百度网盘 提取码:g3k8 ffmpeg 源码分析系列以一条简单的命令开始,ffmpeg -i a.mp4 b.flv,分析其内部逻辑. a. ...

  7. FFMPEG源码分析(一)

    FFMPEG源码分析(一) ffmpeg之前公司项目中就使用过,但是多停留于应用层面,实现某个功能时,需要哪些结构体以及调用哪些函数.最近想系统的学习一下ffmpeg,于是开始看雷霄骅https:// ...

  8. ffmpeg源码分析与应用示例(一)——H.264解码与QP提取

    本文包含以下内容 1.H.264解码流程详述与对应ffmpeg源码解析 2.针对一个应用实例介绍通过修改ffmpeg源码解决问题的方案 具有较强的综合性. 先介绍一下在第二部分中将要解决的实际问题:自 ...

  9. ffmpeg源码分析_ffmpeg音视频同步的几种策略

    在前面的文章中,我们介绍了播放器的视频渲染及音频渲染的相关知识,这些都是单独进行的,一旦在现实开发中将视频及音频结合在一起播放就会出现音视频不同步的问题. 下面我们就来分析一下如何解决音视频同步的问题 ...

最新文章

  1. MVP遇到GMCT:不加入域更改密码有几种方法?
  2. NOI 2016 优秀的拆分 (后缀数组+差分)
  3. Oracle客户端与java_Oracle 谈 JavaFX 及 Java 客户端技术的未来
  4. oc 实例变量可见度、方法
  5. vim显示行号、多行复制和删除
  6. 灵格斯(lingoes)去广告方法
  7. word中装订线位置_Word操作技巧:Word文档双面打印全攻略,解决打印难题
  8. 一个简单的划词翻译工具
  9. labview支持python免费_ni labview 2018
  10. PPT动画教程:修改幻灯片母板
  11. 幼儿园案例经验迁移_幼儿生活经验“迁移”讲述的运用指导
  12. 粗糙集理论应用的实例
  13. python 背景音乐程序代码_python 喜马拉雅 音乐下载 演示代码
  14. 计算机在职研究生的详细介绍
  15. 内存XMP是什么意思
  16. 带外通道技术(OOB)总结
  17. Kali之——使用Easy-Creds工具攻击无线网络
  18. 全志T7 Display驱动分析
  19. 在线帮助文档的6大最佳创建方法,让用户更轻松的了解企业产品
  20. 2022年PMP考试地点一般会在哪里?

热门文章

  1. 重磅!京东云区块链数据服务(BDS)正式开源
  2. 马斯洛需求层次——教会我们因人而异,推销产品
  3. dpi和ppi换算_ppi和dpi换算(1ppi等于多少dpi)
  4. 1.TensorFlow快速入门
  5. antdv tabs闪动
  6. Facebook第三方登录流程总结
  7. layui icon 使用
  8. 周杰伦入局元宇宙,带你搞懂元宇宙怎么玩
  9. canvas线条背景(抽象画布可视化,利用canvas绘制多条线条,再利用多条线条同时动态发生改变,形成一幅美妙的动态图,非常惊艳!)
  10. 少年自学python笔记_自学python笔记(一)