ffmpeg视频编码分析

使用visual studio 2013 ,首先配置项目环境。下载ffmpeg:win64位:https://ffmpeg.zeranoe.com/builds/win64/。下载share与dev,版本须要一致。html



在本身工程项目的.sln文件路径下新建include与lib文件夹,将ffmpeg shared项目的bin目录下.dll拷贝到本身建立的lib中,将dev项目中的lib目录下.lib文件拷贝到本身建立的lib目录中,将dev项目中的include目录下的全部文件拷贝到本身建立的include中并将以下三个头文件拷入include中缓存


设置include与lib的引用ide




最后将须要用到的.dll文件拷贝到生成.exe的目录下,至此环境搭建完成。函数


详细编码流程以下:工具

1.首先解析、处理输入参数,如编码器的参数、图像的参数、输入输出文件;ui

2.创建整个FFMpeg编码器的各类组件工具,顺序依次为:avcodec_register_all -> avcodec_find_encoder ->avcodec_alloc_context3 -> avcodec_open2 -> av_frame_alloc -> av_image_alloc;编码

3.编码循环:av_init_packet -> avcodec_encode_video2(两次) -> av_packet_unrefspa

4.关闭编码器组件:avcodec_close,av_free,av_freep,av_frame_free.net


详细编码代码以下:

1:定义错误码头文件Error.h命令行

#ifndef _ERROR_H_
#define _ERROR_H_

#define IO_FILE_ERROR_OPEN_FAIL -1
#define FF_ERROR_INITIALIZATION_FAILED -10
#define FF_ERROR_ENCODING_FAILED -11

#endif


2:main函数代码与注释

#include "stdio.h"
#include "stdlib.h"
#include "stdint.h"
#include "Error.h"


extern "C"{
	#include "libavutil/imgutils.h"
	#include "libavutil/samplefmt.h"
	#include "libavformat/avformat.h"
	#include "libavutil/opt.h"
}
//文件输入路径
const char *inputFileName = NULL;
//文件输出路径
const char *outPutFileName = NULL;
//输入输出文件指针
FILE *pFin = NULL, *pFout = NULL;
//输入的视频文件宽高
int frameWidth = 0, frameHeight = 0;
//编码的比特率和编码帧数
int bitRate = 0, frameToCode = 0;

//编码器对象
AVCodec *codec = NULL;
//编码器对象的上下文环境
AVCodecContext *codecCtx = NULL;

AVFrame *frame = NULL;
AVPacket pkt;

/**
*从命令行中解析出命令行参数
*/
static int parse_input_paramaters(int argc, char **argv){
	inputFileName = argv[1];
	outPutFileName = argv[2];
	fopen_s(&pFin,inputFileName,"rb+");
	if (!pFin){
		return IO_FILE_ERROR_OPEN_FAIL;
	}
	fopen_s(&pFout, outPutFileName, "wb+");
	if (!pFout){
		return IO_FILE_ERROR_OPEN_FAIL;
	}
	frameWidth = atoi(argv[3]);
	frameHeight = atoi(argv[4]);
	bitRate = atoi(argv[5]);
	frameToCode = atoi(argv[6]);

	return 1;
}

/**
*从命令行中解析出命令行参数
*YUV_420_P格式的数据
*/
static int read_yuv_data(int color){
	//color = 0; y份量 亮度份量 占4份
	//color = 1; u份量 色度份量 占2份
	//color = 2; v份量 色度份量 占2份

	int color_width = color == 0 ? frameWidth : frameWidth / 2;
	int color_height = color == 0 ? frameHeight : frameHeight / 2;
	int color_size = color_width * color_height;
	int color_stride = frame->linesize[color];

	//因为视频边缘可能有填充像素,因此frame的width与linesize可能不相等
	if (color_width == color_stride){
		//frame的width与linesize相等时像素数据是连续的
		fread_s(frame->data[color], color_size, 1, color_size, pFin);

	}
	else
	{
		//frame的width与linesize不相等时候读取像素数据
		for (int row_idx = 0; row_idx < color_height; row_idx++){
			fread_s(frame->data[color] + row_idx * color_stride, color_width, 1, color_width, pFin);

		}
	}
	return color_size;
}

int main(int argc, char **argv){
	int got_packet = 0;

	if (parse_input_paramaters(argc, argv) > 0){
		printf("InputFile: %s\nOutPutFile: %s\nFile:Frame resolution: (%d x %d)\nBitrate: %d\nFrame num to code: %d\n"
			,inputFileName,outPutFileName,frameWidth,frameHeight,bitRate,frameToCode);
	}
	else{
		printf("Error:connamd line error\n");
		return -1;
	}
	{
		//注册须要的ffmpeg编解码组件
		avcodec_register_all();

		//查找编解码器
		codec = avcodec_find_encoder(AV_CODEC_ID_H264);
		if (!codec){
			return FF_ERROR_INITIALIZATION_FAILED;
		}

		//分配AVCodecContext
		codecCtx = avcodec_alloc_context3(codec);
		if (!codecCtx){
			return FF_ERROR_INITIALIZATION_FAILED;
		}

		//设置编码参数
		codecCtx->width = frameWidth;
		codecCtx->height = frameHeight;
		codecCtx->bit_rate = bitRate;
		AVRational r = { 1, 25 };
		codecCtx->time_base = r;
		codecCtx->gop_size = 12;
		codecCtx->max_b_frames = 1;
		codecCtx->pix_fmt = AV_PIX_FMT_YUV420P;

		//av_opt_set(codecCtx->priv_data,"preset","superfast",0);
		av_opt_set(codecCtx->priv_data, "tune", "zerolatency", 0);

		//打开编码器
		if(avcodec_open2(codecCtx, codec, NULL) < 0){
			return FF_ERROR_INITIALIZATION_FAILED;
		}
		//分配AVFrame以及像素存储空间
		frame = av_frame_alloc();
		if (!frame){
			return FF_ERROR_INITIALIZATION_FAILED;
		}
		frame->width = codecCtx->width;
		frame->height = codecCtx->height;
		frame->format = codecCtx->pix_fmt;

		if (av_image_alloc(frame->data, frame->linesize, frame->width, frame->height, (AVPixelFormat)frame->format, 32) < 0){
			return FF_ERROR_INITIALIZATION_FAILED;
		}
	}

	for (int frameIdx = 0; frameIdx < frameToCode; frameIdx++)
	{
		//初始化AVPacket
		av_init_packet(&pkt);
		pkt.data = NULL;
		pkt.size = 0;

		//读取像素数据到AVFrame中
		read_yuv_data(0);
		read_yuv_data(1);
		read_yuv_data(2);

		//设置视频的显示时间戳
		frame->pts = frameIdx;

		//开始编码
		if (avcodec_encode_video2(codecCtx,&pkt,frame,&got_packet) < 0){
			printf("Error: encoding failed!");
			return FF_ERROR_ENCODING_FAILED;
		}

		//将编码后的数据写入输出文件中
		if (got_packet){
			printf("write packet of frame %d,size = %d\n",frameIdx,pkt.size);
			fwrite(pkt.data,1,pkt.size,pFout);
			av_packet_unref(&pkt);
		}
	}
	//在编码器中可能还有一些缓存数据须要进行编码
	for (got_packet = 1; got_packet;)
	{
		//编码
		if (avcodec_encode_video2(codecCtx, &pkt, NULL, &got_packet) < 0){
			printf("Error: encoding failed!");
			return FF_ERROR_ENCODING_FAILED;
		}

		if (got_packet){
			printf("write cached packet,size = %d\n", pkt.size);
			fwrite(pkt.data, 1, pkt.size, pFout);
			av_packet_unref(&pkt);
		}
	}

	//收尾工做,清理内存
	fclose(pFin);
	fclose(pFout);
	avcodec_close(codecCtx);
	av_free(codecCtx);
	av_freep(&frame->data[0]);
	av_frame_free(&frame);

	return 0;
}

最终将yuv格式数据编码为h264格式。


备注:博客内容来源于《FFMpeg视频开发与应用基础——使用FFMpeg工具与SDK》 

视频地址:http://edu.csdn.net/course/detail/2515