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;
}


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com