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