yuv编码成h264格式写成文件
(使用ffmpeg 编码yuv420p编码成h264格式)
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <libavcodec/avcodec.h>
#include <libavutil/time.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>
static int encode(AVCodecContext* enc_ctx, AVFrame* frame, AVPacket* pkt, FILE* outfile)
{
int ret;
//发送一帧进行编码
ret = avcodec_send_frame(enc_ctx, frame);
if(ret < 0)
{
fprintf(stderr, "avcodec_send_frame() failed!\n");
return -1;
}
while (ret >= 0)
{
//获取编码后的数据
ret = avcodec_receive_packet(enc_ctx, pkt);
if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
return 0;
}
else if(ret < 0)
{
fprintf(stderr, "avcodec_receive_packet() failed!\n");
return -1;
}
//写入文件
fwrite(pkt->data, 1, pkt->size, outfile);
}
}
int main()
{
printf("Hello video encoder!\n");
char* in_yuv_file = "test_yuv420p_1280x720.yuv";
char* out_h264_file = "test_420p_1280x720.h264";
FILE* infile = NULL;
FILE* outfile = NULL;
const char* codec_name = "libx264";
const AVCodec* codec = NULL;
AVCodecContext* codec_ctx = NULL;
AVFrame* frame = NULL;
AVPacket* pkt = NULL;
int ret = 0;
//查找指定的编码器
codec = avcodec_find_encoder_by_name(codec_name);
if(!codec)
{
fprintf(stderr, "avcodec_find_encoder_by_name() failed!\n");
return 0;
}
//分配编码器上下文
codec_ctx = avcodec_alloc_context3(codec);
if(!codec_ctx)
{
fprintf(stderr, "avcodec_alloc_context3() failed!\n");
return 0;
}
//设置分辨率
codec_ctx->width = 1280;
codec_ctx->height = 720;
//设置time_base
AVRational time_base = {1, 25};
AVRational framerate = {25, 1};
codec_ctx->time_base = time_base;
codec_ctx->framerate = framerate;
//设置I帧间隔(GOP size)
codec_ctx->gop_size = 25;
//planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
//YYYY....UU....VV....
codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
//设置一些参数
//这些参数可能会相互影响的,preset设置就有可能会影响到profile
if(codec->id == AV_CODEC_ID_H264)
{
//h264的参数
// baseline profile:基本画质。支持I/P 帧,只支持无交错(Progressive)和CAVLC;
//extended profile:进阶画质。支持I/P/B/SP/SI 帧,只支持无交错(Progressive)和CAVLC;
//main profile:主流画质。提供I/P/B 帧,支持无交错(Progressive)和交错(Interlaced),也支持
//CAVLC 和CABAC 的支持;
//high profile:高级画质。在main Profile 的基础上增加了8x8内部预测、自定义量化、 无损视频编码
//和更多的YUV 格式;
ret = av_opt_set(codec_ctx->priv_data, "profile", "main", 0);
if(ret != 0)
{
sprintf(stderr,"av_opt_set() profile = main failed!\n");
}
//x264编码器下的参数
//编码速度和压缩率之间做出1个权衡
//ultrafast
//superfast
//veryfast
//faster
//fast
//medium – default preset
//slow
//slower
//veryslow
//placebo
ret = av_opt_set(codec_ctx->priv_data, "preset", "medium", 0);
if(ret != 0)
{
sprintf(stderr, "av_opt_set() preset = medium failed!\n");
}
//x264编码器下的参数
//film:电影类型,对视频的质量非常严格时使用该选项
//animation:动画片,压缩的视频是动画片时使用该选项
//grain:颗粒物很重,该选项适用于颗粒感很重的视频
//stillimage:静态图像,该选项主要用于静止画面比较多的视频
//psnr:提高psnr,该选项编码出来的视频psnr比较高
//ssim:提高ssim,该选项编码出来的视频ssim比较高
//fastdecode:快速解码,该选项有利于快速解码
//zerolatency:零延迟,该选项主要用于视频直播
ret = av_opt_set(codec_ctx->priv_data, "tune", "zerolatency", 0);
if(ret != 0)
{
sprintf(stderr, "av_opt_set() tune = zerolatency failed!\n");
}
}
//码率
codec_ctx->bit_rate = 3000000;
//将codec_ctx 和codec关联
ret = avcodec_open2(codec_ctx, codec, NULL);
if(ret < 0)
{
fprintf(stderr, "avcodec_open2() failed!\n");
return 0;
}
//打开输入文件 和 输出文件
infile = fopen(in_yuv_file, "rb");
if(!infile)
{
fprintf(stderr, "fopen() in_yuv_file failed!\n");
return 0;
}
outfile = fopen(out_h264_file, "wb");
if(!outfile)
{
fprintf(stderr, "fopen() out_h264_file failed!\n");
return 0;
}
//分配AVPacket
pkt = av_packet_alloc();
if(!pkt)
{
fprintf(stderr, "av_packet_alloc() failed!\n");
return 0;
}
//分配AVFrame
frame = av_frame_alloc();
if(!frame)
{
fprintf(stderr, "av_frame_alloc() failed!\n");
return 0;
}
frame->width = codec_ctx->width;
frame->height = codec_ctx->height;
frame->format = codec_ctx->pix_fmt;
//根据设置的参数分配空间
ret = av_frame_get_buffer(frame, 0);
if(ret < 0)
{
fprintf(stderr, "av_frame_get_buffer() failed!\n");
return 0;
}
//计算出一帧数据的大小 像素格式 * 宽 * 高
int frame_bytes = av_image_get_buffer_size(frame->format, frame->width,
frame->height, 1);
printf("frame_bytes = %d\n", frame_bytes);
uint8_t* yuv_buf = (uint8_t*)malloc(frame_bytes);
if(!yuv_buf)
{
printf("yuv_buf malloc() failed!\n");
return 0;
}
int64_t pts = 0;
while (1)
{
//从文件读一帧数据
memset(yuv_buf, 0, frame_bytes);
size_t read_bytes = fread(yuv_buf, 1, frame_bytes, infile);
if(read_bytes <= 0)
{
fprintf(stderr, "fread end!\n");
break;
}
ret = av_frame_make_writable(frame);
if(ret != 0)
{
fprintf(stderr, "av_frame_make_writable() failed!\n");
break;
}
//根据设置的参数将yuv数据填充到frame->data , frame->linesize
int fill_size = av_image_fill_arrays(frame->data, frame->linesize, yuv_buf,
frame->format, frame->width, frame->height, 1);
if(fill_size != frame_bytes)
{
fprintf(stderr, "av_image_fill_arrays failed!\n");
break;
}
pts += 40;
//设置pts
frame->pts = pts;
ret = encode(codec_ctx, frame, pkt, outfile);
if(ret < 0)
{
fprintf(stderr, "encode failed!\n");
break;
}
}
//冲刷编码器
encode(codec_ctx, NULL, pkt, outfile);
fclose(infile);
fclose(outfile);
if(yuv_buf)
{
free(yuv_buf);
}
av_frame_free(&frame);
av_packet_free(&pkt);
avcodec_free_context(&codec_ctx);
printf("video encoder end!\n");
return 0;
}