本文介绍一个如何使用FFmpeg实现YUV420P的像素数据编码为H.264的压缩编码数据。
项目十分简单,没有多少代码在其中。弄清楚了该项目的代码也就基本弄清楚了FFMPEG的编码流程。
本程序使用的FFmpeg版本为2.2.2(版本较新),开发平台为VC2008(VC2010估计很多人都用不了)。
相关配置已经完成,只需下载源码运行即可。
下面直接上代码:
/*
*将YUV420P进行H264编码压缩
*码术 codemanship
*邮箱: [email protected]
*http://blog.csdn.net/codemanship
*微信公众号: codemanship
*本程序实现了YUV420P的像素数据编码为H.264的压缩编码数据
*是最简单的FFmpeg视频编码方面的教程。
*通过学习本例子可以了解FFmpeg的图片压缩过程。
*/
#ifndef INT64_C
#define INT64_C
#define UINT64_C
#endif
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavutil/imgutils.h"
#include "libavutil/parseutils.h"
#include "libswscale/swscale.h"
#include "libavformat/avformat.h"
};
#pragma comment(lib,"avcodec.lib")
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"swscale.lib")
#pragma comment(lib,"avutil.lib")
#define FrameCount 50
static void fill_yuv_image(uint8_t *data[4], int linesize[4],
int width, int height, int frame_index)
{
int x, y;
/* Y */
for (y = 0; y < height; y++)
for (x = 0; x < width; x++)
data[0][y * linesize[0] + x] = x + y + frame_index * 3;
/* Cb and Cr */
for (y = 0; y < height / 2; y++) {
for (x = 0; x < width / 2; x++) {
data[1][y * linesize[1] + x] = 128 + y + frame_index * 2;
data[2][y * linesize[2] + x] = 64 + x + frame_index * 5;
}
}
}
int main(int argc, char **argv)
{
AVFormatContext* pFormatCtx;
AVOutputFormat* fmt;
AVStream* video_st;
AVCodecContext* pCodecCtx;
AVCodec* pCodec;
AVFrame* srcFrame;
int size;
int width=640,height=480;
const char* outfilename = "out_640x480.h264";
av_register_all();
pFormatCtx = avformat_alloc_context();
fmt = av_guess_format(NULL, outfilename, NULL);
pFormatCtx->oformat = fmt;
if (avio_open(&pFormatCtx->pb,outfilename, AVIO_FLAG_READ_WRITE) < 0)
{
printf("open file failed.");
exit(1);
}
video_st = av_new_stream(pFormatCtx, 0);
if (video_st==NULL)
{
exit(1);
}
pCodecCtx = video_st->codec;
pCodecCtx->codec_id = fmt->video_codec;
pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
pCodecCtx->pix_fmt = PIX_FMT_YUV420P;
pCodecCtx->width = width;
pCodecCtx->height = height;
pCodecCtx->time_base.num = 1;
pCodecCtx->time_base.den = 25;
pCodecCtx->bit_rate = 40000;
pCodecCtx->gop_size=10;
pCodecCtx->qmin = 10;
pCodecCtx->qmax = 30;
//输出格式信息
av_dump_format(pFormatCtx, 0, outfilename, 1);
pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
if (!pCodec)
{
fprintf(stderr, "Codec not found\n");
exit(1);
}
//设置X264编码的参数,减小延迟
AVDictionary *opts = NULL;
av_dict_set(&opts, "profile", "baseline", 0);
av_dict_set(&opts, "preset", "fast", 0);
av_dict_set(&opts, "tune", "zerolatency", 0);
if (avcodec_open2(pCodecCtx, pCodec,&opts) < 0)
{
fprintf(stderr, "Could not open codec\n");
exit(1);
}
srcFrame = av_frame_alloc();
size = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
/* allocate source and destination image buffers */
if ((av_image_alloc(srcFrame->data, srcFrame->linesize,
width, height, PIX_FMT_YUV420P, 16)) < 0) {
fprintf(stderr, "Could not allocate source image\n");
exit(1);
}
//写文件头
avformat_write_header(pFormatCtx,NULL);
AVPacket pkt;
int y_size = pCodecCtx->width * pCodecCtx->height;
av_new_packet(&pkt,y_size*3);
for (int i=0; idata,srcFrame->linesize,width,height,i);
/*设置pts*/
srcFrame->pts=i;
int got_picture=0;
/*编码*/
int ret = avcodec_encode_video2(pCodecCtx, &pkt,srcFrame, &got_picture);
if(ret < 0)
{
fprintf(stderr, "Error encoding frame\n");
return -1;
}
if (got_picture==1)
{
printf("Write frame %3d (size=%5d)\n", i, pkt.size);
pkt.stream_index = video_st->index;
ret = av_write_frame(pFormatCtx, &pkt);
av_free_packet(&pkt);
}
}
//写文件尾
av_write_trailer(pFormatCtx);
//清理
if (video_st)
{
avcodec_close(video_st->codec);
av_freep(srcFrame);
}
avio_close(pFormatCtx->pb);
avformat_free_context(pFormatCtx);
return 0;
}
程序截图
编码前YUV数据编码后H264码流