有关ffmpeg中主要的api函数源码解析参考雷神系列文章,整理如下ffmpeg学习(2)获取和使用,源码分析。

函数sws_scale位于libswscale库,该库是一个主要用于处理图片像素数据的类库。可以完成图片像素格式的转换,图片的拉伸等工作。libswscale的使用参考文章:

FFmpeg源代码简单分析:libswscale的sws_getContext()

最简单的基于FFmpeg的libswscale的示例(YUV转RGB)

本文仅演示的程序将像素格式为YUV420P,分辨率为480x272的视频转换为像素格式为RGB24。另外,还记录将非1字节对齐的AVFrame转换为1字节对齐,便于保存处理。

流程

简单的初始化方法

Libswscale使用起来很方便,最主要的函数只有3个:
(1) sws_getContext():使用参数初始化SwsContext结构体。
(2) sws_scale():转换一帧图像。
(3) sws_freeContext():释放SwsContext结构体。

其中sws_getContext()也可以用另一个接口函数sws_getCachedContext()取代。

复杂但是更灵活的初始化方法

初始化SwsContext除了调用sws_getContext()之外还有另一种方法,更加灵活,可以配置更多的参数。该方法调用的函数如下所示。
(1) sws_alloc_context():为SwsContext结构体分配内存。
(2) av_opt_set_XXX():通过av_opt_set_int()av_opt_set()…等等一系列方法设置SwsContext结构体的值。在这里需要注意,SwsContext结构体的定义看不到,所以不能对其中的成员变量直接进行赋值,必须通过av_opt_set()这类的API才能对其进行赋值。
(3) sws_init_context():初始化SwsContext结构体。

这种复杂的方法可以配置一些sws_getContext()配置不了的参数。比如说设置图像的YUV像素的取值范围是JPEG标准(Y、U、V取值范围都是0-255)还是MPEG标准(Y取值范围是16-235,U、V的取值范围是16-240)。

后面使用函数sws_getContext()sws_scale()完成本文目标。

几个知识点

下文记录几个图像像素数据处理过程中的几个基本知识点:像素格式,图像拉伸,YUV像素取值范围,色域。这里仅说明像素格式,其他可以参考相关博客。

像素格式

像素格式的知识此前已经记录过,不再重复。在这里记录一下FFmpeg支持的像素格式。有几点注意事项:

(1) 所有的像素格式的名称都是以“AV_PIX_FMT_”开头

(2) 像素格式名称后面有“P”的,代表是planar格式,否则就是packed格式。Planar格式不同的分量分别存储在不同的数组中,例如AV_PIX_FMT_YUV420P存储方式如下:

data[0]: Y1, Y2, Y3, Y4, Y5, Y6, Y7, Y8……
data[1]: U1, U2, U3, U4……
data[2]: V1, V2, V3, V4……

Packed格式的数据都存储在同一个数组中,例如AV_PIX_FMT_RGB24存储方式如下:

data[0]: R1, G1, B1, R2, G2, B2, R3, G3, B3, R4, G4, B4……

(3) 像素格式名称后面有“BE”的,代表是Big Endian格式;名称后面有“LE”的,代表是Little Endian格式。

FFmpeg支持的像素格式的定义位于libavutil\pixfmt.h,是一个名称为AVPixelFormat的枚举类型。

FFmpeg有一个专门用于描述像素格式的结构体AVPixFmtDescriptor。该结构体的定义位于libavutil\pixdesc.h。通过av_pix_fmt_desc_get()可以获得指定像素格式的AVPixFmtDescriptor结构体。通过AVPixFmtDescriptor结构体可以获得不同像素格式的一些信息。例如下文中用到了av_get_bits_per_pixel(),通过该函数可以获得指定像素格式每个像素占用的比特数(Bit Per Pixel)

示例程序一

示例程序包含一个输入和一个输出,实现了从输入图像格式(YUV420P)到输出图像格式(RGB24)之间的转换,并且调整了分辨率。

#include <stdio.h>#ifdef __cplusplus
extern "C" {#endif  #include "libswscale/swscale.h"
#include "libavutil/pixdesc.h"
#include "libavutil/frame.h"
#include "libavutil/imgutils.h"#ifdef __cplusplus
}
#endif int main()
{//输入数据和参数FILE *src_file = fopen("Titanic_640x272_yuv420p.yuv", "rb");const int src_w = 640, src_h = 272;AVPixelFormat src_pixfmt = AV_PIX_FMT_YUV420P;int src_bpp = av_get_bits_per_pixel(av_pix_fmt_desc_get(src_pixfmt));FILE *dst_file = fopen("Titanic_640x480_rgb24.rgb", "wb");const int dst_w = 640, dst_h = 480;AVPixelFormat dst_pixfmt = AV_PIX_FMT_RGB24;int dst_bpp = av_get_bits_per_pixel(av_pix_fmt_desc_get(dst_pixfmt));//使用AVFrame分配缓存yuv和rgb数据,用于转换AVFrame *frame_yuv = av_frame_alloc();av_image_alloc(frame_yuv->data, frame_yuv->linesize, src_w, src_h, src_pixfmt, 1);AVFrame *frame_rgb = av_frame_alloc();av_image_alloc(frame_rgb->data, frame_rgb->linesize, dst_w, dst_h, dst_pixfmt, 1);// 读取yuv图像数据到frame_yuv缓存if(fread(frame_yuv->data[0], 1, src_w*src_h*src_bpp / 8, src_file) != src_w*src_h*src_bpp / 8) {return 0;}// 转换SwsContext *img_convert_ctx;img_convert_ctx = sws_getContext(src_w, src_h, src_pixfmt, dst_w,dst_h, dst_pixfmt,SWS_BILINEAR,NULL, NULL,NULL);sws_scale(img_convert_ctx, frame_yuv->data, frame_yuv->linesize, 0, src_h, frame_rgb->data, frame_rgb->linesize);// 保存rgb数据, Packed方式,数据全部保存在 frame_rgb->data[0]中fwrite(frame_rgb->data[0], 1, dst_w*dst_h * dst_bpp / 8, dst_file);   // Packed// 释放sws_freeContext(img_convert_ctx);av_frame_free(&frame_yuv);av_frame_free(&frame_rgb);fclose(src_file);fclose(dst_file);
}

结果如下分别打开yuv和rgb文件,显示正常。

示例程序二

在前面博客 ffmpeg学习(5)视频解码 中,解码出来AVFrame缓存的yuv数据非1字节对齐,并且三个分量保存在不同的内存区域,保存方式相对繁琐。这里再次给出

这里使用libswscale库函数sws_scale将解码后的图像转换为1字节对齐的yuv420p数据。

在源代码中添加 以下变量声明与初始化部分

// yuv420p对齐处理 变量
AVFrame *frame_yuv = av_frame_alloc();
// 分配缓冲区,接收转换后yuv420p的1字节对齐数据,分辨率不改变
frame_yuv->width = video_decoder_ctx->width;
frame_yuv->height = video_decoder_ctx->height;
av_image_alloc(frame_yuv->data, frame_yuv->linesize,frame_yuv->width, frame_yuv->height, AV_PIX_FMT_YUV420P, 1);// SwsContext上下文,用于sws_scale调用
SwsContext *sws_ctx = sws_getContext(video_decoder_ctx->width, video_decoder_ctx->height, video_decoder_ctx->pix_fmt, // 输入格式frame_yuv->width, frame_yuv->height, AV_PIX_FMT_YUV420P,                         // 输出格式SWS_BILINEAR, NULL, NULL, NULL);                                                 // 变换处理

只会在解码后使用如下代码,进行yuv数据处理并保存在文件中

// 解码的视频数据处理
sws_scale(sws_ctx,frame->data, frame->linesize, 0, frame->height,frame_yuv->data, frame_yuv->linesize);fwrite(frame_yuv->data[0], 1, frame_yuv->width*frame_yuv->height * 3 / 2, fyuv);
printf("\rSucceed to decode frame %d\n", frameCnt++);

这种方式更为简单。


  • 20210524 特殊编码器输入要求

    某些硬件编码器处于性能考虑,对yuv420p的内存布局对齐方式有要求。这种情况下,使用1字节对齐分配数据直接编码,图像会有重影。使用16/32进行尝试。

    AVFrame *frame_yuv = av_frame_alloc();
    av_image_alloc(frame_yuv->data, frame_yuv->linesize,video_decoder_ctx->width, frame_yuv->height, AV_PIX_FMT_YUV420P, 32); // 32字节对齐
    // 编解码可能需要参数
    frame_yuv->width = video_decoder_ctx->width;
    frame_yuv->height = video_decoder_ctx->height;
    frame_yuv->format = AV_PIX_FMT_YUV420P;
    

    或者使用av_frame_get_buffer函数 :

    AVFrame *frame_yuv = av_frame_alloc();
    frame_yuv->width = video_decoder_ctx->width;
    frame_yuv->height = video_decoder_ctx->height;
    frame_yuv->format = AV_PIX_FMT_YUV420P;
    // 使用下面方法,要求frame_yuv必须指定图像参数或音频参数;参数0为自动选择对齐字节数
    av_frame_get_buffer(frame_yuv , 0);
    

    可以参考 ffmpeg学习 结构体分析AVFrame。

ffmpeg学习 函数分析sws_scale相关推荐

  1. ffmpeg学习 函数分析swr_convert

    文章目录 简单说明 采样格式转换 声道格式转换 采样频率转换 libswresample库使用 函数介绍 示例代码 有关ffmpeg中主要的api函数源码解析参考雷神系列文章,整理如下 ffmpeg学 ...

  2. ffmpeg学习日记506-源码-av_image_copy()函数分析及功能

    ffmpeg学习日记506-源码-av_image_copy()函数分析及功能 实现文件 av_image_copy()实现在libavutil/imgutils.c中 函数原型 void av_im ...

  3. FFmpeg学习 avcodec软解码函数分析

    前言 本文分析ffmpeg软解码流程,相关函数如下,以find_stream_info中的try_decode_frame为例: 相关函数都在libavcodec包下. 基本调用流程如下: const ...

  4. FFmpeg源码分析:sws_scale图像缩放与图像转换

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

  5. FFmpeg源代码简单分析:libswscale的sws_scale()

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  6. Windows事件等待学习笔记(三)—— WaitForSingleObject函数分析

    Windows事件等待学习笔记(三)-- WaitForSingleObject函数分析 要点回顾 WaitForSingleObject NtWaitForSingleObject KeWaitFo ...

  7. FFmpeg源代码简单分析:libavdevice的gdigrab

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  8. FFmpeg源代码简单分析:libavdevice的avdevice_register_all()

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  9. FFmpeg源代码简单分析:configure

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

最新文章

  1. CB:南土所梁玉婷组-细菌群落的高稳定性和代谢能力促进了土壤中易分解碳的快速减少...
  2. Epic苹果诉讼案,却把索尼微软Steam都拉下水,网友忙吃瓜
  3. 18秋学期计算机基础在线作业2,东大18秋学期《计算机应用基础》在线作业2.pdf...
  4. 【STL基础】list
  5. TP5: 日志记录改造——4
  6. 机器学习之必知开源数据集
  7. android拍照功能编程,android实现手机App实现拍照功能示例
  8. Sql server 数据转到 Mysql 数据库
  9. JVM初学之堆的内存模型
  10. POJ NOI MATH-7657 连乘积末尾0的个数
  11. C++之printf格式
  12. 高校毕业设计管理系统【附源码】
  13. 树莓派默认密码_树莓派介绍:没有显示器,怎样远程控制树莓派?
  14. LoopClosing中为什么要使用剥离尺度的sim3计算投影匹配
  15. 【告别小白】什么是标志性语言?
  16. ARM汇编之MOV指令
  17. 【无敌浪子】python爬取足球赛事数据
  18. Vlan与二层交换机
  19. 程序设计与算法 | (3) 输入输出与运算符、表达式
  20. twitter推特全量用户收集与发文采集

热门文章

  1. HC32F460 FPU使用
  2. 【JM】电脑检测不到U盘
  3. 使用 KubeSphere 和极狐GitLab 打造云原生持续交付系统
  4. 单元主服务器解决性能瓶颈的方法,HBase
  5. 浏览器兼容css hack,CSS Hack技术解决多浏览器兼容问题
  6. [HAL库学习之路]5.IWDG-独立看门狗
  7. C语言编程>第十三周 ⑧ 已知学生的记录由学号和学习成绩组成,M名学生的数据已存入stu结构体数组中。请编写函数fun,该函数的功能是:
  8. 美德乐吸奶器怎么样?
  9. Week8 :差分约束,拓扑排序和kahn,强连通图和kosaraju
  10. tensorflow进阶(更新中...)