一、图像转换swscale库
FFmpeg中,swscale库主要功能为对图像进行变换,主要做:
1.图像大小变换
例:可以将图像从19201080(1080P) 变大3840×2160(4K) 或缩小为1280×720(720p)
2.图像格式转化
例:可以将图像从YUV420P 转化为RGB格式
3.图像滤波处理
会对图像进行滤波处理,例如平滑处理。
主要函数为sws_getCachedContext、sws_freeContext、sws_scale
sws_getCachedContext: 目的是创建一个SwsContext结构体
SwsContext *context : 传入一个SwsContext结构体,判断传入是否context 是否和传入的参数是一致的,如果是一致的就直接返回。不一致的话,则会释放掉后再创建。传入NULL的话就直接创建了。
int srcW:传入图像的宽度
int srcH:传入图像的高度
srcFormat: 传入图像的格式
int dstW: 希望转换的图像宽度
int dstH: 希望转换的图像高度
dstFormat:希望转换的图像格式
flags:选择缩放算法(当输入输出图像大小不同时生效),选择SWS_FAST_BILINEAR速度会比较快
srcFilter:输入图像的滤波器信息, 若不需要传NULL
dstFilter:输出图像的滤波器信息, 若不需要传NULL
param:特定缩放算法需要的参数,默认为NULL
struct SwsContext *sws_getCachedContext(struct SwsContext *context, int srcW,
int srcH, enum AVPixelFormat srcFormat,
int dstW, int dstH,
enum AVPixelFormat dstFormat, int flags,
SwsFilter *srcFilter,
SwsFilter *dstFilter,
const double *param)
{
static const double default_param[2] = {
SWS_PARAM_DEFAULT,
SWS_PARAM_DEFAULT };
int64_t src_h_chr_pos = -513, dst_h_chr_pos = -513,
src_v_chr_pos = -513, dst_v_chr_pos = -513;
if (!param)
param = default_param;
/* 先判断context是否为空,不为空对比参数是否与传输进来的参数一致 */
if (context &&
(context->srcW != srcW ||
context->srcH != srcH ||
context->srcFormat != srcFormat ||
context->dstW != dstW ||
context->dstH != dstH ||
context->dstFormat != dstFormat ||
context->flags != flags ||
context->param[0] != param[0] ||
context->param[1] != param[1])) {
av_opt_get_int(context, "src_h_chr_pos", 0, &src_h_chr_pos);
av_opt_get_int(context, "src_v_chr_pos", 0, &src_v_chr_pos);
av_opt_get_int(context, "dst_h_chr_pos", 0, &dst_h_chr_pos);
av_opt_get_int(context, "dst_v_chr_pos", 0, &dst_v_chr_pos);
sws_freeContext(context);
context = NULL;
}
/* 为空则执行下一步申请空间 */
if (!context) {
if (!(context = sws_alloc_context()))
return NULL;
context->srcW = srcW;
context->srcH = srcH;
context->srcFormat = srcFormat;
context->dstW = dstW;
context->dstH = dstH;
context->dstFormat = dstFormat;
context->flags = flags;
context->param[0] = param[0];
context->param[1] = param[1];
av_opt_set_int(context, "src_h_chr_pos", src_h_chr_pos, 0);
av_opt_set_int(context, "src_v_chr_pos", src_v_chr_pos, 0);
av_opt_set_int(context, "dst_h_chr_pos", dst_h_chr_pos, 0);
av_opt_set_int(context, "dst_v_chr_pos", dst_v_chr_pos, 0);
if (sws_init_context(context, srcFilter, dstFilter) < 0) {
sws_freeContext(context);
return NULL;
}
}
return context;
}
SwsContext *c:传入一个上下文地址
srcSlice :输入数据每个通道的指针,例如rgb只有一个通道,YUV数据则有3个通道
srcStride:输入数据的每个通道一行的字节数,例如 rgb 为packed模式所以填 宽度 * 3, YUV420P为planar模式 所以填一个数组
{ 宽度, 宽度 /2, 宽度 / 2 }
srcSliceY:处理数据起始位置
srcSliceH: 处理多少行数据,可以多线程进行处理。例如线程1处理0~h/2的数据,线程2处理h/2 ~ h的数据。
dst:输出数据的每个通道数据指针
dstStride:输出数据的每个通道一行的字节数
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[])
{
if (c->nb_slice_ctx)
c = c->slice_ctx[0];
return scale_internal(c, srcSlice, srcStride, srcSliceY, srcSliceH,
dst, dstStride, 0, c->dstH);
}
sws_freeContext::释放context
SwsContext *c : 传入一个SwsContext的地址
void sws_freeContext(SwsContext *c)
二、yuv转RGBA代码
#include <iostream>
#include <fstream>
#include<ctime>
using namespace std;
extern "C"{
//引用ffmpeg头文件
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
}
//预处理指令导入库
#pragma comment(lib,"avcodec.lib")
#pragma comment(lib, "swscale.lib")
#define YUV_FILE "gzm.yuv"
#define RGB_FILE "1280_720_gzm.rgba"
int main(int argc, char* argv[])
{
//1920*1080 YUV转 RGBA 1920*1080
int width = 1920;
int height = 1080;
int rgb_width = 1280;
int rgb_height = 720;
int yuv_linesize[3] = {
width, width / 2, width / 2 };
SwsContext* yuv2rgb = nullptr;
//YUV yyyyyy uuu vvv
unsigned char* yuv[3] = {
0 };
yuv[0] = new unsigned char[width * height];
yuv[1] = new unsigned char[width * height / 4];
yuv[2] = new unsigned char[width * height / 4];
//RGB交叉存储 rgba rbga
unsigned char* rgba = new unsigned char[width * height * 4];
int rgba_linesize = rgb_width * 4;
ifstream ifs;
ifs.open(YUV_FILE, ios::binary);
if (!ifs) return -1;
ofstream ofs;
ofs.open(RGB_FILE, ios::binary);
if (!ofs) return -1;
time_t start_time = time(NULL);
cout << "start " << endl;
while (true)
{
ifs.read((char*)yuv[0], width * height);
ifs.read((char*)yuv[1], width * height / 4);
ifs.read((char*)yuv[2], width * height / 4);
if (ifs.gcount() == 0) break;
//YUV转RGBA
yuv2rgb = sws_getCachedContext(
yuv2rgb,
width, height,
AV_PIX_FMT_YUV420P,
rgb_width, rgb_height,
AV_PIX_FMT_RGBA,
SWS_BICUBLIN //双线性插值算法,选择支持变化的算法
, 0, 0, 0);
if (!yuv2rgb)
{
cerr << "sws_getCachedContext failed" << endl;
return -1;
}
unsigned char* data[1];
data[0] = rgba;
int lines[1] = {
rgba_linesize };
int ret = sws_scale(yuv2rgb,
yuv,
yuv_linesize,
0,
height, //输入高度
data,
lines);
//ofs.write((char*)rgba, rgb_width * rgb_height * 4);
}
cout << "end " << endl;
time_t end_time = time(NULL);
cout << end_time - start_time << "" << endl;
delete yuv[0];
delete yuv[1];
delete yuv[2];
delete rgba;
ofs.close();
ifs.close();
return 0;
}
测试背景: 1920 * 1080 YUV420P 数据大小1.4G 转换成 1280 * 720 RBGA 格式
SWS_FAST_BILINEAR : 耗时 4s
SWS_BILINEAR: 耗时 5s
SWS_BICUBLIN: 耗时 5s