FFMpeg实现视频的缩放

本次文章主要涉及到对视频的缩放操作,操作本身很简单,涉及到的核心函数有三个:

1、对SwsContext上下文初始化

2、转换操作


3、资源释放


经过上面三步的操作就可以实现一个视频的缩放,如果涉及到图像的滤波,也是上面三个步骤。针对函数的详细解释,

在接下来的代码展示中会写出来。


另外一个不得不说的是函数:

int av_image_alloc(uint8_t *pointers[4], int linesizes[4],int w, int h, enum AVPixelFormat pix_fmt, int align)

其最后一个参数align,表示分配的数据是按多少字节对齐。只所以对齐是为了提高数据的读取效率。FFMPEG源码中

一般都是将这个参数设置为32,即32字节对齐。所以我在此次代码练习中,也将最后转换的YUV数据保存到按32字节对齐

的空间中,而没有考虑到为什么要写为32。最后播放生存的YUV数据是出现的画面如下:


此时YUV数据已经乱了,无法播放了。

最后查资料发现,数据的对齐要根据保存视频的尺寸来设置的。比方说最后缩放的视频尺寸是480X480。

我们知道针对YUV420P的数据Y分量为480,U,V分量为Y分量width的一半即240。如果我们把align设置为32,那么240/32是不能整除的,最后导致数据错位,播放的时候就会不能正常显示画面,而是出现类似上图所示画面。另外该对齐基数align必须是2的n次方。如果我们想32自己对齐,可以设置生存的视频尺寸是32的倍数,比如576x400。我们可以发现576的一半是288,288是可以整除32的。如果我们将align设置为1,那基本是通用的了,但是这样会严重的影响读取效率。最后设置缩放的分辨率为576x400的效果如下:



代码展示如下:

#ifndef _IO_FILE_H_
#define _IO_FILE_H_

typedef struct _IOFile
{
	char *inputName;		/*输入文件名*/
	char *outputName;	        /*输出文件名*/
	char *inputFrameSize;    /*输入图像尺寸*/
	char *outputFrameSize;  /*输出图像尺寸*/

	FILE *iFile;				/*输入文件指针*/
	FILE *oFile;				/*输出文件指针*/
}IOFile;

#endif
#ifndef _FFMPEG_AV_SCALING_H_
#define _FFMPEG_AV_SCALING_H_

#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      

#endif
#ifndef _COMMON_H_
#define _COMMON_H_

#include 
      
      
       
       
#include "ffmpeg_av_scaling.h"
#include "IOFile.h"

typedef int bool;

#define false 0
#define true 1

#endif
#include "common.h"

#define MAX_FRAME_NUM (100)

/*解析输入的参数*/
static bool hello(int argc, char **argv, IOFile *files)
{
	printf("FFMpeg Scaling Demo.\nCommand format: %s input_file input_frame_size output_file output_frame_size\n", argv[0]);
	if (argc != 5)
	{
		printf("Error: command line error, please re-check.\n");
		return false;
	}

	files->inputName = argv[1];
	files->inputFrameSize = argv[2];
	files->outputName = argv[3];
	files->outputFrameSize = argv[4];

	files->iFile=fopen(files->inputName, "rb+");
	if (!files->iFile)
	{
		printf("Error: cannot open input file.\n");
		return false;
	}

	files->oFile=fopen(files->outputName, "wb+");
	if (!files->oFile)
	{
		printf("Error: cannot open output file.\n");
		return false;
	}

	return true;
}

/*************************************************
	Function:		read_yuv_from_ifile
	Description:	从输入文件中读取像素数据
	Calls:			无
	Called By:		main
	Input:			(in)srcWidth : 输入图像的宽度
					(in)srcHeight : 输入图像的的高度
					(in)color_plane :颜色分量:0——Y;1——U;2——V
					(in)files : 包含输入文件的结构
	Output: 		(out)src_data : 保存输入数据的缓存
					(out)src_linesize :
	Return: 		true : 命令行解析正确
					false : 命令行解析错误
*************************************************/
static int read_yuv_from_ifile(unsigned char *src_data[4], int src_linesize[4],int srcWidth,int srcHeight,int color_plane,IOFile *files)
{
	int frame_height = color_plane == 0?srcHeight : srcHeight/2;
	int frame_width = color_plane == 0?srcWidth : srcWidth/2;
	int frame_size = frame_width*frame_height;
	int frame_stride = src_linesize[color_plane];
	int row_idx;
	
	/*
	linesize =  width + padding size(16+16) for YUV
	*/
	if (frame_width == frame_stride)
	{
	 	/*宽度和跨度相等,像素信息是连续存放*/
		fread(src_data[color_plane],frame_size, 1, files->iFile);
	}
	else
	{
		/*宽度小于跨度,像素信息保存空间之间存在间隔*/
		for ( row_idx = 0 ; row_idx < frame_height ; row_idx++ )
		{
		 	fread(src_data[color_plane]+row_idx*frame_stride, frame_width, 1, files->iFile);
		}
		
	}
	
	
	//printf("frame_size:%d\n",frame_size);
	return frame_size;
}

