作者:gerardlong | 来源:互联网 | 2023-08-10 19:57
本文像素格式转换用到的库是 FFmpeg 的 libswscale,将 YUV 像素格式数据转换成 RGB 像素格式数据。
一、使用的相关函数说明:
1、获取像素格式转换上下文函数:
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:原始宽高和原始像素格式(我们这里原始像素格式是yuv420p);
dstW, dstH, dstFormat:目标宽高和目标像素格式(我们这里原始像素格式是rgb24),不仅可以转换像素格式,也可以转换宽高;
flag:指定使用何种算法,例如快速线性、差值和矩阵等等,不同的算法性能也不同,快速线性算法性能相对较高。只针对尺寸的变换。
/* values for the flags, the stuff on the command line is different */
#define SWS_FAST_BILINEAR 1
#define SWS_BILINEAR 2
#define SWS_BICUBIC 4
#define SWS_X 8
#define SWS_POINT 0x10
#define SWS_AREA 0x20
#define SWS_BICUBLIN 0x40
#define SWS_GAUSS 0x80
#define SWS_SINC 0x100
#define SWS_LANCZOS 0x200
#define SWS_SPLINE 0x400
srcFilter, stFilter:这两个参数是做过滤器用的,可以传 nullptr;
param:和 flag 算法相关,也可以传 nullptr;
返回值:成功返回转换格式上下文指针,失败返回 NULL;
注意:使用完格式转换上下文最后不要忘记调用函数 void sws_freeContext(struct SwsContext *swsContext)
释放上下文;有人可能会有疑问,调用的是 sws_getContext
函数,函数名里并没有 create 或者 alloc 字眼,也需要释放吗?我们可以参考一下源码:
发现源码当中调用了 sws_alloc_set_opts
,所以最后是需要释放上下文的。我们也可以使用以下函数来创建上下文:
struct SwsContext *sws_alloc_context(void);
int av_opt_set(void *obj, const char *name, const char *val, int search_flags);
2、转换函数:
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[]);
参数说明:
struct SwsContext *c:像素格式转换上下文;
const uint8_t *const srcSlice[]:输入的数据,silice可以理解为一帧;
const int srcStride[]:输入数据的每一个平面的每一行的大小(linesize),而不是每一个平面的总大小;
int srcSliceY:从哪个位置开始处理,直接传0即可;
int srcSliceH:图像的高度;
uint8_t *const dst[]:输出的数据;
const int dstStride[]:输出数据的每一个平面的每一行的大小;
注意:sws_scale 函数不会为传入的输入数据和输出数据创建堆空间
3、创建输入输出缓冲区:
#include
int av_image_alloc(uint8_t *pointers[4], int linesizes[4],
int w, int h, enum AVPixelFormat pix_fmt, int align);
参数说明:
(uint8_t *pointers[4]:缓冲区数组(除了 Y、U 和 V 三个分量,可能会有透明度分量,所以数组 size 为 4);
int linesizes[4]:每个平面的每一行的大小数组;
int w, int h, enum AVPixelFormat pix_fmt:图片的宽、高和像素格式;
int align:是否对齐,一般传1;
创建的缓冲区数组指向堆空间,最后我们需要使用函数 av_pfree 释放它;不确定缓冲区是否需要释放的话,可以参考函数注释或者源码;源码内部是有调用 av_malloc 函数的,所以需要我们去释放。
二、示例代码:
示例代码中 inData 输入缓冲区分成了四块,每一块指向一个分量,为了兼容透明分量,指针数组size为4,以YUV420P为例,inData的第1个元素指向 Y 分量,第2个元素指向 U 分量,第3个元素指向 V 分量,一帧的 YUV 数据是连续的,如图:
在音频中没有行的概念 linesize 就是一个平面的大小,但是在视频中是有行的概念的,inStrides中存储的是一帧数据每一行中 Y、U 和 V 分量的长度,以 YUV420P 为例,Y 的长度是一帧的宽度,U 的长度是一帧的宽度的一半,V 的长度也是一帧的宽度的一半。outData 和 outStrides 是同样的道理。
#include "ffmpegutils.h"
#include
#include
extern "C" {
#include
#include
#include
}
FFmpegUtils::FFmpegUtils()
{
}
//void FFmpegUtils::convretRawVideo(RawVideoFile &in, RawVideoFile &out)
void FFmpegUtils::convretRawVideo(RawVideoFrame &in, RawVideoFrame &out)
{
int ret = 0;
// 转换格式上下文
SwsContext *ctx = nullptr;
// 输入输出缓冲区
// inData 和 outData 指向一帧的数据
uint8_t *inData[4], *outData[4];
// 每个平面一行的大小
int inStrides[4], outStrides[4];
// 每一帧图片的大小
int inFrameSize, outFrameSize;
/*
QFile inFile(in.filename);
QFile outFile(out.filename);
*/
ret = av_image_alloc(inData, inStrides, in.width, in.height, in.format, 1);
if (ret