ffmpeg-nvenc

来源:互联网 发布:图片去码软件 编辑:程序博客网 时间:2024/04/30 15:34

nvenc.c是一个用nvidia来执行的代码,下面简单分析nvenc编码的基本流程,
主要关注与nvenc底层相关的接口调用,nvenc底层代码的关键调用如下图所示的8个步骤

AVCodec ff_nvenc_h264_encoder = {    .name           = "nvenc_h264",    .long_name      = NULL_IF_CONFIG_SMALL("NVIDIA NVENC H.264 encoder"),    .type           = AVMEDIA_TYPE_VIDEO,    .id             = AV_CODEC_ID_H264,    /*关键的三个接口函数*/    .init           = nvenc_old_init,    .encode2        = ff_nvenc_encode_frame,    .close          = ff_nvenc_encode_close,    .priv_data_size = sizeof(NvencContext),    .priv_class     = &nvenc_h264_class,    .defaults       = defaults,    .capabilities   = AV_CODEC_CAP_DELAY,    .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP,    .pix_fmts       = ff_nvenc_pix_fmts,};#endif

nvenc关键步骤说明

主要是分析 ff_nvenc_encode_frame和ff_nvenc_encode_init

ff_nvenc_encode_init关键部分

    /*加载函数库     *关键err = nvenc_create_instance(&dl_fn->nvenc_funcs);     */    if ((ret = nvenc_load_libraries(avctx)) < 0)        return ret;    if ((ret = nvenc_setup_device(avctx)) < 0)        return ret;    if ((ret = nvenc_setup_encoder(avctx)) < 0)        return ret;    if ((ret = nvenc_setup_surfaces(avctx)) < 0)        return ret;

1.nvenc_load_libraries
加载函数库,经过这样的处理后就可以使用nvenc底层的函数接口

err = nvenc_create_instance(&dl_fn->nvenc_funcs);//关键调用

2.nvenc_setup_device
获取GPU使用资源(可以理解成要想使用nvidia,需要获取GPU的使用权限),打开段,检测设备兼容性等性能

这个函数的流程会有两个分支,一个是从显卡(显存)传来的数据,另一个是从CPU传来的数据(内存)。这两个流程的区别就是
假设数据从内存传来,为了开启显卡,需要获取显卡的相关资源,因此需要调用一系列获取显卡信息资源的接口,这类接口主要有:

(dl_fn->cu_init(0))                            dl_fn->cu_device_get_count(&nb_devices) //获取设备数量                           cu_res =dl_fn->cu_device_get(&cu_device, idx);//获取idx指定的设备入口                           cu_res =dl_fn->cu_ctx_create(&ctx->cu_context_internal, 0, cu_device);//在这个设备创建相关资源                            ctx->cu_context =ctx->cu_context_internal; //ctx->cu_context会在被open_session使用

假设数据是从显存传过来,代表之前已经用显卡做了解码相关的操作,显卡相关的信息资源就可以直接获取到了

        frames_ctx   = (AVHWFramesContext*)avctx->hw_frames_ctx->data;        device_hwctx = frames_ctx->device_ctx->hwctx;        ctx->cu_context = device_hwctx->cuda_ctx;

这个函数里面有两个重要的接口调用nvenc_open_session和nvenc_check_capabilities

  `nvenc_open_session`   ---------必须有这个操作,后续的接口调用操作才能成功
   ret = p_nvenc->nvEncOpenEncodeSessionEx(&params, &ctx->nvencoder);  //关键调用

nvenc_check_capabilities ————-支持的编码参数检测

3.nvenc_setup_encoder—-设置编码参数

摘取与nvenc底层相关的关键代码:static av_cold int nvenc_setup_encoder(AVCodecContext *avctx){  ....      /*这里涉及到-preset的设置*/    nvenc_map_preset(ctx);    nv_status = nv_status = p_nvenc->nvEncGetEncodePresetConfig(ctx->nvencoder,                                                    ctx->init_encode_params.encodeGUID,                                                    ctx->init_encode_params.presetGUID,                                                    &preset_config);....         /*码率控制设置,大多数nvenc的参数的设置都和码率控制相关*/                                                        nvenc_setup_rate_control(avctx);...........  /*设置h264和hevc的编码参数,level等级*/    res = nvenc_setup_codec_config(avctx);.....        /*初始化编码器*/    nv_status = p_nvenc->nvEncInitializeEncoder(ctx->nvencoder, &ctx->init_encode_params);}

4.nvenc_setup_surfaces ——–分配必要显存的地方

底层库关键调用             nv_status = p_nvenc->nvEncCreateInputBuffer(ctx->nvencoder, &allocSurf);    -----分配输入                 nv_status = p_nvenc->nvEncCreateBitstreamBuffer(ctx->nvencoder, &allocOut);   -----分配输出

ff_nvenc_encode_frame的简单分析:

{ ....    inSurf = get_free_frame(ctx);//从获取空余空间        if (!inSurf) {            av_log(avctx, AV_LOG_ERROR, "No free surfaces\n");            return AVERROR_BUG;        }        /* 把解码之后的数据加载进来        *nvenc_upload_frame两个分支        */        res = nvenc_upload_frame(avctx, frame, inSurf);        if (res) {            inSurf->lockCount = 0;            return res;        }  .......        nv_status = p_nvenc->nvEncEncodePicture(ctx->nvencoder, &pic_params);//开始编码    .....   /*接下来就是编码数据的读写操作   *数据放到surface --fifo   *读写fifo   *av_fifo_generic_read   *av_fifo_generic_write   *处理数据,变成pkt---process_output_surface   */  ...... }

nvenc_upload_frame两个分支:
如果数据是从显存传来

          ret = p_nvenc->nvEncRegisterResource(ctx->nvencoder, &reg);                        nv_status = p_nvenc->nvEncMapInputResource(ctx->nvencoder, &nvenc_frame->in_map);
   如果数据从内存传过来
       v_status = p_nvenc->nvEncLockInputBuffer(ctx->nvencoder, &lockBufferParams);                 nv_status = p_nvenc->nvEncUnlockInputBuffer(ctx->nvencoder, nvenc_frame->input_surface);
0 0