您的位置:首页 > 其它

ffmpeg视频编码分析

2016-10-20 10:24 351 查看
使用visual studio 2013 ,首先配置项目环境。下载ffmpeg:win64位:https://ffmpeg.zeranoe.com/builds/win64/。下载share与dev,版本需要一致。



在自己工程项目的.sln文件路径下新建include与lib文件夹,将ffmpeg shared项目的bin目录下.dll拷贝到自己创建的lib中,将dev项目中的lib目录下.lib文件拷贝到自己创建的lib目录中,将dev项目中的include目录下的所有文件拷贝到自己创建的include中并将如下三个头文件拷入include中



设置include与lib的引用





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

详细编码流程如下:

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

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_unref

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

详细编码代码如下:
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ffmpeg 音视频 编解码