int main( int argc, char* argv[] ) { #if !ENABLE_TEST if ( argc <= 1 ) { PrintBanner(); return 0; } #endif g_Config = ReadConfigFromCommandLine( argc, argv ); #if ENABLE_TEST if ( g_Playlist.IsEmpty() ) g_Playlist.EnqueueTrack( "test.ogg" ); g_Config.m_Verbose = true; #endif auto AudioSubsystem = CreateAudioSubsystem_OpenAL(); AudioSubsystem->Start(); auto Source = AudioSubsystem->CreateAudioSource(); // allow seamless looping if there is only one track if ( g_Playlist.GetNumTracks() == 1 ) Source->SetLooping( g_Config.m_Loop ); bool RequestingExit = false; while ( !g_Playlist.IsEmpty() && !RequestingExit ) { auto FileName = g_Playlist.GetAndPopNextTrack( g_Config.m_Loop ); auto DataBlob = ReadFileAsBlob( FileName.c_str() ); if (!DataBlob || !DataBlob->GetDataSize()) continue; auto Provider = CreateWaveDataProvider( FileName.c_str(), DataBlob ); if (!Provider) continue; Source->BindDataProvider( Provider ); Source->Play(); while ( Source->IsPlaying() && !RequestingExit ) { std::this_thread::sleep_for( std::chrono::milliseconds(10) ); if ( IsKeyPressed() ) RequestingExit = true; }; Source->Stop(); } Source = nullptr; AudioSubsystem->Stop(); return 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, ©Textures[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); }
void OBS::Start(bool recordingOnly, bool replayBufferOnly) { if(bRunning && !bRecording && !bRecordingReplayBuffer) return; int networkMode = AppConfig->GetInt(TEXT("Publish"), TEXT("Mode"), 2); DWORD delayTime = (DWORD)AppConfig->GetInt(TEXT("Publish"), TEXT("Delay")); if (bRecording && networkMode != 0) return; if((bRecording || bRecordingReplayBuffer) && networkMode == 0 && delayTime == 0 && !recordingOnly && !replayBufferOnly) { bFirstConnect = !bReconnecting; if (network) { NetworkStream *net = network; network = nullptr; delete net; } network = CreateRTMPPublisher(); Log(TEXT("=====Stream Start (while recording): %s============================="), CurrentDateTimeString().Array()); bSentHeaders = false; bStreaming = true; ReportStartStreamingTrigger(); ConfigureStreamButtons(); return; } bStartingUp = true; OSEnterMutex(hStartupShutdownMutex); DisableMenusWhileStreaming(true); scenesConfig.SaveTo(String() << lpAppDataPath << "\\scenes.xconfig"); 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 = OBSMessageBox(hwndMain, Str("IncompatibleModules"), NULL, MB_ICONERROR | MB_ABORTRETRYIGNORE); if (ret == IDABORT) { DisableMenusWhileStreaming(false); OSLeaveMutex (hStartupShutdownMutex); bStartingUp = false; return; } else if (ret == IDRETRY) { goto retryHookTest; } alreadyWarnedAboutModules = true; } String strPatchesError; if (OSIncompatiblePatchesLoaded(strPatchesError)) { DisableMenusWhileStreaming(false); OSLeaveMutex (hStartupShutdownMutex); OBSMessageBox(hwndMain, strPatchesError.Array(), NULL, MB_ICONERROR); Log(TEXT("Incompatible patches detected.")); bStartingUp = false; return; } //check the user isn't trying to stream or record with no sources which is typically //a configuration error if (!bTestStream) { bool foundSource = false; XElement *scenes = App->scenesConfig.GetElement(TEXT("scenes")); if (scenes) { UINT numScenes = scenes->NumElements(); for (UINT i = 0; i<numScenes; i++) { XElement *sceneElement = scenes->GetElementByID(i); XElement *sources = sceneElement->GetElement(TEXT("sources")); if (sources && sources->NumElements()) { foundSource = true; break; } } } if (!foundSource) { if (OBSMessageBox(hwndMain, Str("NoSourcesFound"), NULL, MB_ICONWARNING|MB_YESNO) == IDNO) { DisableMenusWhileStreaming(false); OSLeaveMutex(hStartupShutdownMutex); bStartingUp = false; 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); networkMode = AppConfig->GetInt(TEXT("Publish"), TEXT("Mode"), 2); delayTime = (DWORD)AppConfig->GetInt(TEXT("Publish"), TEXT("Delay")); String strError; bFirstConnect = !bReconnecting; if(bTestStream || recordingOnly || replayBufferOnly) network = CreateNullNetwork(); else { switch(networkMode) { case 0: network = (delayTime > 0) ? CreateDelayedPublisher(delayTime) : CreateRTMPPublisher(); break; case 1: network = CreateNullNetwork(); break; } } if(!network) { DisableMenusWhileStreaming(false); OSLeaveMutex (hStartupShutdownMutex); if(!bReconnecting) OBSMessageBox(hwndMain, strError, NULL, MB_ICONERROR); else OBSDialogBox(hinstMain, MAKEINTRESOURCE(IDD_RECONNECTING), hwndMain, OBS::ReconnectDialogProc); bStartingUp = false; 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 = OBSMessageBox(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; DisableMenusWhileStreaming(false); OSLeaveMutex (hStartupShutdownMutex); bStartingUp = false; 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, ©Textures[i]); if(FAILED(err)) { CrashError(TEXT("Unable to create copy texture")); //todo - better error handling } } //------------------------------------------------------------------ String strEncoder = AppConfig->GetString(TEXT("Audio Encoding"), TEXT("Codec"), TEXT("AAC")); BOOL isAAC = strEncoder.CompareI(TEXT("AAC")); UINT format = AppConfig->GetInt(L"Audio Encoding", L"Format", 1); if (!isAAC) format = 0; switch (format) { case 0: sampleRateHz = 44100; break; default: case 1: sampleRateHz = 48000; break; } Log(L"------------------------------------------"); Log(L"Audio Format: %u Hz", sampleRateHz); //------------------------------------------------------------------ BOOL isStereo = AppConfig->GetInt(L"Audio Encoding", L"isStereo", 1); switch (isStereo) { case 0: audioChannels = 1; break; default: case 1: audioChannels = 2; break; } Log(L"------------------------------------------"); Log(L"Audio Channels: %u Ch", audioChannels); //------------------------------------------------------------------ AudioDeviceList playbackDevices; bool useInputDevices = AppConfig->GetInt(L"Audio", L"InputDevicesForDesktopSound", false) != 0; GetAudioDevices(playbackDevices, useInputDevices ? ADT_RECORDING : 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.")); } if (useInputDevices) Log(L"Use Input Devices enabled, not recording standard desktop audio"); AudioDeviceList audioDevices; GetAudioDevices(audioDevices, ADT_RECORDING, false, true); 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) OBSMessageBox(hwndMain, Str("MicrophoneFailure"), NULL, 0); else { int offset = AppConfig->GetInt(TEXT("Audio"), TEXT("MicTimeOffset"), 0); Log(L"Mic time offset: %d", offset); micAudio->SetTimeOffset(offset); } 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); if (bDisableEncoding) audioEncoder = CreateNullAudioEncoder(); else #ifdef USE_AAC if(isAAC) // && 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(); unsigned int numSources = scene->sceneItems.Num(); for(UINT i=0; i<numSources; i++) { XElement *source = scene->sceneItems[i]->GetElement(); String className = source->GetString(TEXT("class")); if(scene->sceneItems[i]->bRender && className == "GlobalSource") { XElement *globalSourceData = source->GetElement(TEXT("data")); String globalSourceName = globalSourceData->GetString(TEXT("name")); if(App->GetGlobalSource(globalSourceName) != NULL) { App->GetGlobalSource(globalSourceName)->GlobalSourceEnterScene(); } } } } if(scene && scene->HasMissingSources()) OBSMessageBox(hwndMain, Str("Scene.MissingSources"), NULL, 0); //------------------------------------------------------------- int maxBitRate = AppConfig->GetInt (TEXT("Video Encoding"), TEXT("MaxBitrate"), 1000); int bufferSize = maxBitRate; if (AppConfig->GetInt(L"Video Encoding", L"UseBufferSize", 0) != 0) 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; //------------------------------------------------------------- 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); //------------------------------------------------------------- //if (!useInputDevices) // 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; String videoEncoderErrors; String vencoder = AppConfig->GetString(L"Video Encoding", L"Encoder"); if (bDisableEncoding) videoEncoder = CreateNullVideoEncoder(); else if(vencoder == L"QSV") videoEncoder = CreateQSVEncoder(fps, outputCX, outputCY, quality, preset, bUsing444, colorDesc, maxBitRate, bufferSize, bUseCFR, videoEncoderErrors); else if(vencoder == L"NVENC") videoEncoder = CreateNVENCEncoder(fps, outputCX, outputCY, quality, preset, bUsing444, colorDesc, maxBitRate, bufferSize, bUseCFR, videoEncoderErrors); else videoEncoder = CreateX264Encoder(fps, outputCX, outputCY, quality, preset, bUsing444, colorDesc, maxBitRate, bufferSize, bUseCFR); if (!videoEncoder) { Log(L"Couldn't initialize encoder"); Stop(true); if (videoEncoderErrors.IsEmpty()) videoEncoderErrors = Str("Encoder.InitFailed"); else videoEncoderErrors = String(Str("Encoder.InitFailedWithReason")) + videoEncoderErrors; OBSMessageBox(hwndMain, videoEncoderErrors.Array(), nullptr, MB_OK | MB_ICONERROR); //might want to defer localization until here to automatically //output english localization to logfile return; } if ((bStreaming = (!recordingOnly && !replayBufferOnly) && networkMode == 0)) ReportStartStreamingTrigger(); //------------------------------------------------------------- // Ensure that the render frame is properly sized ResizeRenderFrame(true); //------------------------------------------------------------- if ((!replayBufferOnly && !StartRecording(recordingOnly)) && !bStreaming) { Stop(true); return; } //------------------------------------------------------------- curFramePic = NULL; bShutdownVideoThread = false; bShutdownEncodeThread = false; //ResetEvent(hVideoThread); hEncodeThread = OSCreateThread((XTHREAD)OBS::EncodeThread, NULL); hVideoThread = OSCreateThread((XTHREAD)OBS::MainCaptureThread, NULL); 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); bStartingUp = false; ConfigureStreamButtons(); }
void initializeVoiceRecognition() { auto session = senseManager->QuerySession(); // 音声入力デバイスを作成する audioSource = session->CreateAudioSource(); if ( audioSource == 0 ){ throw std::runtime_error( "音声入力デバイスの作成に失敗しました" ); } // 音声入力デバイスを列挙する std::cout << "音声入力デバイス" << std::endl; PXCAudioSource::DeviceInfo device = {}; audioSource->ScanDevices(); for ( int i = 0;; ++i ) { PXCAudioSource::DeviceInfo dinfo = {}; auto sts = audioSource->QueryDeviceInfo( i, &dinfo ); if ( sts < PXC_STATUS_NO_ERROR ) { break; } // 音声入力デバイス名を表示する std::wcout << "\t" << dinfo.name << std::endl; // 最初のデバイスを使う if ( i == 0 ){ device = dinfo; } } // 音声入力デバイスを設定する auto sts = audioSource->SetDevice( &device ); if ( sts < PXC_STATUS_NO_ERROR ) { throw std::runtime_error( "音声入力デバイスの設定に失敗しました" ); } // 音声認識エンジンを列挙する std::cout << "音声認識エンジン" << std::endl; PXCSession::ImplDesc in = {}; PXCSession::ImplDesc out = {}; in.cuids[0] = PXCSpeechRecognition::CUID; for ( int i = 0; ; ++i ) { // 音声認識エンジンを取得する auto sts = session->QueryImpl( &in, i, &out ); if ( sts < PXC_STATUS_NO_ERROR ) { break; } // 音声認識エンジン名称を表示する std::wcout << "\t" << out.friendlyName << std::endl; } // 最初の音声認識エンジンを使う sts = session->QueryImpl( &in, 0, &out ); // 音声認識エンジンオブジェクトを作成する sts = session->CreateImpl<PXCSpeechRecognition>( &out, &recognition ); if ( sts < PXC_STATUS_NO_ERROR ) { throw std::runtime_error( "音声認識エンジンオブジェクトの作成に失敗しました" ); } // 対応言語を列挙する PXCSpeechRecognition::ProfileInfo profile; for ( int j = 0;; ++j ) { // 音声認識エンジンが持っているプロファイルを取得する PXCSpeechRecognition::ProfileInfo pinfo; sts = recognition->QueryProfile( j, &pinfo ); if ( sts < PXC_STATUS_NO_ERROR ) { break; } // 対応言語を表示する std::wcout << "\t\t" << LanguageToString( pinfo.language ) << std::endl; // 英語のエンジンを使う(日本語対応時には日本語に変更する) if ( pinfo.language == PXCSpeechRecognition::LANGUAGE_JP_JAPANESE ){ profile = pinfo; } } if ( profile.language == 0 ){ throw std::runtime_error( "選択した音声認識エンジンが見つかりませんでした" ); } // 使用する言語を設定する sts = recognition->SetProfile( &profile ); if ( sts < PXC_STATUS_NO_ERROR ) { throw std::runtime_error( "音声認識エンジンオブジェクトの設定に失敗しました" ); } // コマンドモードに設定する setCommandMode(); // 音声認識を開始する sts = recognition->StartRec( audioSource, this ); if ( sts < PXC_STATUS_NO_ERROR ) { throw std::runtime_error( "音声認識の開始に失敗しました" ); } }
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, ©Textures[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); }
// Start void CAVMediaFlow::Start (bool startvideo, bool startaudio, bool starttext) { if (m_pConfig == NULL) { return; } if (!startvideo && !startaudio && !starttext) return; // Create audio and video sources if (m_pConfig->GetBoolValue(CONFIG_AUDIO_ENABLE) && m_audioSource == NULL && startaudio) { m_audioSource = CreateAudioSource(m_pConfig, m_videoSource); } if (m_pConfig->GetBoolValue(CONFIG_TEXT_ENABLE) && m_textSource == NULL && starttext) { m_textSource = CreateTextSource(m_pConfig); debug_message("Created text source %p", m_textSource); } if (m_pConfig->GetBoolValue(CONFIG_VIDEO_ENABLE) && startvideo) { if (m_videoSource == NULL) { debug_message("start - creating video source"); m_videoSource = CreateVideoSource(m_pConfig); } if (m_audioSource != NULL) { m_audioSource->SetVideoSource(m_videoSource); } } m_maxAudioSamplesPerFrame = 0; CMediaStream *s; s = m_stream_list->GetHead(); // Create the components for each stream. This make sure // that no more than 1 instance of a profile is being encoded. while (s != NULL) { CAudioEncoder *ae_ptr = NULL; CVideoEncoder *ve_ptr = NULL; CTextEncoder *te_ptr = NULL; if (s->GetBoolValue(STREAM_VIDEO_ENABLED) && startvideo) { // see if profile has already been started ve_ptr = FindOrCreateVideoEncoder(s->GetVideoProfile()); s->SetVideoEncoder(ve_ptr); m_pConfig->SetBoolValue(CONFIG_VIDEO_ENABLE, true); } if (s->GetBoolValue(STREAM_AUDIO_ENABLED) && startaudio) { // see if profile has already been started ae_ptr = FindOrCreateAudioEncoder(s->GetAudioProfile()); s->SetAudioEncoder(ae_ptr); m_pConfig->SetBoolValue(CONFIG_AUDIO_ENABLE, true); // when we start the encoder, we will have to pass the channels // configured, as well as the initial sample rate (basically, // replicate SetAudioSrc here... } if (s->GetBoolValue(STREAM_TEXT_ENABLED) && starttext) { // see if profile has already been started te_ptr = FindOrCreateTextEncoder(s->GetTextProfile()); s->SetTextEncoder(te_ptr); m_pConfig->SetBoolValue(CONFIG_TEXT_ENABLE, true); } if (s->GetBoolValue(STREAM_TRANSMIT)) { // check if transmitter has been started on encoder // create rtp destination, add to transmitter if (ve_ptr != NULL) { ve_ptr->AddRtpDestination(s, m_pConfig->GetBoolValue(CONFIG_RTP_DISABLE_TS_OFFSET), m_pConfig->GetIntegerValue(CONFIG_RTP_MCAST_TTL), s->GetIntegerValue(STREAM_VIDEO_SRC_PORT)); } if (ae_ptr != NULL) { ae_ptr->AddRtpDestination(s, m_pConfig->GetBoolValue(CONFIG_RTP_DISABLE_TS_OFFSET), m_pConfig->GetIntegerValue(CONFIG_RTP_MCAST_TTL), s->GetIntegerValue(STREAM_AUDIO_SRC_PORT)); } if (te_ptr != NULL) { te_ptr->AddRtpDestination(s, m_pConfig->GetBoolValue(CONFIG_RTP_DISABLE_TS_OFFSET), m_pConfig->GetIntegerValue(CONFIG_RTP_MCAST_TTL), s->GetIntegerValue(STREAM_TEXT_SRC_PORT)); } createStreamSdp(m_pConfig, s); } if (s->GetBoolValue(STREAM_RECORD)) { // create file sink, add to above encoders. CMediaSink *recorder = s->CreateFileRecorder(m_pConfig); if (ve_ptr != NULL) { ve_ptr->AddSink(recorder); } if (ae_ptr != NULL) { ae_ptr->AddSink(recorder); } if (te_ptr != NULL) { te_ptr->AddSink(recorder); } } s = s->GetNext(); } if (m_audioSource) { m_audioSource->SetAudioSrcSamplesPerFrame(m_maxAudioSamplesPerFrame); debug_message("Setting source sample per frame %u", m_maxAudioSamplesPerFrame); } // If we need raw stuff, we do it here bool createdRaw = false; if (m_pConfig->GetBoolValue(CONFIG_RAW_ENABLE)) { if (m_rawSink == NULL) { m_rawSink = new CRawFileSink(); m_rawSink->SetConfig(m_pConfig); createdRaw = true; } if (m_audioSource != NULL) { m_audioSource->AddSink(m_rawSink); } if (m_videoSource != NULL) { m_videoSource->AddSink(m_rawSink); } if (createdRaw) { m_rawSink->StartThread(); m_rawSink->Start(); } } if (m_pConfig->GetBoolValue(CONFIG_RECORD_RAW_IN_MP4)) { if (m_pConfig->GetBoolValue(CONFIG_RECORD_RAW_IN_MP4_VIDEO) || m_pConfig->GetBoolValue(CONFIG_RECORD_RAW_IN_MP4_AUDIO)) { bool createMp4Raw = false; if (m_mp4RawRecorder == NULL) { m_mp4RawRecorder = new CMp4Recorder(NULL); m_mp4RawRecorder->SetConfig(m_pConfig); createMp4Raw = true; } if (m_audioSource != NULL && m_pConfig->GetBoolValue(CONFIG_RECORD_RAW_IN_MP4_AUDIO)) { m_audioSource->AddSink(m_mp4RawRecorder); } if (m_videoSource != NULL && m_pConfig->GetBoolValue(CONFIG_RECORD_RAW_IN_MP4_VIDEO)) { m_videoSource->AddSink(m_mp4RawRecorder); } if (createMp4Raw) { m_mp4RawRecorder->StartThread(); m_mp4RawRecorder->Start(); } } } // start encoders and any sinks... This may result in some sinks // file, in particular, receiving multiple starts CMediaCodec *mc = m_video_encoder_list; if (mc == NULL && m_videoSource != m_audioSource) { delete m_videoSource; m_videoSource = NULL; } while (mc != NULL) { mc->Start(); mc->StartSinks(); mc = mc->GetNext(); } mc = m_audio_encoder_list; while (mc != NULL) { mc->Start(); mc->StartSinks(); mc = mc->GetNext(); } mc = m_text_encoder_list; while (mc != NULL) { mc->Start(); mc->StartSinks(); mc = mc->GetNext(); } // finally, start sources... if (m_videoSource && m_videoSource == m_audioSource) { m_videoSource->Start(); } else { if (m_audioSource) { m_audioSource->Start(); } if (m_videoSource) { m_videoSource->Start(); } } if (m_textSource != NULL) { m_textSource->Start(); } if (m_videoSource && startaudio) { // force video source to generate a key frame // so that sinks can quickly sync up m_videoSource->RequestKeyFrame(0); } m_started = true; }