【FFmpeg学习笔记005】 libx264和FFmpeg不同方式(YUV编码为H.264)

21 篇文章 24 订阅 ¥9.90 ¥99.00
本文介绍了如何使用libx264进行H.264编码,详细讲解了libx264的主要函数,包括x264_param_default、x264_picture_alloc等。此外,还探讨了FFmpeg如何整合libx264进行编码,涉及avcodec_open2、avcodec_encode_video2等关键函数。通过示例代码展示了从YUV到H.264编码的完整过程。
摘要由CSDN通过智能技术生成

x264官网:http://www.videolan.org/developers/x264.html
libx264编译:http://blog.csdn.net/leixiaohua1020/article/details/42069383
首先直接调用libx264完成编码(YUV编码为H.264)
主要函数详解:
x264_param_default():设置参数集结构体x264_param_t的缺省值。
x264_picture_alloc():为图像结构体x264_picture_t分配内存。
x264_encoder_open():打开编码器。
x264_encoder_encode():编码一帧图像。
x264_encoder_close():关闭编码器。
x264_picture_clean():释放x264_picture_alloc()申请的资源。
存储数据的结构体如下所示。
x264_picture_t:存储压缩编码前的像素数据。
x264_nal_t:存储压缩编码后的码流数据。
话不多说,上代码:

#include <stdio.h>
#include <stdlib.h>

#include "stdint.h"

#if defined ( __cplusplus)
extern "C"
{
#include "x264.h"
};
#else
#include "x264.h"
#endif


int main(int argc, char** argv)
{

    int ret;
    int y_size;
    int i,j;

    //FILE* fp_src  = fopen("../cuc_ieschool_640x360_yuv444p.yuv", "rb");
    FILE* fp_src  = fopen("../cuc_ieschool_640x360_yuv420p.yuv", "rb");

    FILE* fp_dst = fopen("cuc_ieschool.h264", "wb");

    //Encode 50 frame
    //if set 0, encode all frame
    int frame_num = 50;
    int csp=X264_CSP_I420;
    int width=640,height=360;

    int iNal   = 0;
    x264_nal_t* pNals = NULL;
    x264_t* pHandle   = NULL;
    x264_picture_t* pPic_in = (x264_picture_t*)malloc(sizeof(x264_picture_t));
    x264_picture_t* pPic_out = (x264_picture_t*)malloc(sizeof(x264_picture_t));
    x264_param_t* pParam = (x264_param_t*)malloc(sizeof(x264_param_t));

    //Check
    if(fp_src==NULL||fp_dst==NULL){
        printf("Error open files.\n");
        return -1;
    }

    x264_param_default(pParam);
    pParam->i_width   = width; 
    pParam->i_height  = height;
    /*
    //Param
    pParam->i_log_level  = X264_LOG_DEBUG;
    pParam->i_threads  = X264_SYNC_LOOKAHEAD_AUTO;
    pParam->i_frame_total = 0;
    pParam->i_keyint_max = 10;
    pParam->i_bframe  = 5;
    pParam->b_open_gop  = 0;
    pParam->i_bframe_pyramid = 0;
    pParam->rc.i_qp_constant=0;
    pParam->rc.i_qp_max=0;
    pParam->rc.i_qp_min=0;
    pParam->i_bframe_adaptive = X264_B_ADAPT_TRELLIS;
    pParam->i_fps_den  = 1; 
    pParam->i_fps_num  = 25;
    pParam->i_timebase_den = pParam->i_fps_num;
    pParam->i_timebase_num = pParam->i_fps_den;
    */
    pParam->i_csp=csp;
    x264_param_apply_profile(pParam, x264_profile_names[5]);

    pHandle = x264_encoder_open(pParam);

    x264_picture_init(pPic_out);
    x264_picture_alloc(pPic_in, csp, pParam->i_width, pParam->i_height);

    //ret = x264_encoder_headers(pHandle, &pNals, &iNal);

    y_size = pParam->i_width * pParam->i_height;
    //detect frame number
    if(frame_num==0){
        fseek(fp_src,0,SEEK_END);
        switch(csp){
        case X264_CSP_I444:frame_num=ftell(fp_src)/(y_size*3);break;
        case X264_CSP_I420:frame_num=ftell(fp_src)/(y_size*3/2);break;
        default:printf("Colorspace Not Support.\n");return -1;
        }
        fseek(fp_src,0,SEEK_SET);
    }

    //Loop to Encode
    for( i=0;i<frame_num;i++){
        switch(csp){
        case X264_CSP_I444:{
            fread(pPic_in->img.plane[0],y_size,1,fp_src);   //Y
            fread(pPic_in->img.plane[1],y_size,1,fp_src);   //U
            fread(pPic_in->img.plane[2],y_size,1,fp_src);   //V
            break;}
        case X264_CSP_I420:{
            fread(pPic_in->img.plane[0],y_size,1,fp_src);   //Y
            fread(pPic_in->img.plane[1],y_size/4,1,fp_src); //U
            fread(pPic_in->img.plane[2],y_size/4,1,fp_src); //V
            break;}
        default:{
            printf("Colorspace Not Support.\n");
            return -1;}
        }
        pPic_in->i_pts = i;

        ret = x264_encoder_encode(pHandle, &pNals, &iNal, pPic_in, pPic_out);
        if (ret< 0){
            printf("Error.\n");
            return -1;
        }

        printf("Succeed encode frame: %5d\n",i);

        for ( j = 0; j < iNal; ++j){
             fwrite(pNals[j].p_payload, 1, pNals[j].i_payload, fp_dst);
        }
    }
    i=0;
    //flush encoder
    while(1){
        ret = x264_encoder_encode(pHandle, &pNals, &iNal, NULL, pPic_out);
        if(ret==0){
            break;
        }
        printf("Flush 1 frame.\n");
        for (j = 0; j < iNal; ++j){
            fwrite(pNals[j].p_payload, 1, pNals[j].i_payload, fp_dst);
        }
        i++;
    }
    x264_picture_clean(pPic_in);
    x264_encoder_close(pHandle);
    pHandle = NULL;

    free(pPic_in);
    free(pPic_out);
    free(pParam);

    fclose(fp_src);
    fclose(fp_dst);

    return 0;
}

功能强大的FFmpeg整合了很多编码器, 不仅可以编码H.264的视频,而且可以编码MPEG4/MPEG2/VP8等等各种FFmpeg支持的视频。当然编码的流程是相同的,只是需要设置不同的编码参数而已。FFmpeg也调用libx264完成编码 H.264的工作。
主要函数详解:
av_register_all():注册FFmpeg所有编解码器。
avformat_alloc_output_context2():初始化输出码流的AVFormatContext。
avio_open():打开输出文件。
av_new_stream():创建输出码流的AVStream。
avcodec_find_encoder():查找编码器。
avcodec_open2():打开编码器。
avformat_write_header():写文件头(对于某些没有文件头的封装格式,不需要此函数。比如说MPEG2TS)。
avcodec_encode_video2():编码一帧视频。即将AVFrame(存储YUV像素数据)编码为AVPacket(存储H.264等格式的码流数据)。
av_write_frame():将编码后的视频码流写入文件。
flush_encoder():输入的像素数据读取完成后调用此函数。用于输出编码器中剩余的AVPacket。
av_write_trailer():写文件尾(对于某些没有文件头的封装格式,不需要此函数。比如说MPEG2TS)。
代码:

#include <stdio.h>

#define __STDC_CONSTANT_MACROS

#ifdef _WIN32
//Windows
extern "C"
{
#include "libavutil/opt.h"
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#ifdef __cplusplus
};
#endif
#endif

int flush_encoder(AVFormatContext *fmt_ctx,unsigned int stream_index){
    int ret;
    int got_frame;
    AVPacket enc_pkt;
    if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities &
        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);
        av_frame_free(NULL);
        if (ret < 0)
            break;
        if (!got_frame){
            ret=0;
            break;
        }
        printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n",enc_pkt.size);
        /* mux encoded frame */
        ret = av_write_frame(fmt_ctx, &enc_pkt);
        if (ret < 0)
            break;
    }
    return ret;
}

int main(int argc, char* argv[])
{
    AVFormatContext* pFormatCtx;
    AVOutputFormat* fmt;
    AVStream* video_st;
    AVCodecContext* pCodecCtx;
    AVCodec* pCodec;
    AVPacket pkt;
    uint8_t* picture_buf;
    AVFrame* pFrame;
    int picture_size;
    int y_size;
    int framecnt=0;
    //FILE *in_file = fopen("src01_480x272.yuv", "rb"); //Input raw YUV data 
    FILE *in_file = fopen("../cuc_ieschool_640x360_yuv420p.yuv", "rb");   //Input raw YUV data
    int in_w=640,in_h=360;                              //Input data's width and height
    int framenum=100;                                   //Frames to encode
    //const char* out_file = "src01.h264";              //Output Filepath 
    //const char* out_file = "src01.ts";
    //const char* out_file = "src01.hevc";
    const char* out_file = "ds.h264";

    av_register_all();
    //Method1.
    pFormatCtx = avformat_alloc_context();
    //Guess Format
    fmt = av_guess_format(NULL, out_file, NULL);
    pFormatCtx->oformat = fmt;

    //Method 2.
    //avformat_alloc_output_context2(&pFormatCtx, NULL, NULL, out_file);
    //fmt = pFormatCtx->oformat;


    //Open output URL
    if (avio_open(&pFormatCtx->pb,out_file, AVIO_FLAG_READ_WRITE) < 0){
        printf("Failed to open output file! \n");
        return -1;
    }

    video_st = avformat_new_stream(pFormatCtx, 0);
    video_st->time_base.num = 1; 
    video_st->time_base.den = 25;  

    if (video_st==NULL){
        return -1;
    }
    //Param that must set
    pCodecCtx = video_st->codec;
    //pCodecCtx->codec_id =AV_CODEC_ID_HEVC;
    pCodecCtx->codec_id = fmt->video_codec;
    pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
    pCodecCtx->pix_fmt = PIX_FMT_YUV420P;
    pCodecCtx->width = in_w;  
    pCodecCtx->height = in_h;
    pCodecCtx->time_base.num = 1;  
    pCodecCtx->time_base.den = 25;  
    pCodecCtx->bit_rate = 400000;  
    pCodecCtx->gop_size=250;
    //H264
    //pCodecCtx->me_range = 16;
    //pCodecCtx->max_qdiff = 4;
    //pCodecCtx->qcompress = 0.6;
    pCodecCtx->qmin = 10;
    pCodecCtx->qmax = 51;

    //Optional Param
    pCodecCtx->max_b_frames=3;

    // Set Option
    AVDictionary *param = 0;
    //H.264
    if(pCodecCtx->codec_id == AV_CODEC_ID_H264) {
        av_dict_set(&param, "preset", "slow", 0);
        av_dict_set(&param, "tune", "zerolatency", 0);
        //av_dict_set(&param, "profile", "main", 0);
    }
    //H.265
    if(pCodecCtx->codec_id == AV_CODEC_ID_H265){
        av_dict_set(&param, "preset", "ultrafast", 0);
        av_dict_set(&param, "tune", "zero-latency", 0);
    }

    //Show some Information
    av_dump_format(pFormatCtx, 0, out_file, 1);

    pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
    if (!pCodec){
        printf("Can not find encoder! \n");
        return -1;
    }
    if (avcodec_open2(pCodecCtx, pCodec,&param) < 0){
        printf("Failed to open encoder! \n");
        return -1;
    }


    pFrame = av_frame_alloc();
    picture_size = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
    picture_buf = (uint8_t *)av_malloc(picture_size);
    avpicture_fill((AVPicture *)pFrame, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);

    //Write File Header
    avformat_write_header(pFormatCtx,NULL);

    av_new_packet(&pkt,picture_size);

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

    for (int i=0; i<framenum; i++){
        //Read raw YUV data
        if (fread(picture_buf, 1, y_size*3/2, in_file) <= 0){
            printf("Failed to read raw data! \n");
            return -1;
        }else if(feof(in_file)){
            break;
        }
        pFrame->data[0] = picture_buf;              // Y
        pFrame->data[1] = picture_buf+ y_size;      // U 
        pFrame->data[2] = picture_buf+ y_size*5/4;  // V
        //PTS
        pFrame->pts=i;
        int got_picture=0;
        //Encode
        int ret = avcodec_encode_video2(pCodecCtx, &pkt,pFrame, &got_picture);
        if(ret < 0){
            printf("Failed to encode! \n");
            return -1;
        }
        if (got_picture==1){
            printf("Succeed to encode frame: %5d\tsize:%5d\n",framecnt,pkt.size);
            framecnt++;
            pkt.stream_index = video_st->index;
            ret = av_write_frame(pFormatCtx, &pkt);
            av_free_packet(&pkt);
        }
    }
    //Flush Encoder
    int ret = flush_encoder(pFormatCtx,0);
    if (ret < 0) {
        printf("Flushing encoder failed\n");
        return -1;
    }

    //Write file trailer
    av_write_trailer(pFormatCtx);

    //Clean
    if (video_st){
        avcodec_close(video_st->codec);
        av_free(pFrame);
        av_free(picture_buf);
    }
    avio_close(pFormatCtx->pb);
    avformat_free_context(pFormatCtx);

    fclose(in_file);

    return 0;
}

Reference:
http://blog.csdn.net/leixiaohua1020/article/details/42078645
http://blog.csdn.net/leixiaohua1020/article/details/25430425

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: FFmpeg是一款流媒体处理的工具,支持多种视频编码格式,其中也包括YUV编码。H.265是一种高效的视频编码格式,可以将视频文件的大小压缩至原来的一半。 要将YUV编码的视频文件转换为H.265编码,需要进行以下步骤: 1. 通过FFmpeg获取原始视频的YUV数据。 2. 将YUV数据进行处理,将其转换为H.265编码格式。此处需要使用x265编码器,以实现高质量的视频编码。 3. 将处理后的H.265编码数据转化为保存为视频文件。 在使用FFmpeg进行YUV编码为H.265的操作时,需要注意以下几点: 1. YUV数据的格式应该符合编码器的要求,否则将无法进行编码。 2. 编码参数的选择对于编码质量和压缩率都有很大影响,需要注意对应的参数设置。 3. H.265编码是比较耗时的操作,需要足够的计算机性能支持。 通过上述步骤,我们可以将YUV格式的视频文件转换为高效的H.265格式,实现更好的视频质量和更小的视频文件大小。 ### 回答2: FFmpeg是一个跨平台的音视频处理库,其中也包括了对YUV数据的处理和编码功能。而H.265是一种高效的视频编码标准,能够提供更好的视频质量和更小的文件大小。因此,将YUV数据编码为H.265对于提高视频编码的效率和质量非常重要。 在FFmpeg中,可以使用x265编码器来将YUV数据编码为H.265。首先,需要将YUV数据载到FFmpeg中,并设置相应的编码器参数。然后,使用x265编码器对YUV数据进行压缩编码,并输出为H.265视频文件格式。 具体步骤如下: 1. 使用FFmpegYUV数据,可以通过命令行输入以下命令: ffmpeg -s:v widthxheight -pix_fmt yuv420p -i input.yuv 其中,width和height分别表示YUV数据的宽度和高度,input.yuvYUV数据的文件名。 2. 设置编码器参数,可以通过命令行指定编码器的参数,例如: ffmpeg -c:v libx265 -preset medium -x265-params keyint=60 -b:v 2M output.mp4 其中,-c:v表示指定使用x265编码器,-preset medium表示设置为中等压缩质量,-x265-params keyint=60表示设置关键帧间隔为60,-b:v 2M表示设置输出视频的比特率为2M,output.mp4表示输出为H.265视频文件。 3. 进行YUV编码,可以使用以下命令实现: ffmpeg -i input.yuv -c:v libx265 -preset medium -x265-params keyint=60 -b:v 2M output.mp4 其中,-i input.yuv表示输入YUV数据文件,-c:v libx265表示指定使用x265编码器进行编码,后续参数同上。 通过以上步骤,就可以将YUV数据编码为H.265格式的视频文件,从而利用H.265标准的高效性能优势来提高视频编码的效率和质量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值