目录
- 参考
- libswscale介绍
- 示例说明
- 示例代码
1. 参考
- [1] ffmpeg.org/Libswscale Documentation
- [2] ffmpeg.org/FFmpeg Scaler Documentation
- [3] FFmpeg/doc/examples/scaling_video.c
- [4] 雷霄骅/最简单的基于FFmpeg的libswscale的示例(YUV转RGB)
2. libswscale介绍
FFmpeg中的libavfilter提供了执行高度优化的图像比例缩放、图像颜色空间/像素格式转换,如rgb与yuv之间转换。libswscale做一些指令集的优化,因此它的转换效率比自己写的C语言的转换效率高很多。
简单的初始化方法
最主要的函数有3个:
- sws_getContext():使用参数初始化SwsContext结构体。可以用另一个函数sws_getCachedContext()取代。
- sws_scale():转换一帧图像。
- sws_freeContext():释放SwsContext结构体。
复杂但是更灵活的初始化方法
初始化SwsContext除了调用sws_getContext()之外还有另一种方法,更加灵活,可以配置更多的参数。该方法调用的函数如下所示。
- sws_alloc_context():为SwsContext结构体分配内存。
- av_opt_set_XXX():通过av_opt_set_int(),av_opt_set()…等等一系列方法设置SwsContext结构体的值。在这里需要注意,SwsContext结构体的定义看不到,所以不能对其中的成员变量直接进行赋值,必须通过av_opt_set()这类的API才能对其进行赋值。
- sws_init_context():初始化SwsContext结构体。
这种复杂的方法可以配置一些sws_getContext()配置不了的参数。比如说设置图像的YUV像素的取值范围是JPEG标准(Y、U、V取值范围都是0-255)还是MPEG标准(Y取值范围是16-235,U、V的取值范围是16-240)。
FFmpeg中可以通过使用av_opt_set()设置“src_range”和“dst_range”来设置输入和输出的YUV的取值范围。
- 如果“dst_range”字段设置为“1”的话,则代表输出的YUV的取值范围遵循“jpeg”标准。
- 如果“dst_range”字段设置为“0”的话,则代表输出的YUV的取值范围遵循“mpeg”标准。
YUV像素格式的说明参考YUV数据格式
FFmpeg支持的像素格式的定义位于libavutil\pixfmt.h,是一个名称为AVPixelFormat的枚举类型。
FFmpeg支持多种像素拉伸的方式。这些方式的定义位于libswscale\swscale.h中,如下所示。
#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
说明:
- SWS_BICUBIC性能比较好。
- SWS_FAST_BILINEAR在性能和速度之间有一个比好好的平衡。
- SWS_POINT的效果比较差。
- 有关这些方法的评测可以参考文章:《ffmpeg中的sws_scale算法性能测试》
Libswscale支持色域的转换。色域的介绍参考[4]。
3. 示例说明
示例来源于[3]。
生成一个YUV420P格式的视频数据,并通过swscale将其转换为RGB24格式的视频数据。
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……
AV_PIX_FMT_RGB24格式数据存储方式如下:
data[0]: R1, G1, B1, R2, G2, B2, R3, G3, B3, R4, G4, B4……
以下示例程序的流程图。
函数说明:
- sws_getContext():使用参数初始化SwsContext结构体。
- sws_scale():转换一帧图像。
- av_image_alloc(): 根据图像的尺寸、格式和内存对齐方式分配存储图像的空间。
4. 示例代码
以下的代码来源于[3]。
/**
* @file
* libswscale API use example.
* @example scaling_video.c
*/
#include <libavutil/imgutils.h>
#include <libavutil/parseutils.h>
#include <libswscale/swscale.h>
static void fill_yuv_image(uint8_t *data[4], int linesize[4],
int width, int height, int frame_index)
{
int x, y;
/* Y */
for (y = 0; y < height; y++)
for (x = 0; x < width; x++)
data[0][y * linesize[0] + x] = x + y + frame_index * 3;
/* Cb and Cr */
for (y = 0; y < height / 2; y++) {
for (x = 0; x < width / 2; x++) {
data[1][y * linesize[1] + x] = 128 + y + frame_index * 2;
data[2][y * linesize[2] + x] = 64 + x + frame_index * 5;
}
}
}
int main(int argc, char **argv)
{
uint8_t *src_data[4], *dst_data[4];
int src_linesize[4], dst_linesize[4];
int src_w = 320, src_h = 240, dst_w, dst_h;
enum AVPixelFormat src_pix_fmt = AV_PIX_FMT_YUV420P, dst_pix_fmt = AV_PIX_FMT_RGB24;
const char *dst_size = NULL;
const char *dst_filename = NULL;
FILE *dst_file;
int dst_bufsize;
struct SwsContext *sws_ctx;
int i, ret;
if (argc != 3) {
fprintf(stderr, "Usage: %s output_file output_size\n"
"API example program to show how to scale an image with libswscale.\n"
"This program generates a series of pictures, rescales them to the given "
"output_size and saves them to an output file named output_file\n."
"\n", argv[0]);
exit(1);
}
dst_filename = argv[1];
dst_size = argv[2];
if (av_parse_video_size(&dst_w, &dst_h, dst_size) < 0) {
fprintf(stderr,
"Invalid size '%s', must be in the form WxH or a valid size abbreviation\n",
dst_size);
exit(1);
}
dst_file = fopen(dst_filename, "wb");
if (!dst_file) {
fprintf(stderr, "Could not open destination file %s\n", dst_filename);
exit(1);
}
/* create scaling context */
sws_ctx = sws_getContext(src_w, src_h, src_pix_fmt,
dst_w, dst_h, dst_pix_fmt,
SWS_BILINEAR, NULL, NULL, NULL);
if (!sws_ctx) {
fprintf(stderr,
"Impossible to create scale context for the conversion "
"fmt:%s s:%dx%d -> fmt:%s s:%dx%d\n",
av_get_pix_fmt_name(src_pix_fmt), src_w, src_h,
av_get_pix_fmt_name(dst_pix_fmt), dst_w, dst_h);
ret = AVERROR(EINVAL);
goto end;
}
/* allocate source and destination image buffers */
if ((ret = av_image_alloc(src_data, src_linesize,
src_w, src_h, src_pix_fmt, 16)) < 0) {
fprintf(stderr, "Could not allocate source image\n");
goto end;
}
/* buffer is going to be written to rawvideo file, no alignment */
if ((ret = av_image_alloc(dst_data, dst_linesize,
dst_w, dst_h, dst_pix_fmt, 1)) < 0) {
fprintf(stderr, "Could not allocate destination image\n");
goto end;
}
dst_bufsize = ret;
for (i = 0; i < 100; i++) {
/* generate synthetic video */
fill_yuv_image(src_data, src_linesize, src_w, src_h, i);
/* convert to destination format */
sws_scale(sws_ctx, (const uint8_t * const*)src_data,
src_linesize, 0, src_h, dst_data, dst_linesize);
/* write scaled image to file */
fwrite(dst_data[0], 1, dst_bufsize, dst_file);
}
fprintf(stderr, "Scaling succeeded. Play the output file with the command:\n"
"ffplay -f rawvideo -pix_fmt %s -video_size %dx%d %s\n",
av_get_pix_fmt_name(dst_pix_fmt), dst_w, dst_h, dst_filename);
end:
fclose(dst_file);
av_freep(&src_data[0]);
av_freep(&dst_data[0]);
sws_freeContext(sws_ctx);
return ret < 0;
}