00001 /*************************************************************************** 00002 *cr 00003 *cr (C) Copyright 1995-2019 The Board of Trustees of the 00004 *cr University of Illinois 00005 *cr All Rights Reserved 00006 *cr 00007 ***************************************************************************/ 00008 00009 /*************************************************************************** 00010 * RCS INFORMATION: 00011 * 00012 * $RCSfile: OptiXDisplayDevice.h 00013 * $Author: johns $ $Locker: $ $State: Exp $ 00014 * $Revision: 1.5 $ $Date: 2019年01月17日 21:21:00 $ 00015 * 00016 *************************************************************************** 00017 * DESCRIPTION: 00018 * VMD interface for NVIDIA GPU hardware video encoding APIs 00019 * 00020 * The implementation here could in principal use either high level 00021 * libraries like NvPipe or GRID, or lower level video encode APIs 00022 * like NvEnc, depending on what hardware the target platform has and 00023 * whether we want to use codecs not supported by certain APIs. 00024 * 00025 * NvPipe: 00026 * https://github.com/NVIDIA/NvPipe/blob/master/README.md 00027 * 00028 ***************************************************************************/ 00029 00030 00031 #include <stdio.h> 00032 #include <stdlib.h> 00033 #include <string.h> 00034 00035 #include "NVENCMgr.h" 00036 #include "Inform.h" 00037 #include "vmddlopen.h" 00038 00039 #include "cuda.h" 00040 00041 #define DBG() printf("NVENCMgr) %s\n", __func__); 00042 00043 typedef NVENCSTATUS (NVENCAPI* PNVENCODEAPICREATEINSTANCE)(NV_ENCODE_API_FUNCTION_LIST *functionList); 00044 00045 class nvencfctns { 00046 PNVENCODEAPICREATEINSTANCE nvEncodeAPICreateInstance; 00047 NV_ENCODE_API_FUNCTION_LIST fctns; 00048 }; 00049 00050 00051 NVENCMgr::NVENCMgr(void) { 00052 DBG(); 00053 00054 nvenc_lib = NULL; 00055 enc_ready = 0; 00056 inbuf_count = 0; 00057 memset(&nvenc_fctns, 0, sizeof(nvenc_fctns)); 00058 00059 memset(&session_parms, 0, sizeof(session_parms)); 00060 session_parms.version = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER; 00061 session_parms.apiVersion = NVENCAPI_VERSION; 00062 00063 memset(&preset_conf, 0, sizeof(preset_conf)); 00064 preset_conf.version = NV_ENC_PRESET_CONFIG_VER; 00065 preset_conf.presetCfg.version = NV_ENC_CONFIG_VER; 00066 00067 memset(&init_parms, 0, sizeof(init_parms)); 00068 init_parms.version = NV_ENC_INITIALIZE_PARAMS_VER; 00069 00070 memset(&conf, 0, sizeof(conf)); 00071 conf.version = NV_ENC_CONFIG_VER; 00072 00073 memset(&create_inbuf, 0, sizeof(create_inbuf)); 00074 create_inbuf.version = NV_ENC_CREATE_INPUT_BUFFER_VER; 00075 00076 memset(&create_outbuf, 0, sizeof(create_outbuf)); 00077 create_outbuf.version = NV_ENC_CREATE_BITSTREAM_BUFFER_VER; 00078 00079 memset(&enc_preset, 0, sizeof(enc_preset)); 00080 } 00081 00082 00083 NVENCMgr::~NVENCMgr(void) { 00084 DBG(); 00085 00086 if (nvenc_lib) 00087 vmddlclose(nvenc_lib); 00088 } 00089 00090 00091 int NVENCMgr::init(void) { 00092 DBG(); 00093 00094 nvenc_lib = vmddlopen("libnvidia-encode.so.1"); 00095 if (!nvenc_lib) { 00096 msgInfo << "NVENCMgr) Failed to open NVENC hardware video encoder library." 00097 << sendmsg; 00098 vmddlclose(nvenc_lib); 00099 return -1; 00100 } 00101 00102 nvenc_fctns.version = NV_ENCODE_API_FUNCTION_LIST_VER; 00103 00104 nvEncodeAPICreateInstance = (PNVENCODEAPICREATEINSTANCE) vmddlsym(nvenc_lib, "NvEncodeAPICreateInstance"); 00105 if (!nvEncodeAPICreateInstance) { 00106 msgInfo << "NVENCMgr) Failed to load NVENC hardware video encoder instance fctn." 00107 << sendmsg; 00108 vmddlclose(nvenc_lib); 00109 return -1; 00110 } 00111 00112 NVENCSTATUS rc; 00113 rc = nvEncodeAPICreateInstance(&nvenc_fctns); 00114 if (rc != NV_ENC_SUCCESS) { 00115 msgInfo << "NVENCMgr) Failed to load NVENC hardware video encoder instance fctn table." 00116 << sendmsg; 00117 } 00118 00119 return 0; 00120 } 00121 00122 00123 int NVENCMgr::open_session(void) { 00124 DBG(); 00125 00126 CUcontext cuctx = NULL; 00127 CUresult curc; 00128 CUcontext cuctxcurrent = NULL; 00129 int cudevcount = 0; 00130 CUdevice cudev = 0; 00131 00132 #if 0 00133 if ((curc = cuInit(0)) != CUDA_SUCCESS) { 00134 printf("NVENCMgr) failed to initialize CUDA driver API\n"); 00135 return -1; 00136 } 00137 #endif 00138 00139 if ((curc = cuDeviceGetCount(&cudevcount)) != CUDA_SUCCESS) { 00140 printf("NVENCMgr) failed to query CUDA driver API device count\n"); 00141 return -1; 00142 } 00143 00144 if (cudevcount < 1) { 00145 printf("NVENCMgr) no CUDA devices found for NVENC video encoding\n"); 00146 return -1; 00147 } else { 00148 printf("NVENCMgr) CUDA dev count: %d\n", cudevcount); 00149 } 00150 00151 if ((curc = cuDeviceGet(&cudev, 0)) != CUDA_SUCCESS) { 00152 printf("NVENCMgr) Unable to bind CUDA device 0\n"); 00153 return -1; 00154 } 00155 00156 if ((curc = cuCtxCreate(&cuctx, 0, cudev)) != CUDA_SUCCESS) { 00157 printf("NVENCMgr) Unable create CUDA ctx\n"); 00158 return -1; 00159 } 00160 00161 if ((curc = cuCtxPopCurrent(&cuctxcurrent)) != CUDA_SUCCESS) { 00162 printf("NVENCMgr) Unable pop current CUDA ctx\n"); 00163 return -1; 00164 } 00165 // curc = cuCtxGetCurrent(&cuctx); 00166 00167 00168 session_parms.device = cuctx; 00169 session_parms.deviceType = NV_ENC_DEVICE_TYPE_CUDA; 00170 // XXX client keys are not presently required 00171 // session_parms.clientKeyPtr = &NV_CLIENT_KEY; 00172 00173 // printf("NVENCMgr) Creating NVENC hardware encoder session...\n"); 00174 00175 NVENCSTATUS encstat; 00176 encstat = nvenc_fctns.nvEncOpenEncodeSessionEx(&session_parms, &nvenc_ctx); 00177 if (encstat != NV_ENC_SUCCESS) { 00178 printf("NVENCMgr) nvEncOpenEncodeSessionEx() returned an error!\n"); 00179 return -1; 00180 } 00181 00182 // setup encoder presets 00183 enc_preset = NV_ENC_PRESET_LOW_LATENCY_DEFAULT_GUID; 00184 // enc_preset = NV_ENC_PRESET_LOW_LATENCY_HP_GUID; 00185 // enc_preset = NV_ENC_PRESET_LOW_LATENCY_HQ_GUID; 00186 // enc_preset = NV_ENC_PRESET_LOSSLESS_DEFAULT_GUID; 00187 // enc_preset = NV_ENC_PRESET_LOSSLESS_HP_GUID; 00188 // enc_preset = NV_ENC_PRESET_HQ_GUID; 00189 // enc_preset = NV_ENC_PRESET_DEFAULT_GUID; 00190 00191 #if 1 00192 codec = NV_ENC_CODEC_H264_GUID; 00193 #else 00194 codec = NV_ENC_CODEC_HEVC_GUID; 00195 #endif 00196 00197 printf("NVENCMgr) establishing NVENC hardware encoder preset config\n"); 00198 encstat = nvenc_fctns.nvEncGetEncodePresetConfig(nvenc_ctx, codec, 00199 enc_preset, &preset_conf); 00200 if (encstat != NV_ENC_SUCCESS) { 00201 printf("NVENCMgr) nvEncGetEncodePresetConfig() returned an error!\n"); 00202 return -1; 00203 } 00204 00205 init_parms.version = NV_ENC_INITIALIZE_PARAMS_VER; 00206 init_parms.encodeWidth = 1920; 00207 init_parms.encodeHeight = 1080; 00208 init_parms.darWidth = 1920; 00209 init_parms.darHeight = 1080; 00210 init_parms.maxEncodeWidth = 1920; 00211 init_parms.maxEncodeHeight = 1080; 00212 init_parms.frameRateNum = 1; 00213 init_parms.frameRateDen = 30; 00214 init_parms.enableEncodeAsync = 0; 00215 init_parms.enablePTD = 1; 00216 00217 init_parms.encodeGUID = codec; 00218 init_parms.presetGUID = enc_preset; 00219 init_parms.encodeConfig = &preset_conf.presetCfg; 00220 00221 // printf("NVENCMgr) Initializing NVENC hardware encoder ...\n"); 00222 encstat = nvenc_fctns.nvEncInitializeEncoder(nvenc_ctx, &init_parms); 00223 if (encstat != NV_ENC_SUCCESS) { 00224 printf("NVENCMgr) nvEncInitializeEncoder() returned an error!\n"); 00225 return -1; 00226 } 00227 00228 #if 0 00229 // setting refs to zero allows hardware encoder to decide 00230 conf.encodeCodecConfig.h264Config.maxNumRefFrames = 0; 00231 conf.encodeCodecConfig.hevcConfig.maxNumRefFramesInDPB = 0; 00232 00233 // infinite GOP == 0, I-only == 1? 00234 conf.gopLength = 30; 00235 if (conf.gopLength > 0) { 00236 conf.frameIntervalP = 3; // 0=I only, 1=IP, 2=IBP, 3=IBBP, ... 00237 } else { 00238 conf.frameIntervalP = 0; // 0=I only, 1=IP, 2=IBP, 3=IBBP, ... 00239 conf.gopLength = 1; 00240 } 00241 00242 conf.encodeCodecConfig.h264Config.idrPeriod = conf.gopLength; 00243 // H.264 only 00244 conf.encodeCodecConfig.h264Config.hierarchicalPFrames = 1; 00245 conf.encodeCodecConfig.h264Config.hierarchicalBFrames = 1; 00246 00247 conf.rcParams.averageBitRate = 15000000; // 15Mbps 00248 conf.rcParams.maxBitRate = 20000000; // 20Mbps 00249 #endif 00250 00251 // flag encoder as ready 00252 enc_ready = 1; 00253 printf("NVENCMgr) NVENC hardware encoder ready.\n"); 00254 00255 return 0; 00256 } 00257 00258 00259 int NVENCMgr::create_inbufs(int bufcount) { 00260 DBG(); 00261 NVENCSTATUS encstat; 00262 00263 if (!enc_ready) { 00264 printf("NVENCMgr) Error creating input buffers, encoder not ready!\n"); 00265 return -1; 00266 } 00267 00268 // must be multiples of 32 00269 create_inbuf.width = 1920; 00270 create_inbuf.height= 1080; 00271 create_inbuf.memoryHeap = NV_ENC_MEMORY_HEAP_SYSMEM_CACHED; 00272 create_inbuf.bufferFmt = NV_ENC_BUFFER_FORMAT_NV12_PL; 00273 00274 inbuf_count = bufcount; 00275 int i; 00276 for (i=0; i<inbuf_count; i++) { 00277 encstat = nvenc_fctns.nvEncCreateInputBuffer(nvenc_ctx, &create_inbuf); 00278 if (encstat != NV_ENC_SUCCESS) { 00279 printf("NVENCMgr) Failed to create input buffers!\n"); 00280 return -1; 00281 } 00282 } 00283 00284 return 0; 00285 } 00286