SwsContext可用于缩放yuv/rgb图像的大小或者改变其像素点颜色空间格式。
初始化
Libswscale使用起来很方便,最主要的函数只有3个:
(1) sws_getContext():使用参数初始化SwsContext结构体。
(2) sws_scale():转换一帧图像。
(3) sws_freeContext():释放SwsContext结构体。
其中sws_getContext()也可以用另一个接口函数sws_getCachedContext()取代。
像素格式
(1)后缀带P的如yuv420p,则y/u/v分别存放在AVFrame.data[0]/data[1]/data[2]。
(2)后缀不带P的如rgb24,则rgb数据都存放在AVFrame.data[0]中。
AVFrame
本文实例是通过将图像读取到AVFrame中,然后在进行swscale处理的,所以这里介绍下AVFrame的初始化及处理
注意,我们操作yuv数据时,要根据像素格式,响应的访问avFrame.data[0/1/2],以获取正确的yuv数据。
//初始化源图AVFrame结构
AVFrame* ptFrameSrc= av_frame_alloc();
int dwSrcPictureSize = avpicture_get_size(em_Src_Pix_fmt, dwSrc_Width, dwSrc_Height);
//上边的只是alloc了一个AVFrame的结构体,真正数据存放在pbySrcBuf中
uint8_t* pbySrcBuf = (uint8_t*)av_malloc(dwSrcPictureSize);
avpicture_fill((AVPicture *)ptFrameSrc, pbySrcBuf, em_Src_Pix_fmt, dwSrc_Width, dwSrc_Height);
示例
#include <stdio.h>
extern "C"
{
#include <libswscale/swscale.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>
#include <libavutil/frame.h>
#include <libavcodec/avcodec.h>
};
#define _SRC_WIDTH 1920
#define _SRC_HEIGHT 1080
#define _DST_WIDTH 1280
#define _DST_HEIGHT 720
#define _SRC_FILE_NAME "1080P.420pyuv"
#define _DST_FILE_NAME "720P.422pyuv"
#define ASSERT(X) if(!(X)){printf("####[%s:%d]assert failed:%s\n", __FUNCTION__, __LINE__, #X);}
int main()
{
//源图像信息
const int dwSrc_Width = _SRC_WIDTH;
const int dwSrc_Height =_SRC_HEIGHT;
enum AVPixelFormat em_Src_Pix_fmt = AV_PIX_FMT_YUV420P;
int dwSrc_YSize = dwSrc_Width * dwSrc_Height;
int dwSrc_UVSize = dwSrc_YSize/4;
//初始化源图AVFrame结构
AVFrame* ptFrameSrc= av_frame_alloc();
int dwSrcPictureSize = avpicture_get_size(em_Src_Pix_fmt, dwSrc_Width, dwSrc_Height);
//上边的只是alloc了一个AVFrame的结构体,真正数据存放在pbySrcBuf中
uint8_t* pbySrcBuf = (uint8_t*)av_malloc(dwSrcPictureSize);
avpicture_fill((AVPicture *)ptFrameSrc, pbySrcBuf, em_Src_Pix_fmt, dwSrc_Width, dwSrc_Height);
FILE* pf_Src = fopen(_SRC_FILE_NAME, "rb");
ASSERT(pf_Src != NULL);
//目的图像信息
const int dwDst_Width = _DST_WIDTH;
const int dwDst_Height = _DST_HEIGHT;
enum AVPixelFormat em_Dst_Pix_fmt = AV_PIX_FMT_RGB24;
int dwDst_YSize = dwDst_Width * dwDst_Height;
int dwDst_UVSize = dwDst_YSize;
//初始化目的图像AVFrame结构
AVFrame* ptFrameDst= av_frame_alloc();
int dwDstPictureSize = avpicture_get_size(em_Dst_Pix_fmt, dwDst_Width, dwDst_Height);
printf("dwDstPictureSize:%d\n", dwDstPictureSize);
uint8_t* pbyDstBuf = (uint8_t*)av_malloc(dwDstPictureSize);
avpicture_fill((AVPicture *)ptFrameDst, pbyDstBuf, em_Dst_Pix_fmt, dwDst_Width, dwDst_Height);
printf("Dst:(w:%d, h:%d), line0:%d, lin1:%d, line2:%d\n",
ptFrameDst->width, ptFrameDst->height,
ptFrameDst->linesize[0], ptFrameDst->linesize[1], ptFrameDst->linesize[2]);
printf("Dst: data0-%p,%p, data1-%p,%p, data2-%p\n",
ptFrameDst->data[0], ptFrameDst->data[0] + dwDst_YSize, ptFrameDst->data[1], ptFrameDst->data[1] + dwDst_YSize/2, ptFrameDst->data[2]);
FILE* pf_Dst = fopen(_DST_FILE_NAME, "wb");
ASSERT(pf_Dst != NULL);
//获取SwsContext
struct SwsContext* ptImgConvertCtx = sws_getContext(dwSrc_Width, dwSrc_Height, em_Src_Pix_fmt, \
dwDst_Width, dwDst_Height, em_Dst_Pix_fmt, SWS_BICUBIC, NULL, NULL, NULL);
ASSERT(ptImgConvertCtx != NULL);
int dwCnt = 0;
for(;;)
{
//一帧AV_PIX_FMT_YUV420P图像的大小dwSrc_YSize*3/2, 而且这里的data[0] data[1] data[2]必定是连续的
ASSERT((ptFrameSrc->data[1]) == (ptFrameSrc->data[0] + dwSrc_YSize));
ASSERT((ptFrameSrc->data[2]) == (ptFrameSrc->data[1] + dwSrc_YSize/4));
int dwRWSize = fread(ptFrameSrc->data[0], 1, dwSrc_YSize, pf_Src);
dwRWSize += fread(ptFrameSrc->data[1], 1, dwSrc_UVSize, pf_Src);
dwRWSize += fread(ptFrameSrc->data[2], 1, dwSrc_UVSize, pf_Src);
if (dwRWSize <=0)
{
if (feof(pf_Src))
{
fclose(pf_Src);
fclose(pf_Dst);
pf_Src = NULL;
pf_Dst = NULL;
av_log(NULL, AV_LOG_INFO, "end of src file\n");
break;
}
}
dwCnt++;
if (!ptFrameSrc->height)
{
//很奇怪的是avpicture_fill仅设置了ptFraemSrc的data和linesize, 并没有设置ptFrameSrc的宽高值,
ptFrameSrc->height = dwSrc_Height;
}
//sws处理
sws_scale(ptImgConvertCtx, (const uint8_t* const*)ptFrameSrc->data, ptFrameSrc->linesize, 0, ptFrameSrc->height,
ptFrameDst->data, ptFrameDst->linesize);
dwRWSize = fwrite(ptFrameDst->data[0], 1, dwDst_YSize*3, pf_Dst);
//dwRWSize = fwrite(ptFrameDst->data[1], 1, dwDst_UVSize, pf_Dst);
//dwRWSize = fwrite(ptFrameDst->data[2], 1, dwDst_UVSize, pf_Dst);
fflush(pf_Dst);
}
av_frame_free(&ptFrameSrc); //记得释放
av_frame_free(&ptFrameDst);
sws_freeContext(ptImgConvertCtx);
}
雷神的一个相关文章给了另外的操作swscale的方式,我这里复制下来,想看原文的可以通过最后的连接看:
#include <stdio.h>
extern "C"
{
#include "libswscale/swscale.h"
#include "libavutil/opt.h"
#include "libavutil/imgutils.h"
}
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;
int src_bpp=av_get_bits_per_pixel(av_pix_fmt_desc_get(src_pixfmt));
FILE *dst_file = fopen("sintel_1280x720_rgb24.rgb", "wb");
const int dst_w=1280,dst_h=720;
AVPixelFormat dst_pixfmt=AV_PIX_FMT_RGB24;
int dst_bpp=av_get_bits_per_pixel(av_pix_fmt_desc_get(dst_pixfmt));
//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
img_convert_ctx =sws_alloc_context();
//Show AVOption
av_opt_show2(img_convert_ctx,stdout,AV_OPT_FLAG_VIDEO_PARAM,0);
//Set Value
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);
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:{
memcpy(src_data[0],temp_buffer,src_w*src_h); //Y
memcpy(src_data[1],temp_buffer+src_w*src_h,src_w*src_h/4); //U
memcpy(src_data[2],temp_buffer+src_w*src_h*5/4,src_w*src_h/4); //V
break;
}
case AV_PIX_FMT_YUV422P:{
memcpy(src_data[0],temp_buffer,src_w*src_h); //Y
memcpy(src_data[1],temp_buffer+src_w*src_h,src_w*src_h/2); //U
memcpy(src_data[2],temp_buffer+src_w*src_h*3/2,src_w*src_h/2); //V
break;
}
case AV_PIX_FMT_YUV444P:{
memcpy(src_data[0],temp_buffer,src_w*src_h); //Y
memcpy(src_data[1],temp_buffer+src_w*src_h,src_w*src_h); //U
memcpy(src_data[2],temp_buffer+src_w*src_h*2,src_w*src_h); //V
break;
}
case AV_PIX_FMT_YUYV422:{
memcpy(src_data[0],temp_buffer,src_w*src_h*2); //Packed
break;
}
case AV_PIX_FMT_RGB24:{
memcpy(src_data[0],temp_buffer,src_w*src_h*3); //Packed
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:{
fwrite(dst_data[0],1,dst_w*dst_h,dst_file); //Y
fwrite(dst_data[1],1,dst_w*dst_h/4,dst_file); //U
fwrite(dst_data[2],1,dst_w*dst_h/4,dst_file); //V
break;
}
case AV_PIX_FMT_YUV422P:{
fwrite(dst_data[0],1,dst_w*dst_h,dst_file); //Y
fwrite(dst_data[1],1,dst_w*dst_h/2,dst_file); //U
fwrite(dst_data[2],1,dst_w*dst_h/2,dst_file); //V
break;
}
case AV_PIX_FMT_YUV444P:{
fwrite(dst_data[0],1,dst_w*dst_h,dst_file); //Y
fwrite(dst_data[1],1,dst_w*dst_h,dst_file); //U
fwrite(dst_data[2],1,dst_w*dst_h,dst_file); //V
break;
}
case AV_PIX_FMT_YUYV422:{
fwrite(dst_data[0],1,dst_w*dst_h*2,dst_file); //Packed
break;
}
case AV_PIX_FMT_RGB24:{
fwrite(dst_data[0],1,dst_w*dst_h*3,dst_file); //Packed
break;
}
default:{
printf("Not Support Output Pixel Format.\n");
break;
}
}
}
sws_freeContext(img_convert_ctx);
free(temp_buffer);
fclose(dst_file);
av_freep(&src_data[0]);
av_freep(&dst_data[0]);
return 0;
}
本文主要参考雷神的三篇文档:
最简单的基于FFmpeg的libswscale的示例(YUV转RGB)