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

本文详细介绍了如何使用FFmpeg的libswscale库进行YUV到RGB的图像转换,包括简单的初始化方法和可配置参数的复杂初始化过程,涉及像素格式、分辨率调整和颜色空间设置等内容。
摘要由CSDN通过智能技术生成

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

参考雷霄骅博士的文章,链接:最简单的基于FFmpeg的libswscale的示例(YUV转RGB)

简介

本文记录一个基于 FFmpeg 的 libswscale 的示例。

libswscale 里面实现了各种图像像素格式的转换,例如 YUV 与 RGB 之间的转换;以及图像大小缩放(例如 640x360 拉伸为 1280x720)功能。而且 libswscale 还做了相应指令集的优化,因此它的转换效率比自己写的 C 语言的转换效率高很多。

本文记录的程序将像素格式为 YUV420P,分辨率为 480x272 的视频转换为像素格式为 RGB24,分辨率为 1280x720 的视频。

流程

简单的初始化方法

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)。

几个知识点

图像像素数据处理过程中的几个知识点:像素格式,图像拉伸,YUV像素取值范围,色域。

详见于原文章:最简单的基于FFmpeg的libswscale的示例(YUV转RGB)

源程序

本示例程序包含一个输入和一个输出,实现了从输入图像格式(YUV420P)到输出图像格式(RGB24)之间的转换,同时将输入视频的分辨率从 480x272 拉伸为 1280x720。

// Simplest FFmpeg Swscale.cpp : 定义控制台应用程序的入口点。
//

/**
* 最简单的基于 FFmpeg 的 Swscale 示例
* Simplest FFmpeg Swscale
*
* 源程序:
* 雷霄骅 Lei Xiaohua
* leixiaohua1020@126.com
* 中国传媒大学/数字电视技术
* Communication University of China / Digital TV Technology
* http://blog.csdn.net/leixiaohua1020
*
* 修改:
* 刘文晨 Liu Wenchen
* 812288728@qq.com
* 电子科技大学/电子信息
* University of Electronic Science and Technology of China / Electronic and Information Science
* https://blog.csdn.net/ProgramNovice
*
* 本程序使用 libswscale 对像素数据进行缩放转换等处理。
* 它中实现了 YUV420P 格式转换为 RGB24 格式,同时将分辨率从 480x272 拉伸为 1280x720。
* 是最简单的 libswscale 的教程。
*
* This software uses libswscale to scale / convert pixels.
* It convert YUV420P format to RGB24 format,
* and changes resolution from 480x272 to 1280x720.
* It's the simplest tutorial about libswscale.
*/

#include "stdafx.h"

#include <stdio.h>
#include <stdlib.h>

// 解决报错:'fopen': This function or variable may be unsafe.Consider using fopen_s instead.
#pragma warning(disable:4996)

// 解决报错:无法解析的外部符号 __imp__fprintf,该符号在函数 _ShowError 中被引用
#pragma comment(lib, "legacy_stdio_definitions.lib")
extern "C"
{
	// 解决报错:无法解析的外部符号 __imp____iob_func,该符号在函数 _ShowError 中被引用
	FILE __iob_func[3] = { *stdin, *stdout, *stderr };
}

#define __STDC_CONSTANT_MACROS

#ifdef _WIN32
// Windows
extern "C"
{
#include "libswscale/swscale.h"
#include "libavutil/opt.h"
#include "libavutil/imgutils.h"
};
#else
// Linux...
#ifdef __cplusplus
extern "C"
{
#endif

#include <libswscale/swscale.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>
#ifdef __cplusplus
};
#endif
#endif

int main(int argc, char* argv[])
{
	// Parameters	
	FILE *src_file = fopen("sintel_480x272_yuv420p.yuv", "rb");
	const int src_w = 480, src_h = 272;
	AVPixelFormat src_pixfmt = AV_PIX_FMT_YUV420P;
	// 获得指定像素格式每个像素占用的比特数
	const AVPixFmtDescriptor *src_pixdesc = av_pix_fmt_desc_get(src_pixfmt);
	int src_bpp = av_get_bits_per_pixel(src_pixdesc);

	FILE *dst_file = fopen("sintel_1280x720_rgb24.rgb", "wb");
	const int dst_w = 1280, dst_h = 720;
	AVPixelFormat dst_pixfmt = AV_PIX_FMT_RGB24;
	const AVPixFmtDescriptor *dst_pixdesc = av_pix_fmt_desc_get(dst_pixfmt);
	int dst_bpp = av_get_bits_per_pixel(dst_pixdesc);

	// Structures
	uint8_t *src_data[4];
	int src_linesize[4];

	uint8_t *dst_data[4];
	int dst_linesize[4];

	int rescale_method = SWS_BICUBIC; // 双三次插值
	struct SwsContext *img_convert_ctx;
	uint8_t *temp_buffer = (uint8_t *)malloc(src_w * src_h * src_bpp / 8);

	int frame_idx = 0;
	int ret = 0;

	ret = av_image_alloc(src_data, src_linesize, src_w, src_h, src_pixfmt, 1);
	if (ret < 0)
	{
		printf("Could not allocate source image.\n");
		return -1;
	}

	ret = av_image_alloc(dst_data, dst_linesize, dst_w, dst_h, dst_pixfmt, 1);
	if (ret < 0)
	{
		printf("Could not allocate destination image.\n");
		return -1;
	}

	// Init Method 1
	// 为 SwsContext 结构体分配内存
	img_convert_ctx = sws_alloc_context();
	// Show AVOption
	av_opt_show2(img_convert_ctx, stdout, AV_OPT_FLAG_VIDEO_PARAM, 0);
	// 设置 SwsContext 结构体的值
	av_opt_set_int(img_convert_ctx, "sws_flags", SWS_BICUBIC | SWS_PRINT_INFO, 0);
	av_opt_set_int(img_convert_ctx, "srcw", src_w, 0);
	av_opt_set_int(img_convert_ctx, "srch", src_h, 0);
	av_opt_set_int(img_convert_ctx, "src_format", src_pixfmt, 0);
	// '0' for MPEG (Y:0-235), '1' for JPEG (Y:0-255)
	av_opt_set_int(img_convert_ctx, "src_range", 1, 0);
	av_opt_set_int(img_convert_ctx, "dstw", dst_w, 0);
	av_opt_set_int(img_convert_ctx, "dsth", dst_h, 0);
	av_opt_set_int(img_convert_ctx, "dst_format", dst_pixfmt, 0);
	av_opt_set_int(img_convert_ctx, "dst_range", 1, 0);
	// 初始化 SwsContext 结构体
	sws_init_context(img_convert_ctx, NULL, NULL);

	// Init Method 2
	/*
	img_convert_ctx = sws_getContext(src_w, src_h, src_pixfmt, dst_w, dst_h, dst_pixfmt,
		rescale_method, NULL, NULL, NULL);
	// Colorspace
	ret = sws_setColorspaceDetails(img_convert_ctx, sws_getCoefficients(SWS_CS_ITU601), 0,
		sws_getCoefficients(SWS_CS_ITU709), 0, 0, 1 << 16, 1 << 16);
	if (ret == -1)
	{
		printf("Colorspace not support.\n");
		return -1;
	}
	*/

	while (1)
	{
		if (fread(temp_buffer, 1, src_w * src_h * src_bpp / 8, src_file) != src_w * src_h * src_bpp / 8)
		{
			break;
		}

		switch (src_pixfmt)
		{
		case AV_PIX_FMT_GRAY8:
			memcpy(src_data[0], temp_buffer, src_w * src_h);
			break;
		case AV_PIX_FMT_YUV420P:
			// Y、U、V
			memcpy(src_data[0], temp_buffer, src_w * src_h);
			memcpy(src_data[1], temp_buffer + src_w * src_h, src_w * src_h / 4);
			memcpy(src_data[2], temp_buffer + src_w * src_h * 5 / 4, src_w * src_h / 4);
			break;
		case AV_PIX_FMT_YUV422P:
			// Y、U、V
			memcpy(src_data[0], temp_buffer, src_w * src_h);
			memcpy(src_data[1], temp_buffer + src_w * src_h, src_w * src_h / 2);
			memcpy(src_data[2], temp_buffer + src_w * src_h * 3 / 2, src_w * src_h / 2);
			break;
		case AV_PIX_FMT_YUV444P:
			memcpy(src_data[0], temp_buffer, src_w * src_h);
			memcpy(src_data[1], temp_buffer + src_w * src_h, src_w * src_h);
			memcpy(src_data[2], temp_buffer + src_w * src_h * 2, src_w * src_h);
			break;
		case AV_PIX_FMT_YUYV422:
			// Packed
			memcpy(src_data[0], temp_buffer, src_w * src_h * 2);
			break;
		case AV_PIX_FMT_RGB24:
			// Packed
			memcpy(src_data[0], temp_buffer, src_w * src_h * 3);
			break;
		default:
			printf("Not Support Input Pixel Format.\n");
			break;
		}

		// 转换
		sws_scale(img_convert_ctx, src_data, src_linesize, 0, src_h, dst_data, dst_linesize);

		printf("Finish process frame %5d.\n", frame_idx);
		frame_idx++;

		switch (dst_pixfmt)
		{
		case AV_PIX_FMT_GRAY8:
			fwrite(dst_data[0], 1, dst_w * dst_h, dst_file);
			break;
		case AV_PIX_FMT_YUV420P:
			// Y、U、V
			fwrite(dst_data[0], 1, dst_w * dst_h, dst_file);
			fwrite(dst_data[1], 1, dst_w * dst_h / 4, dst_file);
			fwrite(dst_data[2], 1, dst_w * dst_h / 4, dst_file);
			break;
		case AV_PIX_FMT_YUV422P:
			// Y、U、V
			fwrite(dst_data[0], 1, dst_w * dst_h, dst_file);
			fwrite(dst_data[1], 1, dst_w * dst_h / 2, dst_file);
			fwrite(dst_data[2], 1, dst_w * dst_h / 2, dst_file);
			break;
		case AV_PIX_FMT_YUV444P:
			fwrite(dst_data[0], 1, dst_w * dst_h, dst_file);
			fwrite(dst_data[1], 1, dst_w * dst_h, dst_file);
			fwrite(dst_data[2], 1, dst_w * dst_h, dst_file);
			break;
		case AV_PIX_FMT_YUYV422:
			// Packed
			fwrite(dst_data[0], 1, dst_w * dst_h * 2, dst_file);
			break;
		case AV_PIX_FMT_RGB24:
			// Packed
			fwrite(dst_data[0], 1, dst_w * dst_h * 3, dst_file);
			break;
		default:
			printf("Not Support Output Pixel Format.\n");
			break;
		}
	}

	sws_freeContext(img_convert_ctx);

	free(temp_buffer);
	fclose(src_file);
	fclose(dst_file);
	av_freep(&src_data[0]);
	av_freep(&dst_data[0]);

	system("pause");
	return 0;
}

结果

运行程序,输出如下:

在这里插入图片描述

程序的输入为一个名称为“sintel_480x272_yuv420p.yuv”的视频。该视频像素格式是 YUV420P,分辨率为 480x272。

在这里插入图片描述

程序的输出为一个名称为“sintel_1280x720_rgb24.rgb”的视频。该视频像素格式是 RGB24,分辨率为 1280x720。

在这里插入图片描述

工程文件下载

GitHub:UestcXiye / Simplest-FFmpeg-Swscale

CSDN:Simplest FFmpeg Swscale.zip

参考链接

  1. 最简单的基于FFmpeg的libswscale的示例(YUV转RGB)
要将YUV422格式换为RGB格式,您可以使用FFmpeg库中的函数进行换。以下是一个使用FFmpeg进行YUV422到RGB换的示例代码: ```c #include <stdio.h> #include <libavutil/imgutils.h> #include <libswscale/swscale.h> int main() { int width = 640; // 输入图像的宽度 int height = 480; // 输入图像的高度 // 分配输入图像的内存 uint8_t* yuv422Buffer = (uint8_t*)malloc(width * height * 2); // 在这里填充YUV422数据到yuv422Buffer // 分配输出图像的内存 uint8_t* rgbBuffer = (uint8_t*)malloc(width * height * 3); // 创建输入图像的AVFrame AVFrame* yuv422Frame = av_frame_alloc(); yuv422Frame->format = AV_PIX_FMT_YUV422P; yuv422Frame->width = width; yuv422Frame->height = height; av_image_fill_arrays(yuv422Frame->data, yuv422Frame->linesize, yuv422Buffer, AV_PIX_FMT_YUV422P, width, height, 1); // 创建输出图像的AVFrame AVFrame* rgbFrame = av_frame_alloc(); rgbFrame->format = AV_PIX_FMT_RGB24; rgbFrame->width = width; rgbFrame->height = height; av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, rgbBuffer, AV_PIX_FMT_RGB24, width, height, 1); // 创建换上下文 struct SwsContext* swsContext = sws_getContext(width, height, AV_PIX_FMT_YUV422P, width, height, AV_PIX_FMT_RGB24, 0, NULL, NULL, NULL); // 进行换 sws_scale(swsContext, yuv422Frame->data, yuv422Frame->linesize, 0, height, rgbFrame->data, rgbFrame->linesize); // 在这里可以使用rgbBuffer中的RGB数据 // 释放资源 free(yuv422Buffer); free(rgbBuffer); av_frame_free(&yuv422Frame); av_frame_free(&rgbFrame); sws_freeContext(swsContext); return 0; } ``` 请注意,此示例假设您已经填充了正确的YUV422数据到yuv422Buffer。您需要根据实际情况修改输入图像的宽度和高度,并在换后使用rgbBuffer中的RGB数据进行进一步处理。还要确保您已经链接了FFmpeg库并包含所需的头文件。 请参考FFmpeg文档以获得更多关于使用FFmpeg进行图像格式换的详细信息和选项。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UestcXiye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值