本文介绍一个如何使用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; i<FrameCount; i++){
-
/* generate synthetic video */
-
fill_yuv_image(srcFrame->data,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;
-
}