int main(int argc, char **argv)
{
	int ret = 0;
	int srcWidth,srcHeight;
	int dstWidth,dstHeight;
	
	/*解析命令行输入参数*/
	IOFile files = {NULL};

	if ( !hello(argc,argv,&files) )
	{
	 	goto FF_END;
	}

	/*
	根据framesize解析出width和height,比如320x240,解析出
	宽为320,高为240
	*/
	if (av_parse_video_size(&srcWidth,&srcHeight,files.inputFrameSize))
	{
	 	printf("Error:parsing input size failed.\n");
		goto FF_END;
	}

	if (av_parse_video_size(&dstWidth,&dstHeight,files.outputFrameSize))
	{
	 	printf("Error:parsing output size failed.\n");
		goto FF_END;
	}

	/*创建SwsContext结构*/
	enum AVPixelFormat src_pix_fmt = AV_PIX_FMT_YUV420P;
	enum AVPixelFormat dst_pix_fmt = AV_PIX_FMT_YUV420P;	
	/*设置图像缩放上下文信息,函数参数信息如下:
	定义输入图像信息,输出图像信息,SWS_BILINEAR选择的缩放图像算法。(当输入输出图像大小不一样时才有效)
	最后三个NULL的三个参数表示,前两个分别定义输入输出图像滤波器信息,如果不做前后图像滤波,默认为NULL,
	最后一个参数定义了特定缩放算法所需要的参数,默认为NULL
	*/
	printf("srcw:%d,h:%d,dstw:%d,h:%d\n",srcWidth,srcHeight,dstWidth,dstHeight);
	struct SwsContext *sws_ctx = sws_getContext(srcWidth,srcHeight,src_pix_fmt,dstWidth,dstHeight,dst_pix_fmt,SWS_BILINEAR,NULL,NULL,NULL);
	if (!sws_ctx)
	{
	 	printf("Error: allocating SwsContext struct failed.\n");
		goto FF_END;
	}

	/*分配input和output*/
	unsigned char *src_data[4],*dst_data[4];
	int src_linesize[4],dst_linesize[4];

	/*
	根据w,h,pixfmt,分配图像空间,填充srcdata和linesize,最后返回的大小
	对应linesize空间大小
	*/
	if ((ret = av_image_alloc(src_data,src_linesize,srcWidth,srcHeight,src_pix_fmt,32)) < 0)
	{
	 	printf("Error:allocating src image failed.\n");
		goto FF_END;
	}
	
	if ((ret = av_image_alloc(dst_data,dst_linesize,dstWidth,dstHeight,dst_pix_fmt,1)) < 0)
	{
	 	printf("Error:allocating dst image failed.\n");
		goto FF_END;
	}

	/*从输出frame中写出到输出文件*/
	int dst_bufsize = ret;
	int idx;
	for ( idx = 0 ; idx < MAX_FRAME_NUM ; idx++ )
	{
	 	read_yuv_from_ifile(src_data, src_linesize, srcWidth, srcHeight, 0, &files);
		read_yuv_from_ifile(src_data, src_linesize, srcWidth, srcHeight, 1, &files);
		read_yuv_from_ifile(src_data, src_linesize, srcWidth, srcHeight, 2, &files);

		/*转换处理*/
		sws_scale(sws_ctx, (const unsigned char * const *)src_data,src_linesize, 0, srcHeight, dst_data, dst_linesize);

		fwrite(dst_data[0], 1, dst_bufsize, files.oFile);
	}
	printf("Video scaling succeeded.\n");
	
FF_END:
	fclose(files.iFile);
	fclose(files.oFile);
	av_freep(&src_data[0]);
	av_freep(&dst_data[0]);
	sws_freeContext(sws_ctx);
	
	return 0;
}

      
      
     
     
    
    
   
   

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值