Example #1
0
bool NVENCEncoder::populateEncodePresetGUIDs()
{
    uint32_t count = 0;
    NVENCSTATUS nvStatus = pNvEnc->nvEncGetEncodePresetCount(encoder, NV_ENC_CODEC_H264_GUID, &count);
    if (nvStatus != NV_ENC_SUCCESS)
    {
        NvLog(TEXT("Failed getting preset guid list count: 0x%x"), nvStatus);
        return false;
    }

    encodePresetGUIDs.SetSize(count);

    nvStatus = pNvEnc->nvEncGetEncodePresetGUIDs(encoder, NV_ENC_CODEC_H264_GUID, encodePresetGUIDs.Array(), encodePresetGUIDs.Num(), &count);
    if (nvStatus != NV_ENC_SUCCESS)
    {
        NvLog(TEXT("Failed getting preset guid list: 0x%x"), nvStatus);
        return false;
    }

    encodePresetGUIDs.SetSize(count);

    NvLog(TEXT("NVENC supports %d h264 presets"), count);

    return true;
}
Example #2
0
bool initNvEnc()
{
    if (pNvEnc != 0)
        return true;

    if (!checkNvEnc())
        return false;

    nvEncLib = LoadLibrary(encodeLibName);

    if(nvEncLib == 0)
    {
        NvLog(TEXT("Failed to load %s"), encodeLibName);
        goto error;
    }

    PNVENCODEAPICREATEINSTANCE nvEncodeAPICreateInstance =
        (PNVENCODEAPICREATEINSTANCE)GetProcAddress(nvEncLib, "NvEncodeAPICreateInstance");

    if(nvEncodeAPICreateInstance == 0)
    {
        NvLog(TEXT("Failed to load nvenc entrypoint"));
        goto error;
    }

    pNvEnc = new NV_ENCODE_API_FUNCTION_LIST;
    assert(pNvEnc);

    mset(pNvEnc, 0, sizeof(NV_ENCODE_API_FUNCTION_LIST));
    pNvEnc->version = NV_ENCODE_API_FUNCTION_LIST_VER;
    NVENCSTATUS status = nvEncodeAPICreateInstance(pNvEnc);

    if (status != NV_ENC_SUCCESS)
    {
        NvLog(TEXT("Failed to get nvenc instance"));
        goto error;
    }

    NvLog(TEXT("NVENC internal init finished successfully"));

    return true;

error:

    iNvencDeviceCount = 0;

    delete pNvEnc;
    pNvEnc = 0;

    FreeLibrary(nvEncLib);
    nvEncLib = 0;

    NvLog(TEXT("NVENC internal init failed"));

    return false;
}
Example #3
0
NVENCEncoder::~NVENCEncoder()
{
    if (alive)
    {
        for (int i = 0; i < maxSurfaceCount; ++i)
        {
            if (inputSurfaces[i].locked)
                pNvEnc->nvEncUnlockInputBuffer(encoder, inputSurfaces[i].inputSurface);

            pNvEnc->nvEncDestroyInputBuffer(encoder, inputSurfaces[i].inputSurface);
            pNvEnc->nvEncDestroyBitstreamBuffer(encoder, outputSurfaces[i].outputSurface);
        }

        pNvEnc->nvEncDestroyEncoder(encoder);
        cuCtxDestroy(cuContext);

        NvLog(TEXT("Encoder closed"));
    }

    outputSurfaceQueueReady = std::queue<NVENCEncoderOutputSurface*>();
    outputSurfaceQueue = std::queue<NVENCEncoderOutputSurface*>();
    delete[] inputSurfaces;
    delete[] outputSurfaces;

    if (alive)
        encoderRefDec();

    OSCloseMutex(frameMutex);

    delete[] pstart;
}
Example #4
0
void NVENCEncoder::RequestBuffers(LPVOID buffers)
{
    if (!buffers)
        return;

    OSMutexLocker locker(frameMutex);

    mfxFrameData *buff = (mfxFrameData*)buffers;

    if (buff->MemId && inputSurfaces[(unsigned int)buff->MemId - 1].locked)
        return;

    for (int i = 0; i < maxSurfaceCount; ++i)
    {
        if (inputSurfaces[i].locked || inputSurfaces[i].useCount > 0)
            continue;

        NV_ENC_LOCK_INPUT_BUFFER lockBufferParams = { 0 };

        lockBufferParams.version = NV_ENC_LOCK_INPUT_BUFFER_VER;
        lockBufferParams.inputBuffer = inputSurfaces[i].inputSurface;

        NVENCSTATUS nvStatus = pNvEnc->nvEncLockInputBuffer(encoder, &lockBufferParams);

        if (nvStatus != NV_ENC_SUCCESS)
        {
            NvLog(TEXT("nvEncLockInputBuffer failed for surface #%d with 0x%x"), i, nvStatus);

            return;
        }

        buff->Pitch = lockBufferParams.pitch;
        buff->Y = (mfxU8*)lockBufferParams.bufferDataPtr;
        buff->UV = buff->Y + (inputSurfaces[i].height * buff->Pitch);

        buff->MemId = mfxMemId(i + 1);
        inputSurfaces[i].locked = true;

        return;
    }

    NvLog(TEXT("No unlocked frame found"));
}
Example #5
0
void deinitNvEnc()
{
    iNvencDeviceCount = 0;

    delete pNvEnc;
    pNvEnc = 0;

    FreeLibrary(nvEncLib);
    nvEncLib = 0;

    NvLog(TEXT("NVENC deinitialized"));
}
Example #6
0
void NVENCEncoder::RequestKeyframe()
{
    OSMutexLocker locker(frameMutex);

    NV_ENC_RECONFIGURE_PARAMS params = { 0 };
    params.version = NV_ENC_RECONFIGURE_PARAMS_VER;

    params.forceIDR = 1;
    mcpy(&params.reInitEncodeParams, &initEncodeParams, sizeof(NV_ENC_INITIALIZE_PARAMS));

    NVENCSTATUS nvStatus = pNvEnc->nvEncReconfigureEncoder(encoder, &params);
    if (nvStatus != NV_ENC_SUCCESS)
    {
        NvLog(TEXT("Failed requesting a Keyframe with 0x%x"), nvStatus);
    }
}
Example #7
0
bool NVENCEncoder::SetBitRate(DWORD maxBitrate, DWORD bufferSize)
{
    if (dontTouchConfig)
        return false;

    OSMutexLocker locker(frameMutex);

    NV_ENC_RECONFIGURE_PARAMS params = { 0 };
    params.version = NV_ENC_RECONFIGURE_PARAMS_VER;

    if (maxBitrate > 0)
    {
        encodeConfig.rcParams.maxBitRate = maxBitrate * 1000;
        encodeConfig.rcParams.averageBitRate = maxBitrate * 1000;
    }

    if (bufferSize > 0)
    {
        encodeConfig.rcParams.vbvBufferSize = bufferSize * 1000;
        encodeConfig.rcParams.vbvInitialDelay = bufferSize * 1000;
    }

    params.resetEncoder = 1;
    params.forceIDR = 1;
    mcpy(&params.reInitEncodeParams, &initEncodeParams, sizeof(NV_ENC_INITIALIZE_PARAMS));

    NVENCSTATUS nvStatus = pNvEnc->nvEncReconfigureEncoder(encoder, &params);
    if (nvStatus != NV_ENC_SUCCESS)
    {
        NvLog(TEXT("Failed reconfiguring nvenc encoder with 0x%x"), nvStatus);
        return false;
    }

    if (maxBitrate > 0)
        this->maxBitRate = maxBitrate;

    if (bufferSize > 0)
        this->bufferSize = bufferSize;

    return true;
}
Example #8
0
void NVENCEncoder::GetHeaders(DataPacket &packet)
{
    uint32_t outSize = 0;

    char tmpHeader[128];

    NV_ENC_SEQUENCE_PARAM_PAYLOAD payload = { 0 };
    payload.version = NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER;

    payload.spsppsBuffer = tmpHeader;
    payload.inBufferSize = 128;
    payload.outSPSPPSPayloadSize = &outSize;

    NVENCSTATUS nvStatus = pNvEnc->nvEncGetSequenceParams(encoder, &payload);
    if (nvStatus != NV_ENC_SUCCESS)
    {
        NvLog(TEXT("nvEncGetSequenceParams failed with 0x%x"), nvStatus);
        packet.lpPacket = 0;
        packet.size = 0;
        return;
    }

    char *sps = tmpHeader;
    unsigned int i_sps = 4;

    while (tmpHeader[i_sps    ] != 0x00
            || tmpHeader[i_sps + 1] != 0x00
            || tmpHeader[i_sps + 2] != 0x00
            || tmpHeader[i_sps + 3] != 0x01)
    {
        i_sps += 1;
        if (i_sps >= outSize)
        {
            NvLog(TEXT("Invalid SPS/PPS"));
            return;
        }
    }

    char *pps = tmpHeader + i_sps;
    unsigned int i_pps = outSize - i_sps;

    headerPacket.Clear();
    BufferOutputSerializer headerOut(headerPacket);

    headerOut.OutputByte(0x17);
    headerOut.OutputByte(0);
    headerOut.OutputByte(0);
    headerOut.OutputByte(0);
    headerOut.OutputByte(0);
    headerOut.OutputByte(1);
    headerOut.Serialize(sps + 5, 3);
    headerOut.OutputByte(0xff);
    headerOut.OutputByte(0xe1);
    headerOut.OutputWord(htons(i_sps - 4));
    headerOut.Serialize(sps + 4, i_sps - 4);

    headerOut.OutputByte(1);
    headerOut.OutputWord(htons(i_pps - 4));
    headerOut.Serialize(pps + 4, i_pps - 4);

    packet.size = headerPacket.Num();
    packet.lpPacket = headerPacket.Array();
}
Example #9
0
void NVENCEncoder::ProcessOutput(NVENCEncoderOutputSurface *surf, List<DataPacket> &packets, List<PacketType> &packetTypes, DWORD &out_pts)
{
    List<uint32_t> sliceOffsets;
    sliceOffsets.SetSize(encodeConfig.encodeCodecConfig.h264Config.sliceModeData);

    NV_ENC_LOCK_BITSTREAM lockParams = { 0 };
    lockParams.version = NV_ENC_LOCK_BITSTREAM_VER;

    encodeData.Clear();

    lockParams.doNotWait = 0;
    lockParams.outputBitstream = surf->outputSurface;
    lockParams.sliceOffsets = sliceOffsets.Array();

    NVENCSTATUS nvStatus = pNvEnc->nvEncLockBitstream(encoder, &lockParams);
    if (nvStatus != NV_ENC_SUCCESS)
    {
        NvLog(TEXT("Failed locking bitstream with 0x%x"), nvStatus);
        return;
    }

    if (lockParams.bitstreamSizeInBytes > outBufferSize)
    {
        NvLog(TEXT("WARNING: Actual output size is bigger than output buffer size!"));

        outBufferSize = lockParams.bitstreamSizeInBytes;
        delete[] pstart;
        pstart = new uint8_t[outBufferSize];
    }

    memcpy(pstart, lockParams.bitstreamBufferPtr, lockParams.bitstreamSizeInBytes);

    uint8_t *start = pstart;
    uint8_t *end = start + lockParams.bitstreamSizeInBytes;
    const static uint8_t start_seq[] = { 0, 0, 1 };
    start = std::search(start, end, start_seq, start_seq + 3);

    List<x264_nal_t> nalOut;
    while (start != end)
    {
        decltype(start) next = std::search(start + 1, end, start_seq, start_seq + 3);

        x264_nal_t nal;

        nal.i_ref_idc = (start[3] >> 5) & 3;
        nal.i_type = start[3] & 0x1f;

        nal.p_payload = start;
        nal.i_payload = int(next - start);
        nalOut << nal;
        start = next;
    }
    size_t nalNum = nalOut.Num();

    uint64_t dts = surf->timestamp;
    out_pts = (DWORD)lockParams.outputTimeStamp;

    int32_t timeOffset = int(out_pts - dts);
    timeOffset += frameShift;

    if (nalNum && timeOffset < 0)
    {
        frameShift -= timeOffset;
        timeOffset = 0;
    }

    timeOffset = htonl(timeOffset);
    BYTE *timeOffsetAddr = ((BYTE*)&timeOffset) + 1;

    PacketType bestType = PacketType_VideoDisposable;
    bool bFoundFrame = false;

    for (unsigned int i = 0; i < nalNum; i++)
    {
        x264_nal_t &nal = nalOut[i];

        if (nal.i_type == NAL_SEI)
        {
            BYTE *end = nal.p_payload + nal.i_payload;
            BYTE *skip = nal.p_payload;
            while (*(skip++) != 0x1);
            int skipBytes = (int)(skip - nal.p_payload);

            int newPayloadSize = (nal.i_payload - skipBytes);
            BYTE *sei_start = skip + 1;
            while (sei_start < end)
            {
                BYTE *sei = sei_start;
                int sei_type = 0;
                while (*sei == 0xff)
                {
                    sei_type += 0xff;
                    sei += 1;
                }
                sei_type += *sei++;

                int payload_size = 0;
                while (*sei == 0xff)
                {
                    payload_size += 0xff;
                    sei += 1;
                }
                payload_size += *sei++;

                const static BYTE emulation_prevention_pattern[] = { 0, 0, 3 };
                BYTE *search = sei;
                for (BYTE *search = sei;;)
                {
                    search = std::search(search, sei + payload_size, emulation_prevention_pattern, emulation_prevention_pattern + 3);
                    if (search == sei + payload_size)
                        break;
                    payload_size += 1;
                    search += 3;
                }

                int sei_size = (int)(sei - sei_start) + payload_size;
                sei_start[-1] = NAL_SEI;

                if (sei_type == 0x5 /* SEI_USER_DATA_UNREGISTERED */)
                {
                    seiData.Clear();
                    BufferOutputSerializer packetOut(seiData);

                    packetOut.OutputDword(htonl(sei_size + 2));
                    packetOut.Serialize(sei_start - 1, sei_size + 1);
                    packetOut.OutputByte(0x80);
                }
                else
                {
                    BufferOutputSerializer packetOut(encodeData);

                    packetOut.OutputDword(htonl(sei_size + 2));
                    packetOut.Serialize(sei_start - 1, sei_size + 1);
                    packetOut.OutputByte(0x80);
                }
                sei_start += sei_size;

                if (*sei_start == 0x80 && std::find_if_not(sei_start + 1, end, [](uint8_t val) {
                return val == 0;
            }) == end) //find rbsp_trailing_bits
                break;
            }
        }
        else if (nal.i_type == NAL_AUD || nal.i_type == NAL_FILLER)
        {
            BYTE *skip = nal.p_payload;
            while (*(skip++) != 0x1);
            int skipBytes = (int)(skip - nal.p_payload);

            int newPayloadSize = (nal.i_payload - skipBytes);

            BufferOutputSerializer packetOut(encodeData);

            packetOut.OutputDword(htonl(newPayloadSize));
            packetOut.Serialize(nal.p_payload + skipBytes, newPayloadSize);
        }
        else if (nal.i_type == NAL_SLICE_IDR || nal.i_type == NAL_SLICE)
        {
            BYTE *skip = nal.p_payload;
            while (*(skip++) != 0x1);
            int skipBytes = (int)(skip - nal.p_payload);

            if (!bFoundFrame)
            {
                encodeData.Insert(0, (nal.i_type == NAL_SLICE_IDR) ? 0x17 : 0x27);
                encodeData.Insert(1, 1);
                encodeData.InsertArray(2, timeOffsetAddr, 3);

                bFoundFrame = true;
            }

            int newPayloadSize = (nal.i_payload - skipBytes);
            BufferOutputSerializer packetOut(encodeData);

            packetOut.OutputDword(htonl(newPayloadSize));
            packetOut.Serialize(nal.p_payload + skipBytes, newPayloadSize);

            switch (nal.i_ref_idc)
            {
            case NAL_PRIORITY_DISPOSABLE:
                bestType = MAX(bestType, PacketType_VideoDisposable);
                break;
            case NAL_PRIORITY_LOW:
                bestType = MAX(bestType, PacketType_VideoLow);
                break;
            case NAL_PRIORITY_HIGH:
                bestType = MAX(bestType, PacketType_VideoHigh);
                break;
            case NAL_PRIORITY_HIGHEST:
                bestType = MAX(bestType, PacketType_VideoHighest);
                break;
            }
        }
        else
            continue;
    }

    DataPacket packet;
    packet.lpPacket = encodeData.Array();
    packet.size = encodeData.Num();

    packetTypes << bestType;
    packets << packet;

    nvStatus = pNvEnc->nvEncUnlockBitstream(encoder, surf->outputSurface);
    if (nvStatus != NV_ENC_SUCCESS)
    {
        NvLog(TEXT("Failed unlocking bitstream with 0x%x"), nvStatus);
        return;
    }
}
Example #10
0
bool NVENCEncoder::Encode(LPVOID picIn, List<DataPacket> &packets, List<PacketType> &packetTypes, DWORD timestamp, DWORD &out_pts)
{
    NVENCSTATUS nvStatus;
    int i = -1;

    NV_ENC_PIC_PARAMS picParams = { 0 };
    picParams.version = NV_ENC_PIC_PARAMS_VER;

    OSMutexLocker locker(frameMutex);

    packets.Clear();
    packetTypes.Clear();

    if (picIn)
    {
        mfxFrameSurface1 *inputSurface = (mfxFrameSurface1*)picIn;
        mfxFrameData &data = inputSurface->Data;
        assert(data.MemId);

        NVENCEncoderSurface *surf = &inputSurfaces[(unsigned int)data.MemId - 1];

        if (surf->locked)
        {
            nvStatus = pNvEnc->nvEncUnlockInputBuffer(encoder, surf->inputSurface);
            if (nvStatus != NV_ENC_SUCCESS)
            {
                NvLog(TEXT("Unlocking surface failed with 0x%x"), nvStatus);
                return false;
            }
        }

        for (i = 0; i < maxSurfaceCount; ++i)
        {
            if (!outputSurfaces[i].busy)
                break;
        }

        if (i == maxSurfaceCount)
        {
            NvLog(TEXT("Out of output buffers!"));
            surf->locked = false;
            return false;
        }

        surf->locked = false;
        surf->useCount += 1;

        outputSurfaces[i].timestamp = timestamp;
        outputSurfaces[i].inputTimestamp = data.TimeStamp;
        outputSurfaces[i].inSurf = surf;

        picParams.inputBuffer = surf->inputSurface;
        picParams.bufferFmt = NV_ENC_BUFFER_FORMAT_NV12_PL;
        picParams.inputWidth = width;
        picParams.inputHeight = height;
        picParams.outputBitstream = outputSurfaces[i].outputSurface;
        picParams.completionEvent = 0;
        picParams.pictureStruct = NV_ENC_PIC_STRUCT_FRAME;
        picParams.encodePicFlags = 0;
        picParams.inputTimeStamp = data.TimeStamp;
        picParams.inputDuration = 0;
        picParams.codecPicParams.h264PicParams.sliceMode = encodeConfig.encodeCodecConfig.h264Config.sliceMode;
        picParams.codecPicParams.h264PicParams.sliceModeData = encodeConfig.encodeCodecConfig.h264Config.sliceModeData;
        memcpy(&picParams.rcParams, &encodeConfig.rcParams, sizeof(NV_ENC_RC_PARAMS));
    }
    else
    {
        picParams.encodePicFlags = NV_ENC_PIC_FLAG_EOS;
    }

    nvStatus = pNvEnc->nvEncEncodePicture(encoder, &picParams);

    if (picIn && nvStatus == NV_ENC_ERR_NEED_MORE_INPUT)
    {
        outputSurfaceQueue.push(&outputSurfaces[i]);
        outputSurfaces[i].busy = true;
    }

    if (nvStatus != NV_ENC_SUCCESS && nvStatus != NV_ENC_ERR_NEED_MORE_INPUT)
    {
        NvLog(TEXT("nvEncEncodePicture failed with error 0x%x"), nvStatus);
        return false;
    }

    if (nvStatus != NV_ENC_ERR_NEED_MORE_INPUT)
    {
        while (!outputSurfaceQueue.empty())
        {
            NVENCEncoderOutputSurface *qSurf = outputSurfaceQueue.front();
            outputSurfaceQueue.pop();
            outputSurfaceQueueReady.push(qSurf);
        }

        if (picIn)
        {
            outputSurfaceQueueReady.push(&outputSurfaces[i]);
            outputSurfaces[i].busy = true;
        }
    }

    if (!outputSurfaceQueueReady.empty())
    {
        NVENCEncoderOutputSurface *qSurf = outputSurfaceQueueReady.front();
        outputSurfaceQueueReady.pop();

        ProcessOutput(qSurf, packets, packetTypes, out_pts);

        qSurf->busy = false;

        assert(qSurf->inSurf->useCount);
        qSurf->inSurf->useCount -= 1;
    }

    return true;
}
Example #11
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);
}
Example #12
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);
}