yuv编码成h264格式写成文件

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;

    //计算出一帧数据的大小 像素格式 * 宽 * 高
    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->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;
}





猜你喜欢

转载自blog.csdn.net/m0_37599645/article/details/112495135
今日推荐