3.基于FFMPEG将YUV420转为H264/H265

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011003120/article/details/81457952

继续ffmpeg的学习之路。。。

看了雷博的YUV转H264代码,理解了一下大致的流程以及逻辑,然后迫不及待的手敲了一遍,然后编译运行,中间遇到了一些问题,便记录一下。

根据前两篇 YUV420转JPG, PCM转AAC,其实大致流程都是一致的,初始化相关结构体—>赋值一些必要的参数—>打开编码器—>读取原始数据—>编码—>写到输出文件中去….

一、一些概念
在赋值一些参数的时候,有些比较重要,单独列出来加以理解。。。
1.time_base
time_base的原型为

/**
 * rational number numerator/denominator
 */
typedef struct AVRational{
    int num; ///< numerator
    int den; ///< denominator
} AVRational;

其中,num可以理解为分子,den可以理解为分母,实际上time_base的意思就是时间的刻度:
如(1,25),那么时间刻度就是1/25
(1,9000),那么时间刻度就是1/90000
在 AVStream AVCodecContext结构中都存在。
详细介绍可参考:https://blog.csdn.net/supermanwg/article/details/14521869

2.bit_rate
目标的码率,即采样码率;采样码率越大,视频大小越大,但是编码质量也会更好

3.gop_size
可以理解为I帧间隔

4.qmin qmax
最小最大量化系数

5.max_b_frames
两个非B帧之间最大的B帧数目。设置为0时,则不使用B帧,当然B帧越多,整个视频的大小就会越小,但是也可能影响其编码实时性。

6.编码参数
一些编码参数是通过AVDictionary的方式来设置的。主要是与编码速度相关的参数,设置如下:

        av_dict_set(&param, "preset", "slow", 0);
        av_dict_set(&param, "tune", "zerolatency", 0);
        av_dict_set(&param, "profile", "main", 0);

其中,preset可设置的选项为
ultrafast,superfast, veryfast, faster, fast, medium, slow, slower, veryslow

按照前两篇 YUV420转JPG, PCM转AAC的代码,YUV420编码为H264/H265也是分为两个方法,第一种就是标准的编码,然后写到AVFormatContext输出文件中去,第二种则是将编码过来的AVPacket里面的数据直接写到文件中去。

另外,在编码H265的时候,如果提示找不到编码器之类的错误,需要安装libx265,参考:PC下安装FFMPEG

二、代码

1.方法1

#define OUT_H264 1
#define OUT_H265 (!OUT_H264)

#define debug_msg(fmt, args ...) printf("--->sq[%s,%d]  " fmt "\n\n", __FUNCTION__, __LINE__, ##args)

int flush_encoder(AVFormatContext * fmt_ctx, unsigned int stream_index)
{
    int ret = 0;
    int got_frame = 0;
    AVPacket enc_pkt;

    if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities & AV_CODEC_CAP_DELAY))
        return 0;

    while(1)
    {
        enc_pkt.data = NULL;
        enc_pkt.size = 0;
        av_init_packet(&enc_pkt);
        ret = avcodec_encode_video2(fmt_ctx->streams[stream_index]->codec, &enc_pkt, NULL, &got_frame);
        if (ret < 0)
            break;
        if (got_frame == 1)
        {
                printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n",enc_pkt.size);

            ret = av_write_frame(fmt_ctx, &enc_pkt);
            if (ret < 0)
                break;
        av_free_packet(&enc_pkt);
        }
    else
    {
            ret = 0;
            break;
    }
    }

    return ret;
}
int YUV_2_H26X_1()
{
    AVFormatContext* pFormatCtx = NULL;
    AVStream* pStream = NULL;
    AVOutputFormat* pFmt = NULL;
    AVCodecContext* pCodecCtx = NULL;
    AVCodec* pCodec = NULL;
    AVPacket pkt;
    AVFrame* pFrame = NULL;

    FILE* inFileFd = fopen("../ds_480x272.yuv", "rb");
    #if OUT_H265
    char outFile[] = "./test.hevc";
    #else
    char outFile[] = "./test.h264";
    #endif

    int ret = 0;
    int videoWidth = 480;
    int videoHeight = 272;
    int picSize = 0;
    unsigned char* picBuff = NULL;
    int ySize = 0;
    int gotPic = 0;

    av_register_all();

    pFormatCtx = avformat_alloc_context();
    pFmt = av_guess_format(NULL, outFile, NULL);
    pFormatCtx->oformat = pFmt;

    if (avio_open(&pFormatCtx->pb, outFile, AVIO_FLAG_READ_WRITE) < 0)
    {
        debug_msg("avio_open failure!\n");
        ret = -1;
        goto ERR_1;
    }

    if (NULL == (pStream = avformat_new_stream(pFormatCtx, NULL)))
    {
        debug_msg("avformat_new_stream failure!\n");
        ret = -1;
        goto ERR_1;
    }
    //pStream->time_base.num = 1;
    //pStream->time_base.den = 25*3600;

    pCodecCtx = pStream->codec;
    pCodecCtx->codec_id = pFmt->video_codec;
    pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
    pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;// PIX_FMT_YUV420P;
    pCodecCtx->width = videoWidth;
    pCodecCtx->height = videoHeight;
    pCodecCtx->time_base.num = 1;
    pCodecCtx->time_base.den = 25;
    pCodecCtx->bit_rate = 400000;
    pCodecCtx->gop_size = 10;
    pCodecCtx->qmin = 10;
    pCodecCtx->qmax = 51;
    pCodecCtx->max_b_frames = 0;

    ySize = pCodecCtx->width * pCodecCtx->height;

    AVDictionary* pDict = NULL;
    if (pCodecCtx->codec_id == AV_CODEC_ID_H264)
    {
        av_dict_set(&pDict, "preset", "slow", 0);
        av_dict_set(&pDict, "tune", "zerolatency", 0);
        av_dict_set(&pDict, "profile", "main", 0);
    }
    else if (pCodecCtx->codec_id == AV_CODEC_ID_H265)
    {
        //av_dict_set(&pDict, "x265-params", "qp=20", 0);  
        //av_dict_set(&pDict, "preset", "ultrafast", 0);  
        // av_dict_set(&pDict, "tune", "zero-latency", 0);  
        av_dict_set(&pDict, "preset", "ultrafast", 0);
        av_dict_set(&pDict, "tune", "zerolatency", 0);
        av_dict_set(&pDict, "profile", "main", 0);
    }

    if ( NULL == (pCodec = avcodec_find_encoder(pCodecCtx->codec_id)))
    {
        debug_msg("avcodec_find_encoder error!\n");
        ret = -1;
        goto ERR_1;
    }

    if ((avcodec_open2(pCodecCtx, pCodec, &pDict)) < 0)
    {
        debug_msg("avcodec_find_encoder error!\n");
        ret = -1;
        goto ERR_1;
    }

    pFrame = av_frame_alloc();
    picSize = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
    picBuff =  (unsigned char *)av_malloc(picSize);
    avpicture_fill((AVPicture *)pFrame, picBuff, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
    pFrame->width = pCodecCtx->width;
    pFrame->height = pCodecCtx->height;
    pFrame->format = pCodecCtx->pix_fmt;

    avformat_write_header(pFormatCtx, NULL);

    av_new_packet(&pkt,picSize);

    debug_msg("----->ysize is %d, picsize is %d\n",ySize*3/2 ,picSize);
    int i = 0;

    pkt.stream_index = pStream->index;
    debug_msg("index is %d\n",pkt.stream_index );

    while(fread(picBuff, 1, ySize*3/2, inFileFd) > 0)
    {
        pFrame->data[0] = picBuff;                     // Y
        pFrame->data[1] = picBuff+ ySize;          // U 
        pFrame->data[2] = picBuff+ ySize*5/4;  // V

        pFrame->pts = i++;

        if (avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &gotPic) < 0)
        {
            debug_msg("avcodec_encode_video2 error!\n");
            ret = -1;
            goto ERR_2;
        }

        if (gotPic == 1)
        {
            av_write_frame(pFormatCtx, &pkt);
            av_free_packet(&pkt);
        }
    }

    if (flush_encoder(pFormatCtx,0) < 0)
    {
        debug_msg("flush_encoder error!\n");
        ret = -1;
        goto ERR_2;
    }

    av_write_trailer(pFormatCtx);

    ERR_2:
    avcodec_close(pCodecCtx);
    av_free(pFrame);
    if (picBuff != NULL)
        av_free(picBuff);

    avio_close(pFormatCtx->pb);
    avformat_free_context(pFormatCtx);
    av_dict_free(&pDict);

    ERR_1:
    fclose(inFileFd);
    return ret;
}

2.方法2

#define OUT_H264 1
#define OUT_H265 (!OUT_H264)

int YUV_2_H26X_2()
{
    AVCodecContext* pCodecCtx = NULL;
    AVCodec * pCodec = NULL;
    AVPacket pkt;
    AVFrame* pFrame = NULL;

    //int cnt = 0;
    int got_frame = 0;
    int ret = -1;
    FILE* out_file = NULL;
    FILE* in_file = NULL;

    int videoWidth = 480;
    int videoHeight = 272;
    int picSize = 0;
    unsigned char* picBuff = NULL;
    int gotPic = 0;
    int ySize = 0;

    in_file = fopen("../ds_480x272.yuv", "rb");
    if (NULL == in_file)
    {
        printf("open in file error!\n");
        ret = -1;
        return ret;
    }

#if OUT_H265
    out_file =  fopen("test.hevc", "wb");
#else
    out_file =  fopen("test.h264", "wb");
#endif
    if (NULL == out_file)
    {
        printf("open out file error!\n");
        goto ERR_3;
    }

    avcodec_register_all();

    pCodecCtx = avcodec_alloc_context3(NULL);
    if (NULL == pCodecCtx)
    {
        printf("alloc AVCodecContext failure!\n");
        ret = -1;
        goto ERR_2;
    }

#if OUT_H265
    pCodecCtx->codec_id = AV_CODEC_ID_H265;
#else
    pCodecCtx->codec_id = AV_CODEC_ID_H264;
#endif
    pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
    pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;// PIX_FMT_YUV420P;
    pCodecCtx->width = videoWidth;
    pCodecCtx->height = videoHeight;
    pCodecCtx->time_base.num = 1;
    pCodecCtx->time_base.den = 25;
    pCodecCtx->bit_rate = 400000;
    pCodecCtx->gop_size = 10;
    pCodecCtx->qmin = 10;
    pCodecCtx->qmax = 51;
    pCodecCtx->max_b_frames = 0;

    ySize = pCodecCtx->width * pCodecCtx->height;

    AVDictionary* pDict = NULL;
    if (pCodecCtx->codec_id == AV_CODEC_ID_H264)
    {
        av_dict_set(&pDict, "preset", "slow", 0);
        av_dict_set(&pDict, "tune", "zerolatency", 0);
        av_dict_set(&pDict, "profile", "main", 0);
    }
    else if (pCodecCtx->codec_id == AV_CODEC_ID_H265)
    {
        //av_dict_set(&pDict, "x265-params", "qp=20", 0);  
        //av_dict_set(&pDict, "preset", "ultrafast", 0);  
        // av_dict_set(&pDict, "tune", "zero-latency", 0);  
        av_dict_set(&pDict, "preset", "ultrafast", 0);
        av_dict_set(&pDict, "tune", "zerolatency", 0);
        av_dict_set(&pDict, "profile", "main", 0);
    }

    pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
    if (NULL == pCodec)
    {
        printf("can not find encoder!\n");
        ret = -1;
        goto ERR_1;
    }

    if (avcodec_open2(pCodecCtx, pCodec, &pDict) < 0)
    {
        printf("open encoder error!\n");
        ret = -1;
        goto ERR_1;
    }

    pFrame = av_frame_alloc();
    picSize = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
    picBuff =  (unsigned char *)av_malloc(picSize);
    avpicture_fill((AVPicture *)pFrame, picBuff, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
    pFrame->width = pCodecCtx->width;
    pFrame->height = pCodecCtx->height;
    pFrame->format = pCodecCtx->pix_fmt;

    av_init_packet(&pkt);
    pkt.data = NULL;
    pkt.size = 0;
    memset(picBuff, 0, picSize);
    int i =0;

    while((fread(picBuff, 1, picSize, in_file)) > 0)
    {
        pFrame->data[0] = picBuff;                     // Y
        pFrame->data[1] = picBuff+ ySize;          // U 
        pFrame->data[2] = picBuff+ ySize*5/4;  // V

        pFrame->pts = i++;
        if (avcodec_encode_audio2(pCodecCtx, &pkt, pFrame, &got_frame) >= 0)
        {
            if (got_frame)
            {
                fwrite(pkt.data, 1, pkt.size, out_file);
                av_free_packet(&pkt);
            }
            else
            {
                continue;
            }
        }
        else
        {
            printf("encode audio error!\n");
            ret = -1;
            goto ERR_1;
        }

        memset(picBuff,0, picSize);
    }

    //fresh
    pkt.data = NULL;
    pkt.size = 0;
    while(1)
    {
        if (avcodec_encode_audio2(pCodecCtx, &pkt, NULL, &got_frame) >= 0)
        {
            if (got_frame)
            {
                fwrite(pkt.data, 1, pkt.size, out_file);
                av_free_packet(&pkt);
            }
            else
            {
                break;
            }           
        }
        else
        {
            printf("fresh encode audio error!\n");
            ret = -1;
            goto ERR_1;
        }
    }

    ERR_1:
    av_freep(&pFrame->data[0]);
    //av_freep(frame_buff);
    av_frame_free(&pFrame);
    avcodec_close(pCodecCtx);
    avcodec_free_context(&pCodecCtx);
    ERR_2:
    fclose(out_file);
    ERR_3:
    fclose(in_file);

    return ret;
}

猜你喜欢

转载自blog.csdn.net/u011003120/article/details/81457952