视频编码(H264编码)

基本步骤:

  1、打开编码器

void open_codecer(int width, int heigth,AVCodecContext **enc_ctx){
    
    int ret = 0 ;
    AVCodec *codec = NULL;
    codec = avcodec_find_decoder_by_name("libx264");
    if (!codec) {
        printf("codec libx264 not found\n");
        exit(1);
    }
    
    *enc_ctx = avcodec_alloc_context3(codec);
    if (!enc_ctx) {
        printf("could not alloc video codec context\n");
        exit(1);
    }
    //SPS/PPS
    (*enc_ctx)->profile = FF_PROFILE_H264_HIGH_444;
    (*enc_ctx)->level = 50; //表示LEVEL5.0
    
    //设置分辨率
    (*enc_ctx)->width = width;//宽
    (*enc_ctx)->height = heigth;//高
    
    //设置GOP(分组)
    (*enc_ctx)->gop_size = 25;
    (*enc_ctx)->keyint_min = 3; //最小插入I帧的间隔(可选)
    
    //设置B帧的数量
    (*enc_ctx)->max_b_frames = 3;// 一般不超过3帧(可选)
    (*enc_ctx)->has_b_frames = 1;// (可选)
    
    //参考帧数量
    (*enc_ctx)->refs = 3; // 参考帧数量(可选)
    
    //设置输入YUV格式
    (*enc_ctx)->pix_fmt = AV_PIX_FMT_YUV420P;
    
    //设置码率
    (*enc_ctx)->bit_rate = 600*1000; // 600kbps
    
    //设置帧率
    (*enc_ctx)->time_base = (AVRational){1,25};//帧与帧之间的间隔
    (*enc_ctx)->framerate = (AVRational){25,1};//帧率,每秒25帧
    
    // 打卡编码器
    ret = avcodec_open2((*enc_ctx), codec, NULL);
    if (ret<0) {
        printf("failed to open codecer: %s!\n ",av_err2str(ret));
        exit(1);
    }
}

        2、转换NV12到YUV420,这是因为FFmpeg的x264编码器只支持YUV420

while (ret=av_read_frame(ps, &pkt)==0) {

        //YYYYYYYYUVUV NV12
        //YYYYYYYYUUVV YUV420
        memcpy(fram->data[0], pkt->size, 307200);
        for (i=0; i<307200/4; i++) {
            fram->data[1][i] = pkt->data[307200+i*2];
            fram->data[2][i] = pkt->data[307201+i*2];
        }
        //把YUV写入文件
        fwrite(fram->data[0], 1, 307200, yuvoutfile);
        fwrite(fram->data[1], 1, 307200/4, yuvoutfile);
        fwrite(fram->data[2], 1, 307200/4, yuvoutfile);
        av_packet_unref(pkt);
    }

        3、准备编码数据AVFrame

        创建frame:

 static AVFrame* create_frame(int width, int height){
     int ret = 0;
     AVFrame* frame = NULL;
     frame = av_frame_alloc();
     if (!frame) {
         printf("");
     }
     //设置参数
     frame->width = width;
     frame->height = height;
     frame->format = AV_PIX_FMT_YUV420P;
     //获取frame
     ret = av_frame_get_buffer(frame, 32); //按32位对齐
     if (ret<0) {
         printf("");
     }
     return frame;
}

 创建AVPacket:

AVPacket* pck = av_packet_alloc();

4、H264编码

     avcodec_send_frame(<#AVCodecContext *avctx#>, <#const AVFrame *frame#>)

    avcodec_receive_packet(<#AVCodecContext *avctx#>, <#AVPacket *avpkt#>)

static void encoder(AVCodecContext *enc_ctx,AVFrame *frame,
                    AVPacket *newpkt,FILE *outfile){
    int ret = 0;
    // 送原始数据到编码器进行编码
    ret = avcodec_send_frame(enc_ctx, frame);
    if (ret<0) {
        printf("");
    }
    // 从编码器获取编码好的数据
    if (ret>=0) {
        ret = avcodec_receive_packet(enc_ctx, newpkt);
        // 如果编码器数据不足时,返回EAGAIN,或者到数据尾时返回AVERROE_EOF
        if (ret==AVERROR(EAGAIN)||ret ==AVERROR_EOF) {
            return;
        }else{
            printf("");
            exit(1);
        }
        fwrite(newpkt->data, 1, newpkt->size, outfile);
        av_packet_unref(newpkt);
    }
}

注意:

        1、编码器中有未吐出的数据,需要再次调用encoder(AVCodecContext *enc_ctx,NULL,
                    AVPacket *newpkt,FILE *outfile)编码函数,frame传NULL。

        2、对于输入的编码器函数的参数frame,我们需要设置frame->pts,int base = 0 frame->pts=base++;这样我们的视频才会按照顺序播放,否则会花屏。

你可能感兴趣的:(FFmpeg,C/C++,音视频)