您的位置:首页 > 其它

纯净版基于FFMPEG编码器(YUV到H264)

2016-06-29 11:45 441 查看
现在正在学FFMPEG相关知识,写此文的目的就是便于自己对一些函数接口的理解,也希望对同样学习FFMPEG的人有一些帮助。代码不是自己写的,主要是在原代码的基础上增加了一些注释。

#include

#include
#include
#include
#include

const char *inputFilename=NULL;
const char *outputFilename=NULL;

FILE *pFin=NULL,*pFout=NULL;

int frameWidth=0,frameHeight=0;
int bitRate =0,frameToEncode=0;

AVCodec *codec = NULL;//编解码器
AVCodecContext *codecCtx=NULL;//编码器的上下文
AVFrame *frame=NULL;
AVPacket pkt; //编码之后的码流

static int read_yuv_data(int color);

static int parse_input_paramaters(int argc, char **argv)
{
//inputFilename = argv[1];
//outputFilename = argv[2];
inputFilename = "./ds_480x272.yuv";
outputFilename = "./ds1.h264";

pFin=fopen(inputFilename,"rb+");
if ( !pFin )
{
return -1;
}

pFout=fopen(outputFilename,"wb+");
if ( !pFin )
{
return -1;
}

/*获取视频宽高*/
//frameWidth=atoi(argv[3]);
//frameHeight=atoi(argv[4]);
//bitRate=atoi(argv[5]);
frameWidth=480;
frameHeight=272;
bitRate=400000;

/*要编码的帧数*/
//frameToEncode=atoi(argv[6]);
frameToEncode=100;

return 1;
}

int main(int argc, char **argv)
{
int got_packet=0;
int frameIdx;
int color_0;
int color_1;
int color_2;
int y_size;
int ret;
int framecnt=0;

if ( parse_input_paramaters(argc,argv) > 0 )
{
printf("Input file:%s\nOutput file:%s\n",inputFilename,outputFilename);
printf("Frame resolution:(%d x %d)\nBit rate:%d\nFrame num to code:%d\n",frameWidth,frameHeight,bitRate,frameToEncode);
}
else
{
printf("Error: command line put error!\n");
}

/*注册编解码器组件*/
avcodec_register_all();

//查找AVCodec编解码器
codec=avcodec_find_encoder(AV_CODEC_ID_H264);
if ( !codec )
{
//查找失败
return -1;
}

//分配AVCodecContext实列
codecCtx=avcodec_alloc_context3( codec);
if ( !codecCtx )
{
//查找失败
return -11;
}

//设置编码器参数
codecCtx->width = frameWidth;
codecCtx->height = frameHeight;
codecCtx->bit_rate = bitRate;
//codecCtx->bit_rate = 400000;
AVRational r = {1,25};
codecCtx->time_base = r;
//codecCtx->gop_size = 12; //连续画面组大小
codecCtx->gop_size = 10; //连续画面组大小

//两个非B帧之间允许出现多少个B帧数
//设置0表示不使用B帧
//B帧越多,图片越小
codecCtx->max_b_frames = 1;
codecCtx->pix_fmt = AV_PIX_FMT_YUV420P;

//设置私有参数
av_opt_set(codecCtx->priv_data,"preset","slow",0);

//打开编码器
if(avcodec_open2(codecCtx, codec, NULL)< 0)
{
return -11;
}

//分配AVFrame以及像素存储空间
frame=av_frame_alloc();
if ( !frame )
{
return -11;
}

frame->width = codecCtx->width;
frame->height = codecCtx->height;
frame->format = codecCtx->pix_fmt;

//分配avframe包含的像素空间
/*
分配像素的存储空间,内存以16字节对齐
*/
if(av_image_alloc(frame->data,frame->linesize,frame->width,frame->height,codecCtx->pix_fmt,16) < 0)
{
printf("ERROR:avimag alloc fail!\n");
return -11;
}

y_size = codecCtx->width * codecCtx->height;

/*编码过程*/
for ( frameIdx = 0 ; frameIdx < frameToEncode; frameIdx++ )
{
//初始化以后avpacket就可以装载编码之后的码流
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;

//读取数据到AVFrame
//将三个颜色分量读取到AVFrame中
color_0=read_yuv_data(0);
color_1=read_yuv_data(1);
color_2=read_yuv_data(2);
printf("Color0 size:%d,Colort1:%d,Color2:%d\n",color_0,color_1,color_2);

//这一帧被显示的时间
frame->pts = frameIdx;
printf("The frame pts:%d\n",frameIdx);
//编码
//将AVFrame编码成avpack
//got_packet表示是否完成编码出一个完整的数据包
if ( avcodec_encode_video2(codecCtx, &pkt, frame, &got_packet) < 0)
{
printf("Error: encoding failed\n");
return -2;
}

printf("Got packet flag:%d\n",got_packet);

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  ; )
{
//第三个参数设置为NULL就不会从frame中读取数据,而是编码自身内部的缓存数据
if ( avcodec_encode_video2(codecCtx, &pkt, NULL, &got_packet) < 0)
{
printf("Error: encoding failed\n");
return -2;
}

printf("Read data from cache got_packet:%d,line:%d\n",got_packet,__LINE__);

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

/*释放frame中保存的像素*/
av_freep(&frame->data[0]);

/*释放frame本身*/
av_frame_free(&frame);

return 0;
}

static int read_yuv_data(int color)
{
//color = 0 表示Y分量
//color = 1 表示U分量
//color = 2 表示V分量
int row_idx;
int color_height = color == 0 ? frameHeight:frameHeight/2;
int color_width = color == 0 ? frameWidth:frameWidth/2;

//颜色空间大小
int color_size = color_height*color_width;

//各分量图像缓存的宽度,一般linesize是大于图像width的
int color_stride = frame->linesize[color];

//往frame里面读取数据
if ( color_width == color_stride )
{
//表示颜色分量在AVFrame中是连续存放
fread(frame->data[color], color_size, 1, pFin);
}
else
{
//表示颜色分量不是连续存放,内存空间的边缘有填充的数据
//数据不能直接读取了,要按行,一行一行的读取
for ( row_idx = 0 ; row_idx < color_height ; row_idx++ )
{
fread(frame->data[color]+row_idx*color_stride, color_width, 1, pFin);
}
}

return color_size;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ffmpeg h264 yuv 编码