void NVENCEncoder::init() { NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS stEncodeSessionParams = {0}; NV_ENC_PRESET_CONFIG presetConfig = {0}; GUID clientKey = NV_CLIENT_KEY; CUcontext cuContextCurr; NVENCSTATUS nvStatus = NV_ENC_SUCCESS; int surfaceCount = 0; GUID encoderPreset = NV_ENC_PRESET_HQ_GUID; dontTouchConfig = false; bool is2PassRC = false; String profileString = AppConfig->GetString(TEXT("Video Encoding"), TEXT("X264Profile"), TEXT("high")); String presetString = AppConfig->GetString(TEXT("Video Encoding"), TEXT("NVENCPreset"), TEXT("High Quality")); if (presetString == TEXT("High Performance")) { encoderPreset = NV_ENC_PRESET_HP_GUID; } else if (presetString == TEXT("High Quality")) { encoderPreset = NV_ENC_PRESET_HQ_GUID; } else if (presetString == TEXT("Bluray Disk")) { encoderPreset = NV_ENC_PRESET_BD_GUID; } else if (presetString == TEXT("Low Latency (2pass)")) { encoderPreset = NV_ENC_PRESET_LOW_LATENCY_DEFAULT_GUID; is2PassRC = true; } else if (presetString == TEXT("High Performance Low Latency (2pass)")) { encoderPreset = NV_ENC_PRESET_LOW_LATENCY_HP_GUID; is2PassRC = true; } else if (presetString == TEXT("High Quality Low Latency (2pass)")) { encoderPreset = NV_ENC_PRESET_LOW_LATENCY_HQ_GUID; is2PassRC = true; } else if (presetString == TEXT("Low Latency")) { encoderPreset = NV_ENC_PRESET_LOW_LATENCY_DEFAULT_GUID; } else if (presetString == TEXT("High Performance Low Latency")) { encoderPreset = NV_ENC_PRESET_LOW_LATENCY_HP_GUID; } else if (presetString == TEXT("High Quality Low Latency")) { encoderPreset = NV_ENC_PRESET_LOW_LATENCY_HQ_GUID; } else if (presetString == TEXT("Lossless")) { encoderPreset = NV_ENC_PRESET_LOSSLESS_DEFAULT_GUID; dontTouchConfig = true; } else if (presetString == TEXT("High Performance Lossless")) { encoderPreset = NV_ENC_PRESET_LOSSLESS_HP_GUID; dontTouchConfig = true; } else if (presetString == TEXT("NVDefault")) { encoderPreset = NV_ENC_PRESET_DEFAULT_GUID; } else if (presetString == TEXT("Streaming")) { encoderPreset = NV_ENC_PRESET_STREAMING; clientKey = NV_ENC_KEY_STREAMING; } else if (presetString == TEXT("Streaming (2pass)")) { encoderPreset = NV_ENC_PRESET_STREAMING; is2PassRC = true; clientKey = NV_ENC_KEY_STREAMING; } else { if (height > 1080 || (height == 1080 && fps > 30)) { encoderPreset = NV_ENC_PRESET_HQ_GUID; } if (height > 720 || (height == 720 && fps > 30)) { encoderPreset = NV_ENC_PRESET_LOW_LATENCY_HQ_GUID; } else { encoderPreset = NV_ENC_PRESET_LOW_LATENCY_HQ_GUID; is2PassRC = true; } } TCHAR envClientKey[128] = {0}; DWORD envRes = GetEnvironmentVariable(TEXT("NVENC_KEY"), envClientKey, 128); if (envRes > 0 && envRes <= 128) { if (stringToGuid(envClientKey, &clientKey)) { NvLog(TEXT("Got NVENC key from environment")); } else { NvLog(TEXT("NVENC_KEY environment variable has invalid format")); } } mset(&initEncodeParams, 0, sizeof(NV_ENC_INITIALIZE_PARAMS)); mset(&encodeConfig, 0, sizeof(NV_ENC_CONFIG)); encodeConfig.version = NV_ENC_CONFIG_VER; presetConfig.version = NV_ENC_PRESET_CONFIG_VER; presetConfig.presetCfg.version = NV_ENC_CONFIG_VER; initEncodeParams.version = NV_ENC_INITIALIZE_PARAMS_VER; stEncodeSessionParams.version = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER; stEncodeSessionParams.apiVersion = NVENCAPI_VERSION; stEncodeSessionParams.clientKeyPtr = &clientKey; cuContext = 0; checkCudaErrors(cuCtxCreate(&cuContext, 0, pNvencDevices[iNvencUseDeviceID])); checkCudaErrors(cuCtxPopCurrent(&cuContextCurr)); stEncodeSessionParams.device = (void*)cuContext; stEncodeSessionParams.deviceType = NV_ENC_DEVICE_TYPE_CUDA; nvStatus = pNvEnc->nvEncOpenEncodeSessionEx(&stEncodeSessionParams, &encoder); if (nvStatus != NV_ENC_SUCCESS) { encoder = 0; OBSMessageBox(NULL, Str("Encoder.NVENC.OldDriver"), NULL, MB_OK | MB_ICONERROR); NvLog(TEXT("nvEncOpenEncodeSessionEx failed with 0x%x - outdated driver?"), nvStatus); goto error; } if (!populateEncodePresetGUIDs()) goto error; if (!checkPresetSupport(encoderPreset)) { NvLog(TEXT("Preset is not supported!")); goto error; } nvStatus = pNvEnc->nvEncGetEncodePresetConfig(encoder, NV_ENC_CODEC_H264_GUID, encoderPreset, &presetConfig); if (nvStatus != NV_ENC_SUCCESS) { NvLog(TEXT("nvEncGetEncodePresetConfig failed with 0x%x"), nvStatus); goto error; } initEncodeParams.encodeGUID = NV_ENC_CODEC_H264_GUID; initEncodeParams.encodeHeight = height; initEncodeParams.encodeWidth = width; initEncodeParams.darHeight = height; initEncodeParams.darWidth = width; initEncodeParams.frameRateNum = fps; initEncodeParams.frameRateDen = 1; initEncodeParams.enableEncodeAsync = 0; initEncodeParams.enablePTD = 1; initEncodeParams.presetGUID = encoderPreset; initEncodeParams.encodeConfig = &encodeConfig; memcpy(&encodeConfig, &presetConfig.presetCfg, sizeof(NV_ENC_CONFIG)); encodeConfig.version = NV_ENC_CONFIG_VER; if (!dontTouchConfig) { if (keyint != 0) encodeConfig.gopLength = keyint * fps; if (maxBitRate != -1) { encodeConfig.rcParams.averageBitRate = maxBitRate * 1000; encodeConfig.rcParams.maxBitRate = maxBitRate * 1000; } if (bufferSize != -1) { encodeConfig.rcParams.vbvBufferSize = bufferSize * 1000; encodeConfig.rcParams.vbvInitialDelay = bufferSize * 1000; } if (bUseCBR) { auto filler = AppConfig->GetInt(TEXT("Video Encoding"), TEXT("PadCBR"), 1) != 0; if (is2PassRC) { encodeConfig.rcParams.rateControlMode = filler ? NV_ENC_PARAMS_RC_2_PASS_FRAMESIZE_CAP : NV_ENC_PARAMS_RC_2_PASS_VBR; } else { encodeConfig.rcParams.rateControlMode = filler ? NV_ENC_PARAMS_RC_CBR : NV_ENC_PARAMS_RC_VBR; } encodeConfig.frameIntervalP = 1; encodeConfig.encodeCodecConfig.h264Config.adaptiveTransformMode = NV_ENC_H264_ADAPTIVE_TRANSFORM_ENABLE; encodeConfig.encodeCodecConfig.h264Config.fmoMode = NV_ENC_H264_FMO_DISABLE; } else { encodeConfig.rcParams.rateControlMode = NV_ENC_PARAMS_RC_VBR_MINQP; encodeConfig.rcParams.enableMinQP = 1; encodeConfig.rcParams.minQP.qpInterB = 32 - quality; encodeConfig.rcParams.minQP.qpInterP = 32 - quality; encodeConfig.rcParams.minQP.qpIntra = 32 - quality; encodeConfig.frameIntervalP = 3; } encodeConfig.encodeCodecConfig.h264Config.outputBufferingPeriodSEI = 1; encodeConfig.encodeCodecConfig.h264Config.outputPictureTimingSEI = 1; encodeConfig.encodeCodecConfig.h264Config.disableSPSPPS = 1; encodeConfig.encodeCodecConfig.h264Config.enableVFR = bUseCFR ? 0 : 1; encodeConfig.frameFieldMode = NV_ENC_PARAMS_FRAME_FIELD_MODE_FRAME; if(profileString.CompareI(TEXT("main"))) encodeConfig.profileGUID = NV_ENC_H264_PROFILE_MAIN_GUID; else if(profileString.CompareI(TEXT("high"))) encodeConfig.profileGUID = NV_ENC_H264_PROFILE_HIGH_GUID; encodeConfig.encodeCodecConfig.h264Config.disableSPSPPS = 1; encodeConfig.encodeCodecConfig.h264Config.bdirectMode = encodeConfig.frameIntervalP > 1 ? NV_ENC_H264_BDIRECT_MODE_TEMPORAL : NV_ENC_H264_BDIRECT_MODE_DISABLE; encodeConfig.encodeCodecConfig.h264Config.idrPeriod = encodeConfig.gopLength; encodeConfig.encodeCodecConfig.h264Config.h264VUIParameters.videoSignalTypePresentFlag = 1; encodeConfig.encodeCodecConfig.h264Config.h264VUIParameters.colourDescriptionPresentFlag = 1; encodeConfig.encodeCodecConfig.h264Config.h264VUIParameters.colourMatrix = colorDesc.matrix; encodeConfig.encodeCodecConfig.h264Config.h264VUIParameters.videoFullRangeFlag = colorDesc.fullRange; encodeConfig.encodeCodecConfig.h264Config.h264VUIParameters.videoFormat = 5; encodeConfig.encodeCodecConfig.h264Config.h264VUIParameters.colourPrimaries = colorDesc.primaries; encodeConfig.encodeCodecConfig.h264Config.h264VUIParameters.transferCharacteristics = colorDesc.transfer; } nvStatus = pNvEnc->nvEncInitializeEncoder(encoder, &initEncodeParams); if (nvStatus != NV_ENC_SUCCESS) { NvLog(TEXT("nvEncInitializeEncoder failed with 0x%x"), nvStatus); goto error; } for (surfaceCount = 0; surfaceCount < maxSurfaceCount; ++surfaceCount) { NV_ENC_CREATE_INPUT_BUFFER allocSurf = { 0 }; allocSurf.version = NV_ENC_CREATE_INPUT_BUFFER_VER; allocSurf.width = (width + 31) & ~31; allocSurf.height = (height + 31) & ~31; allocSurf.memoryHeap = NV_ENC_MEMORY_HEAP_SYSMEM_CACHED; allocSurf.bufferFmt = NV_ENC_BUFFER_FORMAT_NV12_PL; nvStatus = pNvEnc->nvEncCreateInputBuffer(encoder, &allocSurf); if (nvStatus != NV_ENC_SUCCESS) { NvLog(TEXT("nvEncCreateInputBuffer #%d failed with 0x%x"), surfaceCount, nvStatus); goto error; } inputSurfaces[surfaceCount].width = allocSurf.width; inputSurfaces[surfaceCount].height = allocSurf.height; inputSurfaces[surfaceCount].locked = false; inputSurfaces[surfaceCount].useCount = 0; inputSurfaces[surfaceCount].inputSurface = allocSurf.inputBuffer; NV_ENC_CREATE_BITSTREAM_BUFFER allocOut = { 0 }; allocOut.version = NV_ENC_CREATE_BITSTREAM_BUFFER_VER; allocOut.size = outBufferSize; allocOut.memoryHeap = NV_ENC_MEMORY_HEAP_SYSMEM_CACHED; nvStatus = pNvEnc->nvEncCreateBitstreamBuffer(encoder, &allocOut); if (nvStatus != NV_ENC_SUCCESS) { NvLog(TEXT("Failed allocating bitstream #%d buffer with 0x%x"), surfaceCount, nvStatus); outputSurfaces[surfaceCount].outputSurface = 0; surfaceCount += 1; goto error; } outputSurfaces[surfaceCount].outputSurface = allocOut.bitstreamBuffer; outputSurfaces[surfaceCount].size = allocOut.size; outputSurfaces[surfaceCount].busy = false; } NvLog(TEXT("------------------------------------------")); NvLog(TEXT("%s"), GetInfoString().Array()); NvLog(TEXT("------------------------------------------")); alive = true; return; error: alive = false; for (int i = 0; i < surfaceCount; ++i) { pNvEnc->nvEncDestroyInputBuffer(encoder, inputSurfaces[i].inputSurface); if (outputSurfaces[i].outputSurface != 0) pNvEnc->nvEncDestroyBitstreamBuffer(encoder, outputSurfaces[i].outputSurface); } surfaceCount = 0; if (encoder) pNvEnc->nvEncDestroyEncoder(encoder); encoder = 0; if (cuContext) cuCtxDestroy(cuContext); }
void NVENCEncoder::init() { NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS stEncodeSessionParams = { 0 }; NV_ENC_PRESET_CONFIG presetConfig = { 0 }; GUID clientKey = NV_CLIENT_KEY; CUcontext cuContextCurr; NVENCSTATUS nvStatus = NV_ENC_SUCCESS; int surfaceCount = 0; GUID encoderPreset = NV_ENC_PRESET_HQ_GUID; int useCustomPreset = AppConfig->GetInt(TEXT("Video Encoding"), TEXT("UseCustomSettings")); if (useCustomPreset) { String presetString = AppConfig->GetString(TEXT("Video Encoding"), TEXT("CustomSettings"), TEXT("default")).MakeLower(); if (presetString == TEXT("hp")) encoderPreset = NV_ENC_PRESET_HP_GUID; else if (presetString == TEXT("hq")) encoderPreset = NV_ENC_PRESET_HQ_GUID; else if (presetString == TEXT("bd")) encoderPreset = NV_ENC_PRESET_BD_GUID; else if (presetString == TEXT("ll")) encoderPreset = NV_ENC_PRESET_LOW_LATENCY_DEFAULT_GUID; else if (presetString == TEXT("llhp")) encoderPreset = NV_ENC_PRESET_LOW_LATENCY_HP_GUID; else if (presetString == TEXT("llhq")) encoderPreset = NV_ENC_PRESET_LOW_LATENCY_HQ_GUID; else encoderPreset = NV_ENC_PRESET_DEFAULT_GUID; } TCHAR envClientKey[128] = { 0 }; DWORD envRes = GetEnvironmentVariable(TEXT("NVENC_KEY"), envClientKey, 128); if (envRes > 0 && envRes <= 128) { if (CLSIDFromString(envClientKey, &clientKey) == NOERROR) NvLog(TEXT("Got NVENC key from environment")); else NvLog(TEXT("NVENC_KEY environment variable has invalid format")); } mset(&initEncodeParams, 0, sizeof(NV_ENC_INITIALIZE_PARAMS)); mset(&encodeConfig, 0, sizeof(NV_ENC_CONFIG)); encodeConfig.version = NV_ENC_CONFIG_VER; presetConfig.version = NV_ENC_PRESET_CONFIG_VER; initEncodeParams.version = NV_ENC_INITIALIZE_PARAMS_VER; stEncodeSessionParams.version = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER; stEncodeSessionParams.apiVersion = NVENCAPI_VERSION; stEncodeSessionParams.clientKeyPtr = &clientKey; cuContext = 0; checkCudaErrors(cuCtxCreate(&cuContext, 0, pNvencDevices[iNvencUseDeviceID])); checkCudaErrors(cuCtxPopCurrent(&cuContextCurr)); stEncodeSessionParams.device = (void*)cuContext; stEncodeSessionParams.deviceType = NV_ENC_DEVICE_TYPE_CUDA; nvStatus = pNvEnc->nvEncOpenEncodeSessionEx(&stEncodeSessionParams, &encoder); if (nvStatus != NV_ENC_SUCCESS) { encoder = 0; NvLog(TEXT("nvEncOpenEncodeSessionEx failed - invalid license key?")); goto error; } if (!populateEncodePresetGUIDs()) goto error; if (!checkPresetSupport(encoderPreset)) { NvLog(TEXT("Preset is not supported!")); goto error; } nvStatus = pNvEnc->nvEncGetEncodePresetConfig(encoder, NV_ENC_CODEC_H264_GUID, encoderPreset, &presetConfig); if (nvStatus != NV_ENC_SUCCESS) { NvLog(TEXT("nvEncGetEncodePresetConfig failed")); goto error; } initEncodeParams.encodeGUID = NV_ENC_CODEC_H264_GUID; initEncodeParams.encodeHeight = height; initEncodeParams.encodeWidth = width; initEncodeParams.darHeight = height; initEncodeParams.darWidth = width; initEncodeParams.frameRateNum = fps; initEncodeParams.frameRateDen = 1; initEncodeParams.enableEncodeAsync = 0; initEncodeParams.enablePTD = 1; initEncodeParams.presetGUID = encoderPreset; initEncodeParams.encodeConfig = &encodeConfig; memcpy(&encodeConfig, &presetConfig.presetCfg, sizeof(NV_ENC_CONFIG)); if (keyint != 0) encodeConfig.gopLength = keyint * fps; if (maxBitRate != -1) { encodeConfig.rcParams.averageBitRate = maxBitRate * 1000; encodeConfig.rcParams.maxBitRate = maxBitRate * 1000; } if (bufferSize != -1) encodeConfig.rcParams.vbvBufferSize = bufferSize * 1000; if (bUseCBR) encodeConfig.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CBR; encodeConfig.encodeCodecConfig.h264Config.enableVFR = bUseCFR ? 0 : 1; encodeConfig.frameFieldMode = NV_ENC_PARAMS_FRAME_FIELD_MODE_FRAME; encodeConfig.encodeCodecConfig.h264Config.bdirectMode = encodeConfig.frameIntervalP > 1 ? NV_ENC_H264_BDIRECT_MODE_TEMPORAL : NV_ENC_H264_BDIRECT_MODE_DISABLE; encodeConfig.encodeCodecConfig.h264Config.idrPeriod = encodeConfig.gopLength; encodeConfig.encodeCodecConfig.h264Config.h264VUIParameters.colourMatrix = colorDesc.matrix; encodeConfig.encodeCodecConfig.h264Config.h264VUIParameters.videoFullRangeFlag = colorDesc.fullRange; encodeConfig.encodeCodecConfig.h264Config.h264VUIParameters.colourPrimaries = colorDesc.primaries; encodeConfig.encodeCodecConfig.h264Config.h264VUIParameters.transferCharacteristics = colorDesc.transfer; nvStatus = pNvEnc->nvEncInitializeEncoder(encoder, &initEncodeParams); if (nvStatus != NV_ENC_SUCCESS) { NvLog(TEXT("nvEncInitializeEncoder failed")); goto error; } for (surfaceCount = 0; surfaceCount < maxSurfaceCount; ++surfaceCount) { NV_ENC_CREATE_INPUT_BUFFER allocSurf = { 0 }; allocSurf.version = NV_ENC_CREATE_INPUT_BUFFER_VER; allocSurf.width = (width + 31) & ~31; allocSurf.height = (height + 31) & ~31; allocSurf.memoryHeap = NV_ENC_MEMORY_HEAP_SYSMEM_CACHED; allocSurf.bufferFmt = NV_ENC_BUFFER_FORMAT_NV12_PL; nvStatus = pNvEnc->nvEncCreateInputBuffer(encoder, &allocSurf); if (nvStatus != NV_ENC_SUCCESS) { NvLog(TEXT("nvEncCreateInputBuffer #%d failed"), surfaceCount); goto error; } inputSurfaces[surfaceCount].width = allocSurf.width; inputSurfaces[surfaceCount].height = allocSurf.height; inputSurfaces[surfaceCount].locked = false; inputSurfaces[surfaceCount].useCount = 0; inputSurfaces[surfaceCount].inputSurface = allocSurf.inputBuffer; NV_ENC_CREATE_BITSTREAM_BUFFER allocOut = { 0 }; allocOut.version = NV_ENC_CREATE_BITSTREAM_BUFFER_VER; allocOut.size = 1024 * 1024; allocOut.memoryHeap = NV_ENC_MEMORY_HEAP_SYSMEM_CACHED; nvStatus = pNvEnc->nvEncCreateBitstreamBuffer(encoder, &allocOut); if (nvStatus != NV_ENC_SUCCESS) { NvLog(TEXT("Failed allocating bitstream #%d buffer"), surfaceCount); outputSurfaces[surfaceCount].outputSurface = 0; surfaceCount += 1; goto error; } outputSurfaces[surfaceCount].outputSurface = allocOut.bitstreamBuffer; outputSurfaces[surfaceCount].size = allocOut.size; outputSurfaces[surfaceCount].busy = false; } NvLog(TEXT("------------------------------------------")); NvLog(TEXT("%s"), GetInfoString().Array()); NvLog(TEXT("------------------------------------------")); alive = true; return; error: alive = false; for (int i = 0; i < surfaceCount; ++i) { pNvEnc->nvEncDestroyInputBuffer(encoder, inputSurfaces[i].inputSurface); if (outputSurfaces[i].outputSurface != 0) pNvEnc->nvEncDestroyBitstreamBuffer(encoder, outputSurfaces[i].outputSurface); } surfaceCount = 0; if (encoder) pNvEnc->nvEncDestroyEncoder(encoder); encoder = 0; if (cuContext) cuCtxDestroy(cuContext); }