Example #1
0
Mesh* ResourceManager::GetMesh(CTSTR lpName, BOOL bAddRef)
{
    traceInFast(ResourceManager::GetMesh);

    assert(lpName);

    for(DWORD i=0; i<MeshList.Num(); i++)
    {
        MeshResource &res = MeshList[i];
        if(res.name.CompareI(lpName))
        {
            if(bAddRef)
                ++res.refs;
            return res.mesh;
        }
    }

    //----------------------------------------

    String path;
    if(!Engine::ConvertResourceName(lpName, TEXT("models"), path))
        return NULL;

    //----------------------------------------

    String strAnimPath;
    strAnimPath << GetPathWithoutExtension(path) << TEXT(".xan");

    OSFindData ofd;
    HANDLE hFind = OSFindFirstFile(strAnimPath, ofd);
    BOOL bHasAnimation = (hFind != NULL);
    if(hFind) OSFindClose(hFind);

    Mesh *newMesh = (bHasAnimation) ? new SkinMesh : new Mesh;
    newMesh->LoadMesh(lpName);
    newMesh->bHasAnimation = bHasAnimation;

    MeshResource &res = *MeshList.CreateNew();
    res.mesh = newMesh;
    res.name = lpName;
    res.refs = 1;

    return newMesh;

    traceOutFast;
}
Example #2
0
File: OBS.cpp Project: SeargeDP/OBS
void OBS::GetProfiles(StringList &profileList)
{
    String strProfilesWildcard;
    OSFindData ofd;
    HANDLE hFind;

    profileList.Clear();

    strProfilesWildcard << lpAppDataPath << TEXT("\\profiles\\*.ini");

    if(hFind = OSFindFirstFile(strProfilesWildcard, ofd))
    {
        do
        {
            if(ofd.bDirectory) continue;
            profileList << GetPathWithoutExtension(ofd.fileName);
        } while(OSFindNextFile(hFind, ofd));

        OSFindClose(hFind);
    }
}
Example #3
0
void SkinMesh::LoadAnimations(CTSTR lpName)
{
    traceIn(SkinMesh::LoadAnimations);

    assert(lpName);

    DWORD i, j, k;

    String strMeshPath;
    if(!Engine::ConvertResourceName(lpName, TEXT("models"), strMeshPath))
        return;

    String strFilePath;
    strFilePath << GetPathWithoutExtension(strMeshPath) << TEXT(".xan");


    //-----------------------------------------------

    XFileInputSerializer animData;

    if(!animData.Open(strFilePath))
    {
        AppWarning(TEXT("Could not open animation file \"%s\""), (TSTR)strFilePath);
        return;
    }

    DWORD ver;
    animData << ver;

    if(ver != ANIMFILE_VER && ver != 0x100)
    {
        AppWarning(TEXT("'%s': Bad animation file version"), strFilePath);
        animData.Close();
        return;
    }

    //-----------------------------------------------

    unsigned int numSequences;
    animData << numSequences;
    SequenceList.SetSize(numSequences);
    for(i=0; i<numSequences; i++)
        animData << SequenceList[i];

    DWORD nBones;
    animData << nBones;
    BoneList.SetSize(nBones);
    for(i=0; i<nBones; i++)
    {
        Bone &bone = BoneList[i];

        List<DWORD>     RigidVerts;
        List<VWeight>   BlendedVerts;
        DWORD           nRigid, nBlended;

        //----------------------------

        animData << bone.Pos
                 << bone.Rot
                 << nRigid
                 << nBlended
                 << bone.flags
                 << bone.idParent;

        animData << RigidVerts;
        animData << BlendedVerts;

        if(bone.idParent != 0xFFFFFFFF)
        {
            bone.Parent = BoneList+bone.idParent;
            bone.LocalPos = bone.Pos - BoneList[bone.idParent].Pos;
            BoneList[bone.idParent].Children << &bone;
        }
        else
        {
            bone.Parent = NULL;
            bone.LocalPos = bone.Pos;

            bone.flags |= BONE_ROOT;
        }

        //----------------------------

        bone.Weights.SetSize(nRigid);

        for(j=0; j<RigidVerts.Num(); j++)
        {
            VWeight &weight = bone.Weights[j];

            weight.vert = RigidVerts[j];
            weight.weight = 1.0f;
        }

        bone.Weights.AppendList(BlendedVerts);

        //----------------------------

        DWORD nAnim;
        animData << nAnim;
        bone.seqKeys.SetSize(nAnim);
        for(j=0; j<nAnim; j++)
        {
            SeqKeys &keys = bone.seqKeys[j];

            DWORD nPosKeys, nRotKeys;

            //----------------------------
            
            animData << nRotKeys;
            keys.hasRotKeys = nRotKeys > 0;
            if(keys.hasRotKeys)
            {
                keys.lpRotKeys = (Quat*)Allocate(nRotKeys*sizeof(Quat));
                animData.Serialize(keys.lpRotKeys, nRotKeys*sizeof(Quat));

                keys.lpRotTans = (Quat*)Allocate(nRotKeys*sizeof(Quat));
                for(k=0; k<nRotKeys; k++)
                {
                    DWORD kp1 = (k == (nRotKeys-1)) ? k : (k+1);
                    DWORD km1 = (k == 0) ? 0 : k-1;

                    Quat &qk   = keys.lpRotKeys[k];
                    Quat &qkp1 = keys.lpRotKeys[kp1];
                    Quat &qkm1 = keys.lpRotKeys[km1];

                    keys.lpRotTans[k] = qk.GetInterpolationTangent(qkm1, qkp1);
                }
            }

            //----------------------------
            
            animData << nPosKeys;
            keys.hasPosKeys = nPosKeys > 0;
            if(keys.hasPosKeys)
            {
                keys.lpPosKeys = (Vect*)Allocate(nPosKeys*sizeof(Vect));
                Vect::SerializeArray(animData, keys.lpPosKeys, nPosKeys);
                
                keys.lpPosTans = (Vect*)Allocate(nPosKeys*sizeof(Vect));
                for(k=0; k<nPosKeys; k++)
                {
                    DWORD kp1 = (k == (nPosKeys-1)) ? k : (k+1);
                    DWORD km1 = (k == 0) ? 0 : k-1;

                    Vect &pk   = keys.lpPosKeys[k];
                    Vect &pkp1 = keys.lpPosKeys[kp1];
                    Vect &pkm1 = keys.lpPosKeys[km1];

                    keys.lpPosTans[k] = pk.GetInterpolationTangent(pkm1, pkp1);
                }
            }
        }
    }

    //-----------------------------------------------

    int num;
    animData << num;
    BoneExtensions.SetSize(num);
    BoneExtensionNames.SetSize(num);

    for(int i=0; i<num; i++)
    {
        animData << BoneExtensionNames[i];
        animData << BoneExtensions[i];
    }

    //-----------------------------------------------

    AnimatedSections.SetSize(nSections);

    if(ver == 0x100) //remove
    {
        UINT *indices = (UINT*)IdxBuffer->GetData();

        List<UINT> adjustedIndices;
        adjustedIndices.CopyArray(indices, IdxBuffer->NumIndices());

        VBData *vbd = VertBuffer->GetData();
        List<Vect> &verts = vbd->VertList;

        //--------- 
        // get vert data
        List<VertAnimInfo> vertInfo;
        vertInfo.SetSize(nVerts);

        for(int i=0; i<nBones; i++)
        {
            Bone &bone = BoneList[i];
            for(int j=0; j<bone.Weights.Num(); j++)
            {
                VWeight &vertWeight = bone.Weights[j];

                if(!vertInfo[vertWeight.vert].bones.HasValue(i))
                {
                    vertInfo[vertWeight.vert].bones << i;
                    vertInfo[vertWeight.vert].weights << vertWeight.weight;
                }
            }
        }

        //--------- 
        // remove excess bone influence from verts (let just set the max to 3 for this)
        /*for(int i=0; i<vertInfo.Num(); i++)
        {
            VertAnimInfo &vert = vertInfo[i];

            while(vert.bones.Num() > 3)
            {
                float weakestWeight = M_INFINITE;
                UINT weakestID;

                for(int j=0; j<vert.bones.Num(); j++)
                {
                    if(vert.weights[j] < weakestWeight)
                    {
                        weakestID = j;
                        weakestWeight = vert.weights[j];
                    }
                }

                float weightAdjust = 1.0f/(1.0f-weakestWeight);
                vert.weights.Remove(weakestID);
                vert.bones.Remove(weakestID);

                for(int j=0; j<vert.weights.Num(); j++)
                    vert.weights[j] *= weightAdjust;
            }
        }*/
        for(int i=0; i<vertInfo.Num(); i++)
        {
            VertAnimInfo &vert = vertInfo[i];

            for(int j=0; j<vert.bones.Num(); j++)
            {
                if(vert.weights[j] <= 0.15f)
                {
                    float weightAdjust = 1.0f/(1.0f-vert.weights[j]);
                    vert.weights.Remove(j);
                    vert.bones.Remove(j);

                    for(int k=0; k<vert.weights.Num(); k++)
                        vert.weights[k] *= weightAdjust;

                    --j;
                }
            }
        }

        //--------- 
        // remove excess bone influence from tris (can only have 4 bones influencing any triangle)
        for(int i=0; i<nSections; i++)
        {
            DrawSection &section = SectionList[i];
            if(!section.numFaces) continue;

            for(int j=0; j<section.numFaces; j++)
            {
                UINT *triVertIDs = &indices[(section.startFace+j)*3];

                List<UINT> bones;
                List<float> bestVertWeights;

                for(int k=0; k<3; k++)
                {
                    VertAnimInfo &info = vertInfo[triVertIDs[k]];

                    for(int l=0; l<info.bones.Num(); l++)
                    {
                        UINT id = bones.FindValueIndex(info.bones[l]);
                        if(id == INVALID)
                        {
                            bones.Add(info.bones[l]);
                            bestVertWeights.Add(info.weights[l]);
                        }
                        else
                            bestVertWeights[id] = MAX(bestVertWeights[id], info.weights[l]);
                    }
                }

                while(bones.Num() > 4)
                {
                    int removeBone, removeBoneID;
                    float bestWeight = M_INFINITE;

                    for(int k=0; k<bones.Num(); k++)
                    {
                        if(bestVertWeights[k] < bestWeight)
                        {
                            removeBone = bones[k];
                            removeBoneID = k;
                            bestWeight = bestVertWeights[k];
                        }
                    }

                    for(int k=0; k<3; k++)
                    {
                        VertAnimInfo &info = vertInfo[triVertIDs[k]];
                        UINT id = info.bones.FindValueIndex(removeBone);
                        if(id == INVALID) continue;

                        float weightAdjust = 1.0f/(1.0f-info.weights[id]);
                        info.weights.Remove(id);
                        info.bones.Remove(id);

                        for(int l=0; l<info.weights.Num(); l++)
                            info.weights[l] *= weightAdjust;
                    }

                    bones.Remove(removeBoneID);
                    bestVertWeights.Remove(removeBoneID);
                }
            }
        }

        //--------- 
        // sort out sections of triangles that are influenced up to a max of 4 bones
        // also, duplicate shared verts
        VBData *newVBD = new VBData;
        newVBD->CopyList(*vbd);

        newVBD->TVList.SetSize(2);
        newVBD->TVList[1].SetWidth(4);
        newVBD->TVList[1].SetSize(nVerts);

        List<SubSectionInfo> newSubSections;

        for(int i=0; i<nSections; i++)
        {
            List<TriBoneInfo> triInfo;

            DrawSection &section = SectionList[i];
            if(!section.numFaces) continue;

            for(int j=0; j<section.numFaces; j++)
            {
                UINT *triVertIDs = &indices[(section.startFace+j)*3];

                TriBoneInfo &newTri = *triInfo.CreateNew();

                for(int k=0; k<3; k++)
                {
                    VertAnimInfo &info = vertInfo[triVertIDs[k]];

                    for(int l=0; l<info.bones.Num(); l++)
                        newTri.bones.SafeAdd(info.bones[l]);
                }
            }

            BitList UsedTris;
            UsedTris.SetSize(section.numFaces);
            DWORD nUsedTris = 0;

            while(nUsedTris != section.numFaces)
            {
                DWORD triSectionID = newSubSections.Num();
                SubSectionInfo *curSubSecInfo = newSubSections.CreateNew();
                curSubSecInfo->section = i;

                for(int j=0; j<triInfo.Num(); j++)
                {
                    if(UsedTris[j]) continue;

                    TriBoneInfo &tri = triInfo[j];

                    List<UINT> secBones;
                    secBones.CopyList(curSubSecInfo->bones);

                    BOOL bBadTri = FALSE;
                    for(int k=0; k<tri.bones.Num(); k++)
                    {
                        secBones.SafeAdd(tri.bones[k]);
                        if(secBones.Num() > 4)
                            break;
                    }

                    if(secBones.Num() > 4) continue;

                    DWORD triID = section.startFace+j;

                    curSubSecInfo->bones.CopyList(secBones);
                    curSubSecInfo->tris << triID;

                    UINT *triVertIDs = &indices[triID*3];
                    for(int k=0; k<3; k++)
                    {
                        VertAnimInfo *info = &vertInfo[triVertIDs[k]];
                        UINT id = info->sections.FindValueIndex(triSectionID);
                        if(id == INVALID)
                        {
                            UINT vertID;
                            if(info->sections.Num() >= 1) //duplicate vertex
                            {
                                vertID = newVBD->DuplicateVertex(triVertIDs[k]);
                                adjustedIndices[(triID*3)+k] = vertID;

                                VertAnimInfo &newVertInfo = *vertInfo.CreateNew();
                                info = &vertInfo[triVertIDs[k]]; //reset pointer

                                newVertInfo.bones.CopyList(info->bones);
                                newVertInfo.weights.CopyList(info->weights);
                                newVertInfo.sections << triSectionID;
                            }
                            else
                                vertID = triVertIDs[k];

                            info->sections << triSectionID;
                            info->sectionVertIDs << vertID;

                            List<Vect4> &vertWeights = *newVBD->TVList[1].GetV4();
                            for(int l=0; l<4; l++)
                            {
                                if(l >= curSubSecInfo->bones.Num())
                                    vertWeights[vertID].ptr[l] = 0.0f;
                                else
                                {
                                    UINT boneID = curSubSecInfo->bones[l];
                                    UINT weightID = info->bones.FindValueIndex(boneID);
                                    if(weightID == INVALID)
                                        vertWeights[vertID].ptr[l] = 0.0f;
                                    else
                                        vertWeights[vertID].ptr[l] = info->weights[weightID];
                                }
                            }
                        }
                        else
                            adjustedIndices[(triID*3)+k] = info->sectionVertIDs[id];
                    }

                    UsedTris.Set(j);
                    ++nUsedTris;
                }
            }
            for(int j=0; j<triInfo.Num(); j++)
                triInfo[j].FreeData();
        }

        //---------
        // create animated draw sections and create new index buffer
        DWORD curTriID = 0;
        List<UINT> newIndices;

        for(int i=0; i<newSubSections.Num(); i++)
        {
            SubSectionInfo &subSecInfo = newSubSections[i];
            AnimSection &animSection = AnimatedSections[subSecInfo.section];
            AnimSubSection &subSection = *animSection.SubSections.CreateNew();

            subSection.numBones = subSecInfo.bones.Num();
            for(int j=0; j<subSecInfo.bones.Num(); j++)
                subSection.bones[j] = subSecInfo.bones[j];

            subSection.startFace = curTriID;

            for(int j=0; j<subSecInfo.tris.Num(); j++)
            {
                UINT *tri = &adjustedIndices[subSecInfo.tris[j]*3];
                newIndices << tri[0] << tri[1] << tri[2];

                ++curTriID;
            }

            subSection.numFaces = curTriID-subSection.startFace;

            subSecInfo.FreeData();
        }

        //rebuild original bone data
        for(int i=0; i<BoneList.Num(); i++)
            BoneList[i].Weights.Clear();

        for(int i=0; i<vertInfo.Num(); i++)
        {
            for(int j=0; j<vertInfo[i].bones.Num(); j++)
            {
                VWeight weight;
                weight.vert = i;
                weight.weight = vertInfo[i].weights[j];

                BoneList[vertInfo[i].bones[j]].Weights << weight;
            }

            vertInfo[i].FreeData();
        }

        delete VertBuffer;
        delete IdxBuffer;

        UINT numIndices;
        UINT *indexArray;
        newIndices.TransferTo(indexArray, numIndices);

        nVerts = newVBD->VertList.Num();

        IdxBuffer = CreateIndexBuffer(GS_UNSIGNED_LONG, indexArray, numIndices);
        VertBuffer = CreateVertexBuffer(newVBD);
    }
    else
    {
        for(int i=0; i<nSections; i++)
            animData << AnimatedSections[i].SubSections;
    }

    animData.Close();

    traceOut;
}
Example #4
0
void OBS::Start()
{
    if(bRunning) return;

    OSEnterMutex (hStartupShutdownMutex);

    scenesConfig.Save();

    //-------------------------------------------------------------

    fps = AppConfig->GetInt(TEXT("Video"), TEXT("FPS"), 30);
    frameTime = 1000/fps;

    //-------------------------------------------------------------

    if(!bLoggedSystemStats)
    {
        LogSystemStats();
        bLoggedSystemStats = TRUE;
    }

    OSCheckForBuggyDLLs();

    //-------------------------------------------------------------
retryHookTest:
    bool alreadyWarnedAboutModules = false;
    if (OSIncompatibleModulesLoaded())
    {
        Log(TEXT("Incompatible modules (pre-D3D) detected."));
        int ret = MessageBox(hwndMain, Str("IncompatibleModules"), NULL, MB_ICONERROR | MB_ABORTRETRYIGNORE);
        if (ret == IDABORT)
        {
            OSLeaveMutex (hStartupShutdownMutex);
            return;
        }
        else if (ret == IDRETRY)
        {
            goto retryHookTest;
        }

        alreadyWarnedAboutModules = true;
    }

    String strPatchesError;
    if (OSIncompatiblePatchesLoaded(strPatchesError))
    {
        OSLeaveMutex (hStartupShutdownMutex);
        MessageBox(hwndMain, strPatchesError.Array(), NULL, MB_ICONERROR);
        Log(TEXT("Incompatible patches detected."));
        return;
    }

    //-------------------------------------------------------------

    String processPriority = AppConfig->GetString(TEXT("General"), TEXT("Priority"), TEXT("Normal"));
    if (!scmp(processPriority, TEXT("Idle")))
        SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);
    else if (!scmp(processPriority, TEXT("Above Normal")))
        SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
    else if (!scmp(processPriority, TEXT("High")))
        SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);

    int networkMode = AppConfig->GetInt(TEXT("Publish"), TEXT("Mode"), 2);
    DWORD delayTime = (DWORD)AppConfig->GetInt(TEXT("Publish"), TEXT("Delay"));

    String strError;

    bFirstConnect = !bReconnecting;

    if(bTestStream)
        network = CreateNullNetwork();
    else
    {
        switch(networkMode)
        {
        case 0: network = (delayTime > 0) ? CreateDelayedPublisher(delayTime) : CreateRTMPPublisher(); break;
        case 1: network = CreateNullNetwork(); break;
        }
    }

    if(!network)
    {
        OSLeaveMutex (hStartupShutdownMutex);

        if(!bReconnecting)
            MessageBox(hwndMain, strError, NULL, MB_ICONERROR);
        else
            DialogBox(hinstMain, MAKEINTRESOURCE(IDD_RECONNECTING), hwndMain, OBS::ReconnectDialogProc);
        return;
    }

    bReconnecting = false;

    //-------------------------------------------------------------

    Log(TEXT("=====Stream Start: %s==============================================="), CurrentDateTimeString().Array());

    //-------------------------------------------------------------

    bEnableProjectorCursor = GlobalConfig->GetInt(L"General", L"EnableProjectorCursor", 1) != 0;
    bPleaseEnableProjector = bPleaseDisableProjector = false;

    int monitorID = AppConfig->GetInt(TEXT("Video"), TEXT("Monitor"));
    if(monitorID >= (int)monitors.Num())
        monitorID = 0;

    RECT &screenRect = monitors[monitorID].rect;
    int defCX = screenRect.right  - screenRect.left;
    int defCY = screenRect.bottom - screenRect.top;

    downscaleType = AppConfig->GetInt(TEXT("Video"), TEXT("Filter"), 0);
    downscale = AppConfig->GetFloat(TEXT("Video"), TEXT("Downscale"), 1.0f);
    baseCX = AppConfig->GetInt(TEXT("Video"), TEXT("BaseWidth"),  defCX);
    baseCY = AppConfig->GetInt(TEXT("Video"), TEXT("BaseHeight"), defCY);

    baseCX = MIN(MAX(baseCX, 128), 4096);
    baseCY = MIN(MAX(baseCY, 128), 4096);

    scaleCX = UINT(double(baseCX) / double(downscale));
    scaleCY = UINT(double(baseCY) / double(downscale));

    //align width to 128bit for fast SSE YUV4:2:0 conversion
    outputCX = scaleCX & 0xFFFFFFFC;
    outputCY = scaleCY & 0xFFFFFFFE;

    bUseMultithreadedOptimizations = AppConfig->GetInt(TEXT("General"), TEXT("UseMultithreadedOptimizations"), TRUE) != 0;
    Log(TEXT("  Multithreaded optimizations: %s"), (CTSTR)(bUseMultithreadedOptimizations ? TEXT("On") : TEXT("Off")));

    encoderSkipThreshold = GlobalConfig->GetInt(TEXT("Video"), TEXT("EncoderSkipThreshold"), fps/4);

    //------------------------------------------------------------------

    Log(TEXT("  Base resolution: %ux%u"), baseCX, baseCY);
    Log(TEXT("  Output resolution: %ux%u"), outputCX, outputCY);
    Log(TEXT("------------------------------------------"));

    //------------------------------------------------------------------

    GS = new D3D10System;
    GS->Init();

    //Thanks to ASUS OSD hooking the goddamn user mode driver framework (!!!!), we have to re-check for dangerous
    //hooks after initializing D3D.
retryHookTestV2:
    if (!alreadyWarnedAboutModules)
    {
        if (OSIncompatibleModulesLoaded())
        {
            Log(TEXT("Incompatible modules (post-D3D) detected."));
            int ret = MessageBox(hwndMain, Str("IncompatibleModules"), NULL, MB_ICONERROR | MB_ABORTRETRYIGNORE);
            if (ret == IDABORT)
            {
                //FIXME: really need a better way to abort startup than this...
                delete network;
                delete GS;

                OSLeaveMutex (hStartupShutdownMutex);
                return;
            }
            else if (ret == IDRETRY)
            {
                goto retryHookTestV2;
            }
        }
    }

    //-------------------------------------------------------------

    mainVertexShader    = CreateVertexShaderFromFile(TEXT("shaders/DrawTexture.vShader"));
    mainPixelShader     = CreatePixelShaderFromFile(TEXT("shaders/DrawTexture.pShader"));

    solidVertexShader   = CreateVertexShaderFromFile(TEXT("shaders/DrawSolid.vShader"));
    solidPixelShader    = CreatePixelShaderFromFile(TEXT("shaders/DrawSolid.pShader"));

    if(!mainVertexShader || !mainPixelShader)
        CrashError(TEXT("Unable to load DrawTexture shaders"));

    if(!solidVertexShader || !solidPixelShader)
        CrashError(TEXT("Unable to load DrawSolid shaders"));

    //------------------------------------------------------------------

    CTSTR lpShader;
    if(CloseFloat(downscale, 1.0))
        lpShader = TEXT("shaders/DrawYUVTexture.pShader");
    else if(downscale < 2.01)
    {
        switch(downscaleType)
        {
            case 0: lpShader = TEXT("shaders/DownscaleBilinear1YUV.pShader"); break;
            case 1: lpShader = TEXT("shaders/DownscaleBicubicYUV.pShader"); break;
            case 2: lpShader = TEXT("shaders/DownscaleLanczos6tapYUV.pShader"); break;
        }
    }
    else if(downscale < 3.01)
        lpShader = TEXT("shaders/DownscaleBilinear9YUV.pShader");
    else
        CrashError(TEXT("Invalid downscale value (must be either 1.0, 1.5, 2.0, 2.25, or 3.0)"));

    yuvScalePixelShader = CreatePixelShaderFromFile(lpShader);
    if (!yuvScalePixelShader)
        CrashError(TEXT("Unable to create shader from file %s"), lpShader);

    //-------------------------------------------------------------

    for(UINT i=0; i<NUM_RENDER_BUFFERS; i++)
    {
        mainRenderTextures[i] = CreateRenderTarget(baseCX, baseCY, GS_BGRA, FALSE);
        yuvRenderTextures[i]  = CreateRenderTarget(outputCX, outputCY, GS_BGRA, FALSE);
    }

    //-------------------------------------------------------------

    D3D10_TEXTURE2D_DESC td;
    zero(&td, sizeof(td));
    td.Width            = outputCX;
    td.Height           = outputCY;
    td.Format           = DXGI_FORMAT_B8G8R8A8_UNORM;
    td.MipLevels        = 1;
    td.ArraySize        = 1;
    td.SampleDesc.Count = 1;
    td.ArraySize        = 1;
    td.Usage            = D3D10_USAGE_STAGING;
    td.CPUAccessFlags   = D3D10_CPU_ACCESS_READ;

    for(UINT i=0; i<NUM_RENDER_BUFFERS; i++)
    {
        HRESULT err = GetD3D()->CreateTexture2D(&td, NULL, &copyTextures[i]);
        if(FAILED(err))
        {
            CrashError(TEXT("Unable to create copy texture"));
            //todo - better error handling
        }
    }

    //------------------------------------------------------------------

    UINT format = AppConfig->GetInt(L"Audio Encoding", L"Format", 1);

    switch (format) {
    case 0: sampleRateHz = 44100; break;
    default:
    case 1: sampleRateHz = 48000; break;
    }

    Log(L"------------------------------------------");
    Log(L"Audio Format: %uhz", sampleRateHz);

    //------------------------------------------------------------------

    AudioDeviceList playbackDevices;
    GetAudioDevices(playbackDevices, ADT_PLAYBACK);

    String strPlaybackDevice = AppConfig->GetString(TEXT("Audio"), TEXT("PlaybackDevice"), TEXT("Default"));
    if(strPlaybackDevice.IsEmpty() || !playbackDevices.HasID(strPlaybackDevice))
    {
        AppConfig->SetString(TEXT("Audio"), TEXT("PlaybackDevice"), TEXT("Default"));
        strPlaybackDevice = TEXT("Default");
    }

    Log(TEXT("Playback device %s"), strPlaybackDevice.Array());
    playbackDevices.FreeData();

    desktopAudio = CreateAudioSource(false, strPlaybackDevice);

    if(!desktopAudio) {
        CrashError(TEXT("Cannot initialize desktop audio sound, more info in the log file."));
    }

    AudioDeviceList audioDevices;
    GetAudioDevices(audioDevices, ADT_RECORDING);

    String strDevice = AppConfig->GetString(TEXT("Audio"), TEXT("Device"), NULL);
    if(strDevice.IsEmpty() || !audioDevices.HasID(strDevice))
    {
        AppConfig->SetString(TEXT("Audio"), TEXT("Device"), TEXT("Disable"));
        strDevice = TEXT("Disable");
    }

    audioDevices.FreeData();

    String strDefaultMic;
    bool bHasDefault = GetDefaultMicID(strDefaultMic);

    if(strDevice.CompareI(TEXT("Disable")))
        EnableWindow(GetDlgItem(hwndMain, ID_MICVOLUME), FALSE);
    else
    {
        bool bUseDefault = strDevice.CompareI(TEXT("Default")) != 0;
        if(!bUseDefault || bHasDefault)
        {
            if(bUseDefault)
                strDevice = strDefaultMic;

            micAudio = CreateAudioSource(true, strDevice);

            if(!micAudio)
                MessageBox(hwndMain, Str("MicrophoneFailure"), NULL, 0);
            else
                micAudio->SetTimeOffset(AppConfig->GetInt(TEXT("Audio"), TEXT("MicTimeOffset"), 0));

            EnableWindow(GetDlgItem(hwndMain, ID_MICVOLUME), micAudio != NULL);
        }
        else
            EnableWindow(GetDlgItem(hwndMain, ID_MICVOLUME), FALSE);
    }

    //-------------------------------------------------------------

    bool bDisableEncoding = false;

    if (bTestStream)
        bDisableEncoding = GlobalConfig->GetInt(TEXT("General"), TEXT("DisablePreviewEncoding"), false) != 0;

    //-------------------------------------------------------------

    UINT bitRate = (UINT)AppConfig->GetInt(TEXT("Audio Encoding"), TEXT("Bitrate"), 96);
    String strEncoder = AppConfig->GetString(TEXT("Audio Encoding"), TEXT("Codec"), TEXT("AAC"));

    if (bDisableEncoding)
        audioEncoder = CreateNullAudioEncoder();
    else
#ifdef USE_AAC
    if(strEncoder.CompareI(TEXT("AAC")))// && OSGetVersion() >= 7)
        audioEncoder = CreateAACEncoder(bitRate);
    else
#endif
        audioEncoder = CreateMP3Encoder(bitRate);

    //-------------------------------------------------------------

    desktopVol = AppConfig->GetFloat(TEXT("Audio"), TEXT("DesktopVolume"), 1.0f);
    micVol     = AppConfig->GetFloat(TEXT("Audio"), TEXT("MicVolume"),     1.0f);

    //-------------------------------------------------------------

    bRunning = true;

    if(sceneElement)
    {
        scene = CreateScene(sceneElement->GetString(TEXT("class")), sceneElement->GetElement(TEXT("data")));
        XElement *sources = sceneElement->GetElement(TEXT("sources"));
        if(sources)
        {
            UINT numSources = sources->NumElements();
            for(UINT i=0; i<numSources; i++)
            {
                SceneItem *item = scene->AddImageSource(sources->GetElementByID(i));
                if(item)
                {
                    if(ListView_GetItemState(GetDlgItem(hwndMain, ID_SOURCES), i, LVIS_SELECTED) > 0)
                        item->Select(true);
                }
            }
        }

        scene->BeginScene();
    }

    if(scene && scene->HasMissingSources())
        MessageBox(hwndMain, Str("Scene.MissingSources"), NULL, 0);

    //-------------------------------------------------------------

    int maxBitRate = AppConfig->GetInt   (TEXT("Video Encoding"), TEXT("MaxBitrate"), 1000);
    int bufferSize = AppConfig->GetInt   (TEXT("Video Encoding"), TEXT("BufferSize"), 1000);
    int quality    = AppConfig->GetInt   (TEXT("Video Encoding"), TEXT("Quality"),    8);
    String preset  = AppConfig->GetString(TEXT("Video Encoding"), TEXT("Preset"),     TEXT("veryfast"));
    bUsing444      = false;//AppConfig->GetInt   (TEXT("Video Encoding"), TEXT("Use444"),     0) != 0;
    bUseCFR        = AppConfig->GetInt(TEXT("Video Encoding"), TEXT("UseCFR"), 1) != 0;

    //-------------------------------------------------------------

    bWriteToFile = networkMode == 1 || AppConfig->GetInt(TEXT("Publish"), TEXT("SaveToFile")) != 0;
    String strOutputFile = AppConfig->GetString(TEXT("Publish"), TEXT("SavePath"));

    strOutputFile.FindReplace(TEXT("\\"), TEXT("/"));

    if (bWriteToFile)
    {
        OSFindData ofd;
        HANDLE hFind = NULL;
        bool bUseDateTimeName = true;
        bool bOverwrite = GlobalConfig->GetInt(L"General", L"OverwriteRecordings", false) != 0;

        if(!bOverwrite && (hFind = OSFindFirstFile(strOutputFile, ofd)))
        {
            String strFileExtension = GetPathExtension(strOutputFile);
            String strFileWithoutExtension = GetPathWithoutExtension(strOutputFile);

            if(strFileExtension.IsValid() && !ofd.bDirectory)
            {
                String strNewFilePath;
                UINT curFile = 0;

                do 
                {
                    strNewFilePath.Clear() << strFileWithoutExtension << TEXT(" (") << FormattedString(TEXT("%02u"), ++curFile) << TEXT(").") << strFileExtension;
                } while(OSFileExists(strNewFilePath));

                strOutputFile = strNewFilePath;

                bUseDateTimeName = false;
            }

            if(ofd.bDirectory)
                strOutputFile.AppendChar('/');

            OSFindClose(hFind);
        }

        if(bUseDateTimeName)
        {
            String strFileName = GetPathFileName(strOutputFile);

            if(!strFileName.IsValid() || !IsSafeFilename(strFileName))
            {
                SYSTEMTIME st;
                GetLocalTime(&st);

                String strDirectory = GetPathDirectory(strOutputFile),
                       extension = GetPathExtension(strOutputFile);
                if(extension.IsEmpty())
                    extension = TEXT("mp4");
                strOutputFile = FormattedString(TEXT("%s/%u-%02u-%02u-%02u%02u-%02u.%s"), strDirectory.Array(), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, extension.Array());
            }
        }
    }

    //-------------------------------------------------------------

    bufferingTime = GlobalConfig->GetInt(TEXT("General"), TEXT("SceneBufferingTime"), 700);
    Log(TEXT("Scene buffering time set to %u"), bufferingTime);

    //-------------------------------------------------------------

    bForceMicMono = AppConfig->GetInt(TEXT("Audio"), TEXT("ForceMicMono")) != 0;
    bRecievedFirstAudioFrame = false;

    //hRequestAudioEvent = CreateSemaphore(NULL, 0, 0x7FFFFFFFL, NULL);
    hSoundDataMutex = OSCreateMutex();
    hSoundThread = OSCreateThread((XTHREAD)OBS::MainAudioThread, NULL);

    //-------------------------------------------------------------

    StartBlankSoundPlayback(strPlaybackDevice);

    //-------------------------------------------------------------

    colorDesc.fullRange = false;
    colorDesc.primaries = ColorPrimaries_BT709;
    colorDesc.transfer  = ColorTransfer_IEC6196621;
    colorDesc.matrix    = outputCX >= 1280 || outputCY > 576 ? ColorMatrix_BT709 : ColorMatrix_SMPTE170M;

    videoEncoder = nullptr;
    if (bDisableEncoding)
        videoEncoder = CreateNullVideoEncoder();
    else if(AppConfig->GetInt(TEXT("Video Encoding"), TEXT("UseQSV")) != 0)
        videoEncoder = CreateQSVEncoder(fps, outputCX, outputCY, quality, preset, bUsing444, colorDesc, maxBitRate, bufferSize, bUseCFR);

    if(!videoEncoder)
        videoEncoder = CreateX264Encoder(fps, outputCX, outputCY, quality, preset, bUsing444, colorDesc, maxBitRate, bufferSize, bUseCFR);


    //-------------------------------------------------------------

    // Ensure that the render frame is properly sized
    ResizeRenderFrame(true);

    //-------------------------------------------------------------

    if(!bTestStream && bWriteToFile && strOutputFile.IsValid())
    {
        String strFileExtension = GetPathExtension(strOutputFile);
        if(strFileExtension.CompareI(TEXT("flv")))
            fileStream = CreateFLVFileStream(strOutputFile);
        else if(strFileExtension.CompareI(TEXT("mp4")))
            fileStream = CreateMP4FileStream(strOutputFile);

        if(!fileStream)
        {
            Log(TEXT("Warning - OBSCapture::Start: Unable to create the file stream. Check the file path in Broadcast Settings."));
            MessageBox(hwndMain, Str("Capture.Start.FileStream.Warning"), Str("Capture.Start.FileStream.WarningCaption"), MB_OK | MB_ICONWARNING);        
        }
    }

    //-------------------------------------------------------------

    curFramePic = NULL;
    bShutdownVideoThread = false;
    bShutdownEncodeThread = false;
    //ResetEvent(hVideoThread);
    hEncodeThread = OSCreateThread((XTHREAD)OBS::EncodeThread, NULL);
    hVideoThread = OSCreateThread((XTHREAD)OBS::MainCaptureThread, NULL);

    if(bTestStream)
    {
        EnableWindow(GetDlgItem(hwndMain, ID_STARTSTOP), FALSE);
        SetWindowText(GetDlgItem(hwndMain, ID_TESTSTREAM), Str("MainWindow.StopTest"));
    }
    else
    {
        EnableWindow(GetDlgItem(hwndMain, ID_TESTSTREAM), FALSE);
        SetWindowText(GetDlgItem(hwndMain, ID_STARTSTOP), Str("MainWindow.StopStream"));
    }

    EnableWindow(GetDlgItem(hwndMain, ID_SCENEEDITOR), TRUE);

    //-------------------------------------------------------------

    ReportStartStreamTrigger();
    
    SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, 0, 0, 0);
    SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_AWAYMODE_REQUIRED | ES_DISPLAY_REQUIRED);

    UpdateRenderViewMessage();

    //update notification icon to reflect current status
    UpdateNotificationAreaIcon();

    OSLeaveMutex (hStartupShutdownMutex);
}
Example #5
0
void OBS::StartRecording()
{
    if (bRecording) return;
    int networkMode = AppConfig->GetInt(TEXT("Publish"), TEXT("Mode"), 2);

    bWriteToFile = networkMode == 1 || AppConfig->GetInt(TEXT("Publish"), TEXT("SaveToFile")) != 0;
    String strOutputFile = AppConfig->GetString(TEXT("Publish"), TEXT("SavePath"));

    strOutputFile.FindReplace(TEXT("\\"), TEXT("/"));

    // Don't request a keyframe while everything is starting up for the first time
    if(!bStartingUp) videoEncoder->RequestKeyframe();

    if (bWriteToFile)
    {
        OSFindData ofd;
        HANDLE hFind = NULL;
        bool bUseDateTimeName = true;
        bool bOverwrite = GlobalConfig->GetInt(L"General", L"OverwriteRecordings", false) != 0;

        if(!bOverwrite && (hFind = OSFindFirstFile(strOutputFile, ofd)))
        {
            String strFileExtension = GetPathExtension(strOutputFile);
            String strFileWithoutExtension = GetPathWithoutExtension(strOutputFile);

            if(strFileExtension.IsValid() && !ofd.bDirectory)
            {
                String strNewFilePath;
                UINT curFile = 0;

                do 
                {
                    strNewFilePath.Clear() << strFileWithoutExtension << TEXT(" (") << FormattedString(TEXT("%02u"), ++curFile) << TEXT(").") << strFileExtension;
                } while(OSFileExists(strNewFilePath));

                strOutputFile = strNewFilePath;

                bUseDateTimeName = false;
            }

            if(ofd.bDirectory)
                strOutputFile.AppendChar('/');

            OSFindClose(hFind);
        }

        if(bUseDateTimeName)
        {
            String strFileName = GetPathFileName(strOutputFile);

            if(!strFileName.IsValid() || !IsSafeFilename(strFileName))
            {
                SYSTEMTIME st;
                GetLocalTime(&st);

                String strDirectory = GetPathDirectory(strOutputFile);
                String file = strOutputFile.Right(strOutputFile.Length() - strDirectory.Length());
                String extension;

                if (!file.IsEmpty())
                    extension = GetPathExtension(file.Array());

                if(extension.IsEmpty())
                    extension = TEXT("mp4");
                strOutputFile = FormattedString(TEXT("%s/%u-%02u-%02u-%02u%02u-%02u.%s"), strDirectory.Array(), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, extension.Array());
            }
        }
    }

    if(!bTestStream && bWriteToFile && strOutputFile.IsValid())
    {
        String strFileExtension = GetPathExtension(strOutputFile);
        if(strFileExtension.CompareI(TEXT("flv")))
            fileStream = CreateFLVFileStream(strOutputFile);
        else if(strFileExtension.CompareI(TEXT("mp4")))
            fileStream = CreateMP4FileStream(strOutputFile);

        if(!fileStream)
        {
            Log(TEXT("Warning - OBSCapture::Start: Unable to create the file stream. Check the file path in Broadcast Settings."));
            MessageBox(hwndMain, Str("Capture.Start.FileStream.Warning"), Str("Capture.Start.FileStream.WarningCaption"), MB_OK | MB_ICONWARNING);        
            bRecording = false;
        }
        else {
            bRecording = true;
            ReportStartRecordingTrigger();
        }
        ConfigureStreamButtons();
    }
}
Example #6
0
String GetOutputFilename(bool replayBuffer=false)
{
    String path = OSGetDefaultVideoSavePath(replayBuffer ? L"\\Replay-$T.flv" : L"\\.flv");
    String strOutputFile = AppConfig->GetString(TEXT("Publish"), replayBuffer ? L"ReplayBufferSavePath" : L"SavePath", path.IsValid() ? path.Array() : nullptr);
    strOutputFile.FindReplace(TEXT("\\"), TEXT("/"));

    OSFindData ofd;
    HANDLE hFind = NULL;
    bool bUseDateTimeName = true;
    bool bOverwrite = GlobalConfig->GetInt(L"General", L"OverwriteRecordings", false) != 0;
    
    strOutputFile = ExpandRecordingFilename(strOutputFile);

    CreatePath(GetPathDirectory(strOutputFile));

    if (!bOverwrite && (hFind = OSFindFirstFile(strOutputFile, ofd)))
    {
        String strFileExtension = GetPathExtension(strOutputFile);
        String strFileWithoutExtension = GetPathWithoutExtension(strOutputFile);

        if (strFileExtension.IsValid() && !ofd.bDirectory)
        {
            String strNewFilePath;
            UINT curFile = 0;

            do
            {
                strNewFilePath.Clear() << strFileWithoutExtension << TEXT(" (") << FormattedString(TEXT("%02u"), ++curFile) << TEXT(").") << strFileExtension;
            } while (OSFileExists(strNewFilePath));

            strOutputFile = strNewFilePath;

            bUseDateTimeName = false;
        }

        if (ofd.bDirectory)
            strOutputFile.AppendChar('/');

        OSFindClose(hFind);
    }

    if (bUseDateTimeName)
    {
        String strFileName = GetPathFileName(strOutputFile);

        if (!strFileName.IsValid() || !IsSafeFilename(strFileName))
        {
            SYSTEMTIME st;
            GetLocalTime(&st);

            String strDirectory = GetPathDirectory(strOutputFile);
            String file = strOutputFile.Right(strOutputFile.Length() - strDirectory.Length());
            String extension;

            if (!file.IsEmpty())
                extension = GetPathExtension(file.Array());

            if (extension.IsEmpty())
                extension = TEXT("mp4");
            strOutputFile = FormattedString(TEXT("%s/%u-%02u-%02u-%02u%02u-%02u.%s"), strDirectory.Array(), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, extension.Array());
        }
    }

    return strOutputFile;
}
Example #7
0
void OBS::Start()
{
    if(bRunning) return;

    OSEnterMutex (hStartupShutdownMutex);

    scenesConfig.Save();

    //-------------------------------------------------------------

    fps = AppConfig->GetInt(TEXT("Video"), TEXT("FPS"), 30);
    frameTime = 1000/fps;

    //-------------------------------------------------------------

    if(!bLoggedSystemStats)
    {
        LogSystemStats();
        bLoggedSystemStats = TRUE;
    }

    //-------------------------------------------------------------

    if (OSIncompatibleModulesLoaded())
    {
        OSLeaveMutex (hStartupShutdownMutex);
        MessageBox(hwndMain, Str("IncompatibleModules"), NULL, MB_ICONERROR);
        Log(TEXT("Incompatible modules detected."));
        return;
    }

    String strPatchesError;
    if (OSIncompatiblePatchesLoaded(strPatchesError))
    {
        OSLeaveMutex (hStartupShutdownMutex);
        MessageBox(hwndMain, strPatchesError.Array(), NULL, MB_ICONERROR);
        Log(TEXT("Incompatible patches detected."));
        return;
    }

    //-------------------------------------------------------------

    int networkMode = AppConfig->GetInt(TEXT("Publish"), TEXT("Mode"), 2);
    DWORD delayTime = (DWORD)AppConfig->GetInt(TEXT("Publish"), TEXT("Delay"));

    String strError;

    if(bTestStream)
        network = CreateNullNetwork();
    else
    {
        switch(networkMode)
        {
        case 0: network = (delayTime > 0) ? CreateDelayedPublisher(delayTime) : CreateRTMPPublisher(); break;
        case 1: network = CreateNullNetwork(); break;
        }
    }

    if(!network)
    {
        OSLeaveMutex (hStartupShutdownMutex);

        if(!bReconnecting)
            MessageBox(hwndMain, strError, NULL, MB_ICONERROR);
        else
            DialogBox(hinstMain, MAKEINTRESOURCE(IDD_RECONNECTING), hwndMain, OBS::ReconnectDialogProc);
        return;
    }

    bReconnecting = false;

    //-------------------------------------------------------------

    Log(TEXT("=====Stream Start: %s==============================================="), CurrentDateTime().Array());

    //-------------------------------------------------------------

    int monitorID = AppConfig->GetInt(TEXT("Video"), TEXT("Monitor"));
    if(monitorID >= (int)monitors.Num())
        monitorID = 0;

    RECT &screenRect = monitors[monitorID].rect;
    int defCX = screenRect.right  - screenRect.left;
    int defCY = screenRect.bottom - screenRect.top;

    downscaleType = AppConfig->GetInt(TEXT("Video"), TEXT("Filter"), 0);
    downscale = AppConfig->GetFloat(TEXT("Video"), TEXT("Downscale"), 1.0f);
    baseCX = AppConfig->GetInt(TEXT("Video"), TEXT("BaseWidth"),  defCX);
    baseCY = AppConfig->GetInt(TEXT("Video"), TEXT("BaseHeight"), defCY);

    baseCX = MIN(MAX(baseCX, 128), 4096);
    baseCY = MIN(MAX(baseCY, 128), 4096);

    scaleCX = UINT(double(baseCX) / double(downscale));
    scaleCY = UINT(double(baseCY) / double(downscale));

    //align width to 128bit for fast SSE YUV4:2:0 conversion
    outputCX = scaleCX & 0xFFFFFFFC;
    outputCY = scaleCY & 0xFFFFFFFE;

    bUseMultithreadedOptimizations = AppConfig->GetInt(TEXT("General"), TEXT("UseMultithreadedOptimizations"), TRUE) != 0;
    Log(TEXT("  Multithreaded optimizations: %s"), (CTSTR)(bUseMultithreadedOptimizations ? TEXT("On") : TEXT("Off")));

    GlobalConfig->SetInt(TEXT("Audio"), TEXT("GlobalAudioTimeAdjust"), 0);

    //------------------------------------------------------------------

    Log(TEXT("  Base resolution: %ux%u"), baseCX, baseCY);
    Log(TEXT("  Output resolution: %ux%u"), outputCX, outputCY);
    Log(TEXT("------------------------------------------"));

    //------------------------------------------------------------------

    GS = new D3D10System;
    GS->Init();

    //-------------------------------------------------------------

    mainVertexShader    = CreateVertexShaderFromFile(TEXT("shaders/DrawTexture.vShader"));
    mainPixelShader     = CreatePixelShaderFromFile(TEXT("shaders/DrawTexture.pShader"));

    solidVertexShader   = CreateVertexShaderFromFile(TEXT("shaders/DrawSolid.vShader"));
    solidPixelShader    = CreatePixelShaderFromFile(TEXT("shaders/DrawSolid.pShader"));

    //------------------------------------------------------------------

    CTSTR lpShader;
    if(CloseFloat(downscale, 1.0))
        lpShader = TEXT("shaders/DrawYUVTexture.pShader");
    else if(downscale < 2.01)
    {
        switch(downscaleType)
        {
            case 0: lpShader = TEXT("shaders/DownscaleBilinear1YUV.pShader"); break;
            case 1: lpShader = TEXT("shaders/DownscaleBicubicYUV.pShader"); break;
            case 2: lpShader = TEXT("shaders/DownscaleLanczos6tapYUV.pShader"); break;
        }
    }
    else if(downscale < 3.01)
        lpShader = TEXT("shaders/DownscaleBilinear9YUV.pShader");
    else
        CrashError(TEXT("Invalid downscale value (must be either 1.0, 1.5, 2.0, 2.25, or 3.0)"));

    yuvScalePixelShader = CreatePixelShaderFromFile(lpShader);
    if (!yuvScalePixelShader)
        CrashError(TEXT("Unable to create shader from file %s"), lpShader);

    //-------------------------------------------------------------

    for(UINT i=0; i<NUM_RENDER_BUFFERS; i++)
    {
        mainRenderTextures[i] = CreateRenderTarget(baseCX, baseCY, GS_BGRA, FALSE);
        yuvRenderTextures[i]  = CreateRenderTarget(outputCX, outputCY, GS_BGRA, FALSE);
    }

    //-------------------------------------------------------------

    D3D10_TEXTURE2D_DESC td;
    zero(&td, sizeof(td));
    td.Width            = outputCX;
    td.Height           = outputCY;
    td.Format           = DXGI_FORMAT_B8G8R8A8_UNORM;
    td.MipLevels        = 1;
    td.ArraySize        = 1;
    td.SampleDesc.Count = 1;
    td.ArraySize        = 1;
    td.Usage            = D3D10_USAGE_STAGING;
    td.CPUAccessFlags   = D3D10_CPU_ACCESS_READ;

    for(UINT i=0; i<NUM_RENDER_BUFFERS; i++)
    {
        HRESULT err = GetD3D()->CreateTexture2D(&td, NULL, &copyTextures[i]);
        if(FAILED(err))
        {
            CrashError(TEXT("Unable to create copy texture"));
            //todo - better error handling
        }
    }

    //-------------------------------------------------------------

    AudioDeviceList playbackDevices;
    GetAudioDevices(playbackDevices, ADT_PLAYBACK);

    String strPlaybackDevice = AppConfig->GetString(TEXT("Audio"), TEXT("PlaybackDevice"), TEXT("Default"));
    if(strPlaybackDevice.IsEmpty() || !playbackDevices.HasID(strPlaybackDevice))
    {
        AppConfig->SetString(TEXT("Audio"), TEXT("PlaybackDevice"), TEXT("Default"));
        strPlaybackDevice = TEXT("Default");
    }

    Log(TEXT("Playback device %s"), strPlaybackDevice.Array());
    playbackDevices.FreeData();

    desktopAudio = CreateAudioSource(false, strPlaybackDevice);

    if(!desktopAudio) {
        CrashError(TEXT("Cannot initialize desktop audio sound, more info in the log file."));
    }

    AudioDeviceList audioDevices;
    GetAudioDevices(audioDevices, ADT_RECORDING);

    String strDevice = AppConfig->GetString(TEXT("Audio"), TEXT("Device"), NULL);
    if(strDevice.IsEmpty() || !audioDevices.HasID(strDevice))
    {
        AppConfig->SetString(TEXT("Audio"), TEXT("Device"), TEXT("Disable"));
        strDevice = TEXT("Disable");
    }

    audioDevices.FreeData();

    String strDefaultMic;
    bool bHasDefault = GetDefaultMicID(strDefaultMic);

    if(strDevice.CompareI(TEXT("Disable")))
        EnableWindow(GetDlgItem(hwndMain, ID_MICVOLUME), FALSE);
    else
    {
        bool bUseDefault = strDevice.CompareI(TEXT("Default")) != 0;
        if(!bUseDefault || bHasDefault)
        {
            if(bUseDefault)
                strDevice = strDefaultMic;

            micAudio = CreateAudioSource(true, strDevice);

            if(!micAudio)
                MessageBox(hwndMain, Str("MicrophoneFailure"), NULL, 0);
            else
                micAudio->SetTimeOffset(AppConfig->GetInt(TEXT("Audio"), TEXT("MicTimeOffset"), 0));

            EnableWindow(GetDlgItem(hwndMain, ID_MICVOLUME), micAudio != NULL);
        }
        else
            EnableWindow(GetDlgItem(hwndMain, ID_MICVOLUME), FALSE);
    }

    //-------------------------------------------------------------

    UINT bitRate = (UINT)AppConfig->GetInt(TEXT("Audio Encoding"), TEXT("Bitrate"), 96);
    String strEncoder = AppConfig->GetString(TEXT("Audio Encoding"), TEXT("Codec"), TEXT("AAC"));

#ifdef USE_AAC
    if(strEncoder.CompareI(TEXT("AAC")) && OSGetVersion() >= 7)
        audioEncoder = CreateAACEncoder(bitRate);
    else
#endif
        audioEncoder = CreateMP3Encoder(bitRate);

    //-------------------------------------------------------------

    desktopVol = AppConfig->GetFloat(TEXT("Audio"), TEXT("DesktopVolume"), 1.0f);
    micVol     = AppConfig->GetFloat(TEXT("Audio"), TEXT("MicVolume"),     1.0f);

    //-------------------------------------------------------------

    bRunning = true;

    if(sceneElement)
    {
        scene = CreateScene(sceneElement->GetString(TEXT("class")), sceneElement->GetElement(TEXT("data")));
        XElement *sources = sceneElement->GetElement(TEXT("sources"));
        if(sources)
        {
            UINT numSources = sources->NumElements();
            for(UINT i=0; i<numSources; i++)
            {
                SceneItem *item = scene->AddImageSource(sources->GetElementByID(i));
                if(item)
                {
                    if(ListView_GetItemState(GetDlgItem(hwndMain, ID_SOURCES), i, LVIS_SELECTED) > 0)
                        item->Select(true);
                }
            }
        }

        scene->BeginScene();
    }

    if(scene && scene->HasMissingSources())
        MessageBox(hwndMain, Str("Scene.MissingSources"), NULL, 0);

    //-------------------------------------------------------------

    int maxBitRate = AppConfig->GetInt   (TEXT("Video Encoding"), TEXT("MaxBitrate"), 1000);
    int bufferSize = AppConfig->GetInt   (TEXT("Video Encoding"), TEXT("BufferSize"), 1000);
    int quality    = AppConfig->GetInt   (TEXT("Video Encoding"), TEXT("Quality"),    8);
    String preset  = AppConfig->GetString(TEXT("Video Encoding"), TEXT("Preset"),     TEXT("veryfast"));
    bUsing444      = AppConfig->GetInt   (TEXT("Video Encoding"), TEXT("Use444"),     0) != 0;

    bDupeFrames = AppConfig->GetInt(TEXT("Video Encoding"), TEXT("DupeFrames"), 1) != 0;

    if(bUsing444)
        bDupeFrames = bUseCFR = false;
    else
    {
        bUseCFR = AppConfig->GetInt(TEXT("Video Encoding"), TEXT("UseCFR"), 0) != 0;
        if(bUseCFR) bDupeFrames = true;
    }

    //-------------------------------------------------------------

    bWriteToFile = networkMode == 1 || AppConfig->GetInt(TEXT("Publish"), TEXT("SaveToFile")) != 0;
    String strOutputFile = AppConfig->GetString(TEXT("Publish"), TEXT("SavePath"));

    strOutputFile.FindReplace(TEXT("\\"), TEXT("/"));

    if (bWriteToFile)
    {
        OSFindData ofd;
        HANDLE hFind = NULL;
        bool bUseDateTimeName = true;

        if(hFind = OSFindFirstFile(strOutputFile, ofd))
        {
            String strFileExtension = GetPathExtension(strOutputFile);
            String strFileWithoutExtension = GetPathWithoutExtension(strOutputFile);

            if(strFileExtension.IsValid() && !ofd.bDirectory)
            {
                String strNewFilePath;
                UINT curFile = 0;

                do 
                {
                    strNewFilePath.Clear() << strFileWithoutExtension << TEXT(" (") << FormattedString(TEXT("%02u"), ++curFile) << TEXT(").") << strFileExtension;
                } while(OSFileExists(strNewFilePath));

                strOutputFile = strNewFilePath;

                bUseDateTimeName = false;
            }

            if(ofd.bDirectory)
                strOutputFile.AppendChar('/');

            OSFindClose(hFind);
        }

        if(bUseDateTimeName)
        {
            String strFileName = GetPathFileName(strOutputFile);

            if(!strFileName.IsValid() || !IsSafeFilename(strFileName))
            {
                SYSTEMTIME st;
                GetLocalTime(&st);

                String strDirectory = GetPathDirectory(strOutputFile);
                strOutputFile = FormattedString(TEXT("%s/%u-%02u-%02u-%02u%02u-%02u.mp4"), strDirectory.Array(), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
            }
        }
    }

    //-------------------------------------------------------------

    bufferingTime = GlobalConfig->GetInt(TEXT("General"), TEXT("SceneBufferingTime"), 400);

    //-------------------------------------------------------------

    bForceMicMono = AppConfig->GetInt(TEXT("Audio"), TEXT("ForceMicMono")) != 0;
    bRecievedFirstAudioFrame = false;

    //hRequestAudioEvent = CreateSemaphore(NULL, 0, 0x7FFFFFFFL, NULL);
    hSoundDataMutex = OSCreateMutex();
    hSoundThread = OSCreateThread((XTHREAD)OBS::MainAudioThread, NULL);

    //-------------------------------------------------------------

    StartBlankSoundPlayback(strPlaybackDevice);

    //-------------------------------------------------------------

    ctsOffset = 0;
    videoEncoder = CreateX264Encoder(fps, outputCX, outputCY, quality, preset, bUsing444, maxBitRate, bufferSize, bUseCFR, bDupeFrames);

    //-------------------------------------------------------------

    // Ensure that the render frame is properly sized
    ResizeRenderFrame(true);

    //-------------------------------------------------------------

    if(!bTestStream && bWriteToFile && strOutputFile.IsValid())
    {
        String strFileExtension = GetPathExtension(strOutputFile);
        if(strFileExtension.CompareI(TEXT("flv")))
            fileStream = CreateFLVFileStream(strOutputFile);
        else if(strFileExtension.CompareI(TEXT("mp4")))
            fileStream = CreateMP4FileStream(strOutputFile);
    }

    //-------------------------------------------------------------

    hMainThread = OSCreateThread((XTHREAD)OBS::MainCaptureThread, NULL);

    if(bTestStream)
    {
        EnableWindow(GetDlgItem(hwndMain, ID_STARTSTOP), FALSE);
        SetWindowText(GetDlgItem(hwndMain, ID_TESTSTREAM), Str("MainWindow.StopTest"));
    }
    else
    {
        EnableWindow(GetDlgItem(hwndMain, ID_TESTSTREAM), FALSE);
        SetWindowText(GetDlgItem(hwndMain, ID_STARTSTOP), Str("MainWindow.StopStream"));
    }

    EnableWindow(GetDlgItem(hwndMain, ID_SCENEEDITOR), TRUE);

    //-------------------------------------------------------------

    ReportStartStreamTrigger();
    
    SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, 0, 0, 0);
    SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_AWAYMODE_REQUIRED | ES_DISPLAY_REQUIRED);

    UpdateRenderViewMessage();

    //update notification icon to reflect current status
    UpdateNotificationAreaIcon();

    OSLeaveMutex (hStartupShutdownMutex);
}