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_baseAVRational 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设置就有可能会影响到profileif(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//placeboret = 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;}//分配AVPacketpkt = av_packet_alloc();if(!pkt){fprintf(stderr, "av_packet_alloc() failed!\n");return 0;}//分配AVFrameframe = 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;//计算出一帧数据的大小 像素格式 * 宽 * 高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;}//根据设置的参数将yuv数据填充到frame->data , frame->linesizeint 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;//设置ptsframe->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;
}