Ejemplo n.º 1
0
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);
}
Ejemplo n.º 2
0
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);
}