FFmpeg video编码 yuv_h264 (多线程 和profile 定义)

函数说明:
avcodec_find_encoder_by_name:根据指定的编码器名称查找注册的编码器。 

avcodec_alloc_context3:为AVCodecContext分配内存。 avcodec_open2:打开编解码器。 

avcodec_send_frame:将AVFrame⾮压缩数据给编码器。。 

avcodec_receive_packet:获取到编码后的AVPacket数据。 

av_frame_get_buffer: 为⾳频或视频数据分配新的buffer。在调⽤这个函数之前,必须在AVFame上设 置好以下属性:format(视频为像素格式,⾳频为样本格式)nb_samples(样本个数,针对⾳频)channel_layout(通道类型,针对⾳频)、width/height(宽⾼,针对视频)。 


av_frame_make_writable:确保AVFrame是可写的,尽可能避免数据的复制。 如果AVFrame不是是可写的,将分配新的buffer和复制数据。 

av_image_fill_arrays: 存储⼀帧像素数据存储到AVFrame对应的data buffer。

常用参数

char *in_yuv_file = NULL;
    char *out_h264_file = NULL;
    FILE *infile = NULL;
    FILE *outfile = NULL;

    const char *codec_name = NULL;
    const AVCodec *codec = NULL;
    AVCodecContext *codec_ctx= NULL;
    AVFrame *frame = NULL;
    AVPacket *pkt = NULL;

初始化编码器 设定编码参数在后面

codec = avcodec_find_encoder_by_name(codec_name);
codec_ctx = avcodec_alloc_context3(codec);
//设定参数
ret = avcodec_open2(codec_ctx, codec, NULL);

设定具体参数 常规 输入格式为YUV420P

 //set up resolution
    codec_ctx->width = 1280;
    codec_ctx->height = 720;
    //set up time base
    codec_ctx->time_base = (AVRational){1, 25};
    codec_ctx->framerate = (AVRational){25, 1};
    //set i-frame interval
    codec_ctx->gop_size = 25;
    codec_ctx->max_b_frames = 0;
    codec_ctx->pix_fmt= AV_PIX_FMT_YUV420P;
    codec_ctx->bit_rate = 3000000;

如果通过name 获取到的编码器ID 是 AV_CODEC_ID_H264
可以通过下面这三个参数设定编码效率和模式 和编码时间有直接性关系
profile 设计到编码等级越老的可以选择 profile 格式越少

 if (codec->id == AV_CODEC_ID_H264) {
        // 相关的参数可以参考libx264.c的 AVOption options
        // These three paramaters are affect video quality and encoder time
        // ultrafast all encode time:2270ms
        // medium all encode time:5815ms
        // veryslow all encode time:19836ms
        ret = av_opt_set(codec_ctx->priv_data, "preset", "medium", 0);
        if (ret != 0) {
            printf("av_opt_set preset failed\n");
        }
        ret = av_opt_set(codec_ctx->priv_data, "profile", "main", 0); // 默认是high
        if (ret != 0) {
            printf("av_opt_set profile failed\n");
        }
        ret = av_opt_set(codec_ctx->priv_data, "tune", "zerolatency", 0); // 直播是才使用该设置
//        ret = av_opt_set(codec_ctx->priv_data, "tune","film",0); //  画质film
        if (ret != 0) {
            printf("av_opt_set tune failed\n");
        }
    }

在这里插入图片描述

设置多线程编码 设置多线程编码以后送往编码器的数据不会立即编码写入packet 如图 由于IO操作其实段时间解码 也许多线程并不一定快 直播一般不采用

//        codec_ctx->thread_count = 64;  // 开了多线程后也会导致帧输出延迟, 需要缓存thread_count帧后再编程。
//        codec_ctx->thread_type = FF_THREAD_FRAME; // 并 设置为FF_THREAD_FRAME

图一没有开启多线程
在这里插入图片描述
图二开启多线程
在这里插入图片描述
初始化packet 和frame

        pkt = av_packet_alloc();
        frame = av_frame_alloc();
        frame->format = codec_ctx->pix_fmt;
        frame->width = codec_ctx->width;
        frame->height = codec_ctx->height;
        ret = av_frame_get_buffer(frame, 0);

计算和初始化缓冲区

        //Calculate the data of each frame  Align memory by 1
        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);

开始编码
通过上文获取到的数据 从文件中循环 读取数据

 size_t read_bytes = fread(yuv_buf, 1, frame_bytes, infile);

然后将存储一帧图像到AvFrame对应的buffer 中

 int need_size = av_image_fill_arrays(frame->data, frame->linesize, yuv_buf,
                                                 frame->format,
                                                 frame->width, frame->height, 1);

编码
还是一样的 送入编码器 循环取编码后的结果

static int encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt,
                  FILE *outfile)

{
    int ret;

    /* send the frame to the encoder */
    if (frame)
        printf("Send frame %3"PRId64"\n", frame->pts);
    /* 通过查阅代码,使用x264进行编码时,具体缓存帧是在x264源码进行,
     * 不会增加avframe对应buffer的reference*/
    ret = avcodec_send_frame(enc_ctx, frame);
    if (ret < 0)
    {
        fprintf(stderr, "Error sending a frame for encoding\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, "Error encoding audio frame\n");
            return -1;
        }

        if(pkt->flags & AV_PKT_FLAG_KEY)
            printf("Write packet flags:%d pts:%3"PRId64" dts:%3"PRId64" (size:%5d)\n",
                   pkt->flags, pkt->pts, pkt->dts, pkt->size);
        if(!pkt->flags)
            printf("Write packet flags:%d pts:%3"PRId64" dts:%3"PRId64" (size:%5d)\n",
                   pkt->flags, pkt->pts, pkt->dts, pkt->size);
        fwrite(pkt->data, 1, pkt->size, outfile);
    }
    return 0;

}

源码地址:https://github.com/zycccer/aac_mp3_pcm

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值