ImageSource* OBS::AddGlobalSourceToScene(CTSTR lpName) { XElement *globals = scenesConfig.GetElement(TEXT("global sources")); if(globals) { XElement *globalSourceElement = globals->GetElement(lpName); if(globalSourceElement) { CTSTR lpClass = globalSourceElement->GetString(TEXT("class")); if(lpClass) { ImageSource *newGlobalSource = CreateImageSource(lpClass, globalSourceElement->GetElement(TEXT("data"))); if(newGlobalSource) { App->EnterSceneMutex(); GlobalSourceInfo *info = globalSources.CreateNew(); info->strName = lpName; info->element = globalSourceElement; info->source = newGlobalSource; info->source->BeginScene(); App->LeaveSceneMutex(); return newGlobalSource; } } } } AppWarning(TEXT("OBS::AddGlobalSourceToScene: Could not find global source '%s'"), lpName); return NULL; }
void OBS::CheckSources() { XElement *curSceneElement = App->sceneElement; XElement *sources = curSceneElement->GetElement(TEXT("sources")); if(!sources) return; HWND hwndSources = GetDlgItem(hwndMain, ID_SOURCES); UINT numSources = ListView_GetItemCount(hwndSources); for(UINT i = 0; i < numSources; i++) { bool checked = ListView_GetCheckState(hwndSources, i) > 0; XElement *source =sources->GetElementByID(i); bool curRender = source->GetInt(TEXT("render"), 0) > 0; if(curRender != checked) { source->SetInt(TEXT("render"), (checked)?1:0); if(scene && i < scene->NumSceneItems()) { SceneItem *sceneItem = scene->GetSceneItem(i); sceneItem->bRender = checked; sceneItem->SetRender(checked); } ReportSourceChanged(source->GetName(), source); } } }
XElement* OBS::GetGlobalSourceElement(CTSTR lpName) { XElement *globals = scenesConfig.GetElement(TEXT("global sources")); if(globals) return globals->GetElement(lpName); return NULL; }
bool STDCALL OBS::ConfigGlobalSource(XElement *element, bool bCreating) { XElement *data = element->GetElement(TEXT("data")); CTSTR lpGlobalSourceName = data->GetString(TEXT("name")); XElement *globalSources = App->scenesConfig.GetElement(TEXT("global sources")); if(!globalSources) //shouldn't happen return false; XElement *globalSourceElement = globalSources->GetElement(lpGlobalSourceName); if(!globalSourceElement) //shouldn't happen return false; CTSTR lpClass = globalSourceElement->GetString(TEXT("class")); ClassInfo *classInfo = App->GetImageSourceClass(lpClass); if(!classInfo) //shouldn't happen return false; if(classInfo->configProc) { if(!classInfo->configProc(globalSourceElement, bCreating)) return false; element->SetInt(TEXT("cx"), globalSourceElement->GetInt(TEXT("cx"))); element->SetInt(TEXT("cy"), globalSourceElement->GetInt(TEXT("cy"))); if(App->bRunning) { for(UINT i=0; i<App->globalSources.Num(); i++) { GlobalSourceInfo &info = App->globalSources[i]; if(info.strName.CompareI(lpGlobalSourceName) && info.source) { info.source->UpdateSettings(); break; } } } } return true; }
ServerPingSettings::ServerPingSettings() : SettingsPane() { // class privates pingers = new List<Pinger *>(); InitializeCriticalSection(&pingerMutex); // service UINT serviceCount; XConfig serverData; if(serverData.Open(TEXT("services.xconfig"))) { XElement *services = serverData.GetElement(TEXT("services")); if(services) { serviceCount = services->NumElements(); for(UINT i = 0; i < serviceCount; i++) { XElement *service = services->GetElementByID(i); if (service) { XElement *servers = service->GetElement(TEXT("servers")); if(servers) { UINT numServers = servers->NumDataItems(); for(UINT i=0; i<numServers; i++) { XDataItem *server = servers->GetDataItemByID(i); if (server) { pingers->Add(new Pinger(service, server)); } } } } } } } serverData.Close(); hThread = 0; }
void OBS::SetSourceRender(CTSTR sourceName, bool render) { XElement *curSceneElement = App->sceneElement; XElement *sources = curSceneElement->GetElement(TEXT("sources")); if(!sources) return; HWND hwndSources = GetDlgItem(hwndMain, ID_SOURCES); UINT numSources = ListView_GetItemCount(hwndSources); for(UINT i = 0; i < numSources; i++) { bool checked = ListView_GetCheckState(hwndSources, i) > 0; XElement *source =sources->GetElementByID(i); if(scmp(source->GetName(), sourceName) == 0 && checked != render) { if(scene && i < scene->NumSceneItems()) { SceneItem *sceneItem = scene->GetSceneItem(i); sceneItem->SetRender(render); } else { source->SetInt(TEXT("render"), (render)?1:0); } App->bChangingSources = true; ListView_SetCheckState(hwndSources, i, render); App->bChangingSources = false; ReportSourceChanged(sourceName, source); break; } } }
json_t* OBSAPIMessageHandler::HandleGetCurrentScene(OBSAPIMessageHandler* handler, json_t* message) { OBSEnterSceneMutex(); json_t* ret = GetOkResponse(); XElement* scene = OBSGetSceneElement(); json_object_set_new(ret, "name", json_string_wchar(scene->GetName())); XElement* sources = scene->GetElement(TEXT("sources")); json_t* scene_items = json_array(); if(sources != NULL) { for(UINT i = 0; i < sources->NumElements(); i++) { XElement* source = sources->GetElementByID(i); json_array_append_new(scene_items, getSourceJson(source)); } } json_object_set_new(ret, "sources", scene_items); OBSLeaveSceneMutex(); return ret; }
INT_PTR SettingsPublish::ProcMessage(UINT message, WPARAM wParam, LPARAM lParam) { HWND hwndTemp; switch(message) { case WM_INITDIALOG: { LocalizeWindow(hwnd); RECT serviceRect, saveToFileRect; GetWindowRect(GetDlgItem(hwnd, IDC_SERVICE), &serviceRect); GetWindowRect(GetDlgItem(hwnd, IDC_SAVEPATH), &saveToFileRect); data.fileControlOffset = saveToFileRect.top-serviceRect.top; //-------------------------------------------- hwndTemp = GetDlgItem(hwnd, IDC_MODE); SendMessage(hwndTemp, CB_ADDSTRING, 0, (LPARAM)Str("Settings.Publish.Mode.LiveStream")); SendMessage(hwndTemp, CB_ADDSTRING, 0, (LPARAM)Str("Settings.Publish.Mode.FileOnly")); int mode = LoadSettingComboInt(hwndTemp, TEXT("Publish"), TEXT("Mode"), 0, 2); data.mode = mode; //-------------------------------------------- hwndTemp = GetDlgItem(hwnd, IDC_SERVICE); int itemId = (int)SendMessage(hwndTemp, CB_ADDSTRING, 0, (LPARAM)TEXT("Custom")); SendMessage(hwndTemp, CB_SETITEMDATA, itemId, 0); UINT numServices = 0; XConfig serverData; if(serverData.Open(TEXT("services.xconfig"))) { XElement *services = serverData.GetElement(TEXT("services")); if(services) { numServices = services->NumElements(); for(UINT i=0; i<numServices; i++) { XElement *service = services->GetElementByID(i); itemId = (int)SendMessage(hwndTemp, CB_ADDSTRING, 0, (LPARAM)service->GetName()); SendMessage(hwndTemp, CB_SETITEMDATA, itemId, service->GetInt(TEXT("id"))); } } } int serviceID = AppConfig->GetInt(TEXT("Publish"), TEXT("Service"), 0); if(mode != 0) ShowWindow(hwndTemp, SW_HIDE); //-------------------------------------------- hwndTemp = GetDlgItem(hwnd, IDC_PLAYPATH); LoadSettingEditString(hwndTemp, TEXT("Publish"), TEXT("PlayPath"), NULL); if(mode != 0) ShowWindow(hwndTemp, SW_HIDE); if(serviceID == 0) //custom { ShowWindow(GetDlgItem(hwnd, IDC_SERVERLIST), SW_HIDE); hwndTemp = GetDlgItem(hwnd, IDC_URL); LoadSettingEditString(hwndTemp, TEXT("Publish"), TEXT("URL"), NULL); SendDlgItemMessage(hwnd, IDC_SERVICE, CB_SETCURSEL, 0, 0); } else { ShowWindow(GetDlgItem(hwnd, IDC_URL), SW_HIDE); hwndTemp = GetDlgItem(hwnd, IDC_SERVERLIST); XElement *services = serverData.GetElement(TEXT("services")); if(services) { XElement *service = NULL; numServices = services->NumElements(); for(UINT i=0; i<numServices; i++) { XElement *curService = services->GetElementByID(i); if(curService->GetInt(TEXT("id")) == serviceID) { SendDlgItemMessage(hwnd, IDC_SERVICE, CB_SETCURSEL, i+1, 0); service = curService; break; } } if(service) { XElement *servers = service->GetElement(TEXT("servers")); if(servers) { UINT numServers = servers->NumDataItems(); for(UINT i=0; i<numServers; i++) { XDataItem *server = servers->GetDataItemByID(i); SendMessage(hwndTemp, CB_ADDSTRING, 0, (LPARAM)server->GetName()); } } } } LoadSettingComboString(hwndTemp, TEXT("Publish"), TEXT("URL"), NULL); } if(mode != 0) ShowWindow(hwndTemp, SW_HIDE); //-------------------------------------------- hwndTemp = GetDlgItem(hwnd, IDC_LOWLATENCYMODE); BOOL bLowLatencyMode = AppConfig->GetInt(TEXT("Publish"), TEXT("LowLatencyMode"), 0); SendMessage(hwndTemp, BM_SETCHECK, bLowLatencyMode ? BST_CHECKED : BST_UNCHECKED, 0); //-------------------------------------------- hwndTemp = GetDlgItem(hwnd, IDC_AUTORECONNECT); BOOL bAutoReconnect = AppConfig->GetInt(TEXT("Publish"), TEXT("AutoReconnect"), 1); SendMessage(hwndTemp, BM_SETCHECK, bAutoReconnect ? BST_CHECKED : BST_UNCHECKED, 0); if(mode != 0) ShowWindow(hwndTemp, SW_HIDE); hwndTemp = GetDlgItem(hwnd, IDC_AUTORECONNECT_TIMEOUT); EnableWindow(GetDlgItem(hwnd, IDC_AUTORECONNECT_TIMEOUT_EDIT), bAutoReconnect); EnableWindow(hwndTemp, bAutoReconnect); int retryTime = AppConfig->GetInt(TEXT("Publish"), TEXT("AutoReconnectTimeout"), 10); if(retryTime > 60) retryTime = 60; else if(retryTime < 0) retryTime = 0; SendMessage(hwndTemp, UDM_SETRANGE32, 0, 60); SendMessage(hwndTemp, UDM_SETPOS32, 0, retryTime); if(mode != 0) ShowWindow(hwndTemp, SW_HIDE); //-------------------------------------------- hwndTemp = GetDlgItem(hwnd, IDC_DELAY); int delayTime = AppConfig->GetInt(TEXT("Publish"), TEXT("Delay"), 0); SendMessage(hwndTemp, UDM_SETRANGE32, 0, 900); SendMessage(hwndTemp, UDM_SETPOS32, 0, delayTime); //-------------------------------------------- if(mode != 0) { ShowWindow(GetDlgItem(hwnd, IDC_SERVICE_STATIC), SW_HIDE); ShowWindow(GetDlgItem(hwnd, IDC_PLAYPATH_STATIC), SW_HIDE); ShowWindow(GetDlgItem(hwnd, IDC_URL_STATIC), SW_HIDE); ShowWindow(GetDlgItem(hwnd, IDC_SERVER_STATIC), SW_HIDE); ShowWindow(GetDlgItem(hwnd, IDC_LOWLATENCYMODE), SW_HIDE); ShowWindow(GetDlgItem(hwnd, IDC_AUTORECONNECT_TIMEOUT_STATIC), SW_HIDE); ShowWindow(GetDlgItem(hwnd, IDC_AUTORECONNECT_TIMEOUT_EDIT), SW_HIDE); ShowWindow(GetDlgItem(hwnd, IDC_DELAY_STATIC), SW_HIDE); ShowWindow(GetDlgItem(hwnd, IDC_DELAY_EDIT), SW_HIDE); ShowWindow(GetDlgItem(hwnd, IDC_DELAY), SW_HIDE); ShowWindow(GetDlgItem(hwnd, IDC_KEEPRECORDING), SW_HIDE); //ShowWindow(GetDlgItem(hwnd, IDC_DASHBOARDLINK), SW_HIDE); //ShowWindow(GetDlgItem(hwnd, IDC_DASHBOARDLINK_STATIC), SW_HIDE); ShowWindow(GetDlgItem(hwnd, IDC_SAVETOFILE), SW_HIDE); AdjustWindowPos(GetDlgItem(hwnd, IDC_SAVEPATH_STATIC), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_SAVEPATH), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_BROWSE), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STOPSTREAMHOTKEY_STATIC), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STOPSTREAMHOTKEY), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_CLEARHOTKEY), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STARTSTREAMHOTKEY_STATIC), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STARTSTREAMHOTKEY), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_CLEARHOTKEY_STARTSTREAM), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STARTRECORDINGHOTKEY_STATIC), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STOPRECORDINGHOTKEY_STATIC), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STARTRECORDINGHOTKEY), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STOPRECORDINGHOTKEY), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_CLEARHOTKEY_STARTRECORDING), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_CLEARHOTKEY_STOPRECORDING), 0, -data.fileControlOffset); } //-------------------------------------------- DWORD startHotkey = AppConfig->GetInt(TEXT("Publish"), TEXT("StartStreamHotkey")); SendMessage(GetDlgItem(hwnd, IDC_STARTSTREAMHOTKEY), HKM_SETHOTKEY, startHotkey, 0); //-------------------------------------------- DWORD stopHotkey = AppConfig->GetInt(TEXT("Publish"), TEXT("StopStreamHotkey")); SendMessage(GetDlgItem(hwnd, IDC_STOPSTREAMHOTKEY), HKM_SETHOTKEY, stopHotkey, 0); //-------------------------------------------- startHotkey = AppConfig->GetInt(TEXT("Publish"), TEXT("StartRecordingHotkey")); SendMessage(GetDlgItem(hwnd, IDC_STARTRECORDINGHOTKEY), HKM_SETHOTKEY, startHotkey, 0); //-------------------------------------------- stopHotkey = AppConfig->GetInt(TEXT("Publish"), TEXT("StopRecordingHotkey")); SendMessage(GetDlgItem(hwnd, IDC_STOPRECORDINGHOTKEY), HKM_SETHOTKEY, stopHotkey, 0); //-------------------------------------------- BOOL bKeepRecording = AppConfig->GetInt(TEXT("Publish"), TEXT("KeepRecording")); SendMessage(GetDlgItem(hwnd, IDC_KEEPRECORDING), BM_SETCHECK, bKeepRecording ? BST_CHECKED : BST_UNCHECKED, 0); BOOL bSaveToFile = AppConfig->GetInt(TEXT("Publish"), TEXT("SaveToFile")); SendMessage(GetDlgItem(hwnd, IDC_SAVETOFILE), BM_SETCHECK, bSaveToFile ? BST_CHECKED : BST_UNCHECKED, 0); String path = OSGetDefaultVideoSavePath(L"\\.flv"); CTSTR lpSavePath = AppConfig->GetStringPtr(TEXT("Publish"), TEXT("SavePath"), path.IsValid() ? path.Array() : nullptr); SetWindowText(GetDlgItem(hwnd, IDC_SAVEPATH), lpSavePath); EnableWindow(GetDlgItem(hwnd, IDC_KEEPRECORDING), true); EnableWindow(GetDlgItem(hwnd, IDC_SAVEPATH), true); EnableWindow(GetDlgItem(hwnd, IDC_BROWSE), true); //-------------------------------------------- //SetWindowText(GetDlgItem(hwnd, IDC_DASHBOARDLINK), App->strDashboard); //-------------------------------------------- SetWarningInfo(); ShowWindow(GetDlgItem(hwnd, IDC_INFO), SW_HIDE); SetChangedSettings(false); return TRUE; } case WM_CTLCOLORSTATIC: { switch (GetDlgCtrlID((HWND)lParam)) { case IDC_WARNINGS: SetTextColor((HDC)wParam, RGB(255, 0, 0)); SetBkColor((HDC)wParam, COLORREF(GetSysColor(COLOR_3DFACE))); return (INT_PTR)GetSysColorBrush(COLOR_BTNFACE); } } break; case WM_DESTROY: { } break; case WM_NOTIFY: { NMHDR *nmHdr = (NMHDR*)lParam; if(nmHdr->idFrom == IDC_AUTORECONNECT_TIMEOUT) { if(nmHdr->code == UDN_DELTAPOS) SetChangedSettings(true); } break; } case WM_COMMAND: { bool bDataChanged = false; switch(LOWORD(wParam)) { case IDC_MODE: { if(HIWORD(wParam) != CBN_SELCHANGE) break; int mode = (int)SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0); int swShowControls = (mode == 0) ? SW_SHOW : SW_HIDE; ShowWindow(GetDlgItem(hwnd, IDC_SERVICE), swShowControls); ShowWindow(GetDlgItem(hwnd, IDC_PLAYPATH), swShowControls); int serviceID = (int)SendMessage(GetDlgItem(hwnd, IDC_SERVICE), CB_GETCURSEL, 0, 0); if(serviceID == 0) { ShowWindow(GetDlgItem(hwnd, IDC_SERVERLIST), SW_HIDE); ShowWindow(GetDlgItem(hwnd, IDC_URL), swShowControls); } else { ShowWindow(GetDlgItem(hwnd, IDC_SERVERLIST), swShowControls); ShowWindow(GetDlgItem(hwnd, IDC_URL), SW_HIDE); } if(mode == 0 && data.mode == 1) { AdjustWindowPos(GetDlgItem(hwnd, IDC_SAVEPATH_STATIC), 0, data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_SAVEPATH), 0, data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_BROWSE), 0, data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STOPSTREAMHOTKEY_STATIC), 0, data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STOPSTREAMHOTKEY), 0, data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_CLEARHOTKEY), 0, data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STARTSTREAMHOTKEY_STATIC), 0, data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STARTSTREAMHOTKEY), 0, data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_CLEARHOTKEY_STARTSTREAM), 0, data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STARTRECORDINGHOTKEY_STATIC), 0, data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STOPRECORDINGHOTKEY_STATIC), 0, data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STARTRECORDINGHOTKEY), 0, data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STOPRECORDINGHOTKEY), 0, data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_CLEARHOTKEY_STARTRECORDING), 0, data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_CLEARHOTKEY_STOPRECORDING), 0, data.fileControlOffset); } else if(mode == 1 && data.mode == 0) { AdjustWindowPos(GetDlgItem(hwnd, IDC_SAVEPATH_STATIC), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_SAVEPATH), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_BROWSE), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STOPSTREAMHOTKEY_STATIC), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STOPSTREAMHOTKEY), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_CLEARHOTKEY), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STARTSTREAMHOTKEY_STATIC), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STARTSTREAMHOTKEY), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_CLEARHOTKEY_STARTSTREAM), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STARTRECORDINGHOTKEY_STATIC), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STOPRECORDINGHOTKEY_STATIC), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STARTRECORDINGHOTKEY), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_STOPRECORDINGHOTKEY), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_CLEARHOTKEY_STARTRECORDING), 0, -data.fileControlOffset); AdjustWindowPos(GetDlgItem(hwnd, IDC_CLEARHOTKEY_STOPRECORDING), 0, -data.fileControlOffset); } data.mode = mode; ShowWindow(GetDlgItem(hwnd, IDC_SERVICE_STATIC), swShowControls); ShowWindow(GetDlgItem(hwnd, IDC_PLAYPATH_STATIC), swShowControls); ShowWindow(GetDlgItem(hwnd, IDC_URL_STATIC), swShowControls); ShowWindow(GetDlgItem(hwnd, IDC_SERVER_STATIC), swShowControls); //ShowWindow(GetDlgItem(hwnd, IDC_DASHBOARDLINK), swShowControls); //ShowWindow(GetDlgItem(hwnd, IDC_DASHBOARDLINK_STATIC), swShowControls); ShowWindow(GetDlgItem(hwnd, IDC_LOWLATENCYMODE), swShowControls); ShowWindow(GetDlgItem(hwnd, IDC_AUTORECONNECT), swShowControls); ShowWindow(GetDlgItem(hwnd, IDC_AUTORECONNECT_TIMEOUT), swShowControls); ShowWindow(GetDlgItem(hwnd, IDC_AUTORECONNECT_TIMEOUT_STATIC), swShowControls); ShowWindow(GetDlgItem(hwnd, IDC_AUTORECONNECT_TIMEOUT_EDIT), swShowControls); ShowWindow(GetDlgItem(hwnd, IDC_DELAY_STATIC), swShowControls); ShowWindow(GetDlgItem(hwnd, IDC_DELAY_EDIT), swShowControls); ShowWindow(GetDlgItem(hwnd, IDC_DELAY), swShowControls); ShowWindow(GetDlgItem(hwnd, IDC_SAVETOFILE), swShowControls); ShowWindow(GetDlgItem(hwnd, IDC_KEEPRECORDING), swShowControls); SetWarningInfo(); bDataChanged = true; break; } case IDC_SERVICE: if(HIWORD(wParam) == CBN_SELCHANGE) { int serviceID = (int)SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0); if(serviceID == 0) { ShowWindow(GetDlgItem(hwnd, IDC_SERVERLIST), SW_HIDE); ShowWindow(GetDlgItem(hwnd, IDC_URL), SW_SHOW); SetWindowText(GetDlgItem(hwnd, IDC_URL), NULL); //SetWindowText(GetDlgItem(hwnd, IDC_DASHBOARDLINK), NULL); } else { ShowWindow(GetDlgItem(hwnd, IDC_URL), SW_HIDE); hwndTemp = GetDlgItem(hwnd, IDC_SERVERLIST); ShowWindow(hwndTemp, SW_SHOW); SendMessage(hwndTemp, CB_RESETCONTENT, 0, 0); XConfig serverData; if(serverData.Open(TEXT("services.xconfig"))) { XElement *services = serverData.GetElement(TEXT("services")); if(services) { XElement *service = services->GetElementByID(serviceID-1); if(service) { XElement *servers = service->GetElement(TEXT("servers")); if(servers) { UINT numServers = servers->NumDataItems(); for(UINT i=0; i<numServers; i++) { XDataItem *server = servers->GetDataItemByID(i); SendMessage(hwndTemp, CB_ADDSTRING, 0, (LPARAM)server->GetName()); } } } } } SendMessage(hwndTemp, CB_SETCURSEL, 0, 0); } SetWindowText(GetDlgItem(hwnd, IDC_PLAYPATH), NULL); bDataChanged = true; } SetWarningInfo(); break; case IDC_AUTORECONNECT: if(HIWORD(wParam) == BN_CLICKED) { BOOL bAutoReconnect = SendMessage((HWND)lParam, BM_GETCHECK, 0, 0) == BST_CHECKED; EnableWindow(GetDlgItem(hwnd, IDC_AUTORECONNECT_TIMEOUT), bAutoReconnect); EnableWindow(GetDlgItem(hwnd, IDC_AUTORECONNECT_TIMEOUT_EDIT), bAutoReconnect); SetChangedSettings(true); } break; case IDC_AUTORECONNECT_TIMEOUT_EDIT: if(HIWORD(wParam) == EN_CHANGE) SetChangedSettings(true); break; case IDC_DELAY_EDIT: if(HIWORD(wParam) == EN_CHANGE) bDataChanged = true; break; case IDC_KEEPRECORDING: if(HIWORD(wParam) == BN_CLICKED) { BOOL bKeepRecording = SendMessage((HWND)lParam, BM_GETCHECK, 0, 0) == BST_CHECKED; App->bKeepRecording = bKeepRecording != 0; bDataChanged = true; } break; case IDC_BROWSE: { TCHAR lpFile[512]; OPENFILENAME ofn; zero(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = hwnd; ofn.lpstrFile = lpFile; ofn.nMaxFile = 511; ofn.lpstrFile[0] = 0; ofn.lpstrFilter = TEXT("MP4 File (*.mp4)\0*.mp4\0Flash Video File (*.flv)\0*.flv\0"); ofn.lpstrFileTitle = NULL; ofn.nMaxFileTitle = 0; ofn.nFilterIndex = 1; String path = OSGetDefaultVideoSavePath(); ofn.lpstrInitialDir = AppConfig->GetStringPtr(TEXT("Publish"), TEXT("LastSaveDir"), path.IsValid() ? path.Array() : nullptr); ofn.Flags = OFN_PATHMUSTEXIST; TCHAR curDirectory[512]; GetCurrentDirectory(511, curDirectory); BOOL bChoseFile = GetSaveFileName(&ofn); SetCurrentDirectory(curDirectory); if(*lpFile && bChoseFile) { String strFile = lpFile; strFile.FindReplace(TEXT("\\"), TEXT("/")); String strExtension = GetPathExtension(strFile); if(strExtension.IsEmpty() || (!strExtension.CompareI(TEXT("flv")) && /*!strExtension.CompareI(TEXT("avi")) &&*/ !strExtension.CompareI(TEXT("mp4")))) { switch(ofn.nFilterIndex) { case 1: strFile << TEXT(".mp4"); break; case 2: strFile << TEXT(".flv"); break; /*case 3: strFile << TEXT(".avi"); break;*/ } } String strFilePath = GetPathDirectory(strFile).FindReplace(TEXT("/"), TEXT("\\")) << TEXT("\\"); AppConfig->SetString(TEXT("Publish"), TEXT("LastSaveDir"), strFilePath); strFile.FindReplace(TEXT("/"), TEXT("\\")); SetWindowText(GetDlgItem(hwnd, IDC_SAVEPATH), strFile); bDataChanged = true; } break; } case IDC_LOWLATENCYMODE: case IDC_SAVETOFILE: if(HIWORD(wParam) == BN_CLICKED) bDataChanged = true; break; case IDC_STARTSTREAMHOTKEY: case IDC_STOPSTREAMHOTKEY: //case IDC_DASHBOARDLINK: case IDC_STARTRECORDINGHOTKEY: case IDC_STOPRECORDINGHOTKEY: if(HIWORD(wParam) == EN_CHANGE) SetChangedSettings(true); break; case IDC_PLAYPATH: case IDC_URL: case IDC_SAVEPATH: if(HIWORD(wParam) == EN_CHANGE) bDataChanged = true; break; case IDC_CLEARHOTKEY_STARTSTREAM: if(HIWORD(wParam) == BN_CLICKED) { if(SendMessage(GetDlgItem(hwnd, IDC_STARTSTREAMHOTKEY), HKM_GETHOTKEY, 0, 0)) { SendMessage(GetDlgItem(hwnd, IDC_STARTSTREAMHOTKEY), HKM_SETHOTKEY, 0, 0); SetChangedSettings(true); } } break; case IDC_CLEARHOTKEY: if(HIWORD(wParam) == BN_CLICKED) { if(SendMessage(GetDlgItem(hwnd, IDC_STOPSTREAMHOTKEY), HKM_GETHOTKEY, 0, 0)) { SendMessage(GetDlgItem(hwnd, IDC_STOPSTREAMHOTKEY), HKM_SETHOTKEY, 0, 0); SetChangedSettings(true); } } break; case IDC_CLEARHOTKEY_STARTRECORDING: if (HIWORD(wParam) == BN_CLICKED) { if (SendMessage(GetDlgItem(hwnd, IDC_STARTRECORDINGHOTKEY), HKM_GETHOTKEY, 0, 0)) { SendMessage(GetDlgItem(hwnd, IDC_STARTRECORDINGHOTKEY), HKM_SETHOTKEY, 0, 0); SetChangedSettings(true); } } break; case IDC_CLEARHOTKEY_STOPRECORDING: if (HIWORD(wParam) == BN_CLICKED) { if (SendMessage(GetDlgItem(hwnd, IDC_STOPRECORDINGHOTKEY), HKM_GETHOTKEY, 0, 0)) { SendMessage(GetDlgItem(hwnd, IDC_STOPRECORDINGHOTKEY), HKM_SETHOTKEY, 0, 0); SetChangedSettings(true); } } break; case IDC_SERVERLIST: if(HIWORD(wParam) == CBN_EDITCHANGE || HIWORD(wParam) == CBN_SELCHANGE) bDataChanged = true; break; } if(bDataChanged) { ShowWindow(GetDlgItem(hwnd, IDC_INFO), SW_SHOW); SetChangedSettings(true); } break; } } return FALSE; }
void SettingsPublish::OptimizeSettings() { auto refresh_on_exit = GuardScope([&] { SetWarningInfo(); }); XConfig serverData; if (!serverData.Open(L"services.xconfig")) return; XElement *services = serverData.GetElement(L"services"); if (!services) return; UINT numServices = services->NumElements(); int serviceID = (int)SendMessage(GetDlgItem(hwnd, IDC_SERVICE), CB_GETITEMDATA, SendMessage(GetDlgItem(hwnd, IDC_SERVICE), CB_GETCURSEL, 0, 0), 0); XElement *r = nullptr; for (UINT i = 0; i < numServices; i++) { XElement *service = services->GetElementByID(i); if (service->GetInt(L"id") != serviceID) continue; //check to see if the service we're using has recommendations if (!service->HasItem(L"recommended")) return; r = service->GetElement(L"recommended"); break; } if (!r) return; using optimizers_t = std::vector<std::function<void()>>; optimizers_t optimizers; String changes = Str("Settings.Publish.Optimize.Optimizations"); String currentAudioCodec = AppConfig->GetString(L"Audio Encoding", L"Codec", L"AAC"); int audioBitrate = AppConfig->GetInt(L"Audio Encoding", L"Bitrate", 96); if (r->HasItem(L"ratecontrol")) { bool useCBR = AppConfig->GetInt(L"Video Encoding", L"UseCBR", 1) != 0; CTSTR rc = r->GetString(L"ratecontrol"); if (!scmp(rc, L"cbr") && !useCBR) { optimizers.push_back([] { AppConfig->SetInt(L"Video Encoding", L"UseCBR", 1); }); changes << Str("Settings.Publish.Optimize.UseCBR"); } } if (r->HasItem(L"max bitrate")) { int maxBitrate = AppConfig->GetInt(L"Video Encoding", L"MaxBitrate", 1000); int max_bitrate = r->GetInt(L"max bitrate"); if (maxBitrate > max_bitrate) { optimizers.push_back([max_bitrate] { AppConfig->SetInt(L"Video Encoding", L"MaxBitrate", max_bitrate); }); changes << FormattedString(Str("Settings.Publish.Optimize.Maxbitrate"), max_bitrate); } } if (r->HasItem(L"supported audio codec")) { StringList codecs; r->GetStringList(L"supported audio codec", codecs); if (codecs.FindValueIndex(currentAudioCodec) == INVALID) { String codec = codecs[0]; optimizers.push_back([codec] { AppConfig->SetString(L"Audio Encoding", L"Codec", codec.Array()); AppConfig->SetInt(L"Audio Encoding", L"Format", codec.CompareI(L"AAC") ? 1 : 0); //set to 44.1 kHz in case of MP3, see SettingsEncoding.cpp }); changes << FormattedString(Str("Settings.Publish.Optimize.UnsupportedAudioCodec"), codec.Array()); } } if (r->HasItem(L"max audio bitrate aac") && (!scmp(currentAudioCodec, L"AAC"))) { int maxaudioaac = r->GetInt(L"max audio bitrate aac"); if (audioBitrate > maxaudioaac) { optimizers.push_back([maxaudioaac] { AppConfig->SetInt(L"Audio Encoding", L"Bitrate", maxaudioaac); }); changes << FormattedString(Str("Settings.Publish.Optimize.MaxAudiobitrate"), maxaudioaac); } } if (r->HasItem(L"max audio bitrate mp3") && (!scmp(currentAudioCodec, L"MP3"))) { int maxaudiomp3 = r->GetInt(L"max audio bitrate mp3"); if (audioBitrate > maxaudiomp3) { optimizers.push_back([maxaudiomp3] { AppConfig->SetInt(L"Audio Encoding", L"Bitrate", maxaudiomp3); }); changes << FormattedString(Str("Settings.Publish.Optimize.MaxAudiobitrate"), maxaudiomp3); } } if (r->HasItem(L"profile")) { String currentx264Profile = AppConfig->GetString(L"Video Encoding", L"X264Profile", L"high"); String expectedProfile = r->GetString(L"profile"); if (!expectedProfile.CompareI(currentx264Profile)) { optimizers.push_back([expectedProfile] { AppConfig->SetString(L"Video Encoding", L"X264Profile", expectedProfile); }); changes << FormattedString(Str("Settings.Publish.Optimize.RecommendMainProfile"), expectedProfile.Array()); } } if (r->HasItem(L"keyint")) { int keyframeInt = AppConfig->GetInt(L"Video Encoding", L"KeyframeInterval", 0); int keyint = r->GetInt(L"keyint"); if (!keyframeInt || keyframeInt * 1000 > keyint) { optimizers.push_back([keyint] { AppConfig->SetInt(L"Video Encoding", L"KeyframeInterval", keyint / 1000); }); changes << FormattedString(Str("Settings.Publish.Optimize.Keyint"), keyint / 1000); } } if (OBSMessageBox(hwnd, changes.Array(), Str("Optimize"), MB_OKCANCEL | MB_ICONINFORMATION) != IDOK) return; for (optimizers_t::const_reference i : optimizers) i(); }
void SettingsPublish::SetWarningInfo() { int serviceID = (int)SendMessage(GetDlgItem(hwnd, IDC_SERVICE), CB_GETITEMDATA, SendMessage(GetDlgItem(hwnd, IDC_SERVICE), CB_GETCURSEL, 0, 0), 0); bool bUseCBR = AppConfig->GetInt(TEXT("Video Encoding"), TEXT("UseCBR"), 1) != 0; int maxBitRate = AppConfig->GetInt(TEXT("Video Encoding"), TEXT("MaxBitrate"), 1000); int keyframeInt = AppConfig->GetInt(TEXT("Video Encoding"), TEXT("KeyframeInterval"), 0); int audioBitRate = AppConfig->GetInt(TEXT("Audio Encoding"), TEXT("Bitrate"), 96); String currentx264Profile = AppConfig->GetString(TEXT("Video Encoding"), TEXT("X264Profile"), L"high"); String currentAudioCodec = AppConfig->GetString(TEXT("Audio Encoding"), TEXT("Codec"), TEXT("AAC")); float currentAspect = AppConfig->GetInt(L"Video", L"BaseWidth") / (float)max(1, AppConfig->GetInt(L"Video", L"BaseHeight")); //ignore for non-livestreams if (data.mode != 0) { SetDlgItemText(hwnd, IDC_WARNINGS, TEXT("")); return; } bool hasErrors = false; bool canOptimize = false; String strWarnings; XConfig serverData; if(serverData.Open(TEXT("services.xconfig"))) { XElement *services = serverData.GetElement(TEXT("services")); if(services) { UINT numServices = services->NumElements(); for(UINT i=0; i<numServices; i++) { XElement *service = services->GetElementByID(i); if (service->GetInt(TEXT("id")) == serviceID) { strWarnings = FormattedString(Str("Settings.Publish.Warning.BadSettings"), service->GetName()); //check to see if the service we're using has recommendations if (!service->HasItem(TEXT("recommended"))) { SetDlgItemText(hwnd, IDC_WARNINGS, TEXT("")); return; } XElement *r = service->GetElement(TEXT("recommended")); if (r->HasItem(TEXT("ratecontrol"))) { CTSTR rc = r->GetString(TEXT("ratecontrol")); if (!scmp (rc, TEXT("cbr")) && !bUseCBR) { hasErrors = true; canOptimize = true; strWarnings << Str("Settings.Publish.Warning.UseCBR"); } } if (r->HasItem(TEXT("max bitrate"))) { int max_bitrate = r->GetInt(TEXT("max bitrate")); if (maxBitRate > max_bitrate) { hasErrors = true; canOptimize = true; strWarnings << FormattedString(Str("Settings.Publish.Warning.Maxbitrate"), max_bitrate); } } if (r->HasItem(L"supported audio codec")) { StringList codecs; r->GetStringList(L"supported audio codec", codecs); if (codecs.FindValueIndex(currentAudioCodec) == INVALID) { String msg = Str("Settings.Publish.Warning.UnsupportedAudioCodec"); //good thing OBS only supports MP3 (and AAC), otherwise I'd have to come up with a better translation solution msg.FindReplace(L"$1", codecs[0].Array()); msg.FindReplace(L"$2", currentAudioCodec.Array()); hasErrors = true; canOptimize = true; strWarnings << msg; } } if (r->HasItem(TEXT("max audio bitrate aac")) && (!scmp(currentAudioCodec, TEXT("AAC")))) { int maxaudioaac = r->GetInt(TEXT("max audio bitrate aac")); if (audioBitRate > maxaudioaac) { hasErrors = true; canOptimize = true; strWarnings << FormattedString(Str("Settings.Publish.Warning.MaxAudiobitrate"), maxaudioaac); } } if (r->HasItem(TEXT("max audio bitrate mp3")) && (!scmp(currentAudioCodec, TEXT("MP3")))) { int maxaudiomp3 = r->GetInt(TEXT("max audio bitrate mp3")); if (audioBitRate > maxaudiomp3) { hasErrors = true; canOptimize = true; strWarnings << FormattedString(Str("Settings.Publish.Warning.MaxAudiobitrate"), maxaudiomp3); } } if (r->HasItem(L"video aspect ratio")) { String aspectRatio = r->GetString(L"video aspect ratio"); StringList numbers; aspectRatio.GetTokenList(numbers, ':'); if (numbers.Num() == 2) { float aspect = numbers[0].ToInt() / max(1.f, numbers[1].ToFloat()); if (!CloseFloat(aspect, currentAspect)) { String aspectLocalized = Str("Settings.Video.AspectRatioFormat"); aspectLocalized.FindReplace(L"$1", UIntString(numbers[0].ToInt())); aspectLocalized.FindReplace(L"$2", UIntString(numbers[1].ToInt())); String msg = Str("Settings.Publish.Warning.VideoAspectRatio"); msg.FindReplace(L"$1", aspectLocalized); strWarnings << msg; hasErrors = true; } } } if (r->HasItem(TEXT("profile"))) { String expectedProfile = r->GetString(TEXT("profile")); if (!expectedProfile.CompareI(currentx264Profile)) { hasErrors = true; canOptimize = true; strWarnings << Str("Settings.Publish.Warning.RecommendMainProfile"); } } if (r->HasItem(TEXT("keyint"))) { int keyint = r->GetInt(TEXT("keyint")); if (!keyframeInt || keyframeInt * 1000 > keyint) { hasErrors = true; canOptimize = true; strWarnings << FormattedString(Str("Settings.Publish.Warning.Keyint"), keyint / 1000); } } break; } } } } if (hasErrors) { if (canOptimize) strWarnings << Str("Settings.Publish.Warning.CanOptimize"); SetDlgItemText(hwnd, IDC_WARNINGS, strWarnings.Array()); } else SetDlgItemText(hwnd, IDC_WARNINGS, TEXT("")); SetCanOptimizeSettings(canOptimize); }
bool OBS::SetScene(CTSTR lpScene) { if(bDisableSceneSwitching) return false; HWND hwndScenes = GetDlgItem(hwndMain, ID_SCENES); UINT curSel = (UINT)SendMessage(hwndScenes, LB_GETCURSEL, 0, 0); //------------------------- if(curSel != LB_ERR) { UINT textLen = (UINT)SendMessage(hwndScenes, LB_GETTEXTLEN, curSel, 0); String strLBName; strLBName.SetLength(textLen); SendMessage(hwndScenes, LB_GETTEXT, curSel, (LPARAM)strLBName.Array()); if(!strLBName.CompareI(lpScene)) { UINT id = (UINT)SendMessage(hwndScenes, LB_FINDSTRINGEXACT, -1, (LPARAM)lpScene); if(id == LB_ERR) return false; SendMessage(hwndScenes, LB_SETCURSEL, id, 0); } } else { UINT id = (UINT)SendMessage(hwndScenes, LB_FINDSTRINGEXACT, -1, (LPARAM)lpScene); if(id == LB_ERR) return false; SendMessage(hwndScenes, LB_SETCURSEL, id, 0); } //------------------------- XElement *scenes = scenesConfig.GetElement(TEXT("scenes")); XElement *newSceneElement = scenes->GetElement(lpScene); if(!newSceneElement) return false; if(sceneElement == newSceneElement) return true; sceneElement = newSceneElement; CTSTR lpClass = sceneElement->GetString(TEXT("class")); if(!lpClass) { AppWarning(TEXT("OBS::SetScene: no class found for scene '%s'"), newSceneElement->GetName()); return false; } if(bRunning) { Log(TEXT("++++++++++++++++++++++++++++++++++++++++++++++++++++++")); Log(TEXT(" New Scene")); } XElement *sceneData = newSceneElement->GetElement(TEXT("data")); //------------------------- Scene *newScene = NULL; if(bRunning) newScene = CreateScene(lpClass, sceneData); //------------------------- HWND hwndSources = GetDlgItem(hwndMain, ID_SOURCES); SendMessage(hwndSources, WM_SETREDRAW, (WPARAM)FALSE, (LPARAM) 0); bChangingSources = true; ListView_DeleteAllItems(hwndSources); XElement *sources = sceneElement->GetElement(TEXT("sources")); if(sources) { UINT numSources = sources->NumElements(); ListView_SetItemCount(hwndSources, numSources); for(UINT i=0; i<numSources; i++) { XElement *sourceElement = sources->GetElementByID(i); bool render = sourceElement->GetInt(TEXT("render"), 1) > 0; AddSourceItem((LPWSTR)sourceElement->GetName(), render, i); if(bRunning && newScene) newScene->AddImageSource(sourceElement); } } bChangingSources = false; SendMessage(hwndSources, WM_SETREDRAW, (WPARAM)TRUE, (LPARAM) 0); RedrawWindow(hwndSources, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN); if(scene && newScene->HasMissingSources()) MessageBox(hwndMain, Str("Scene.MissingSources"), NULL, 0); if(bRunning) { //todo: cache scenes maybe? undecided. not really as necessary with global sources OSEnterMutex(hSceneMutex); if(scene) scene->EndScene(); Scene *previousScene = scene; scene = newScene; scene->BeginScene(); if(!bTransitioning) { bTransitioning = true; transitionAlpha = 0.0f; } OSLeaveMutex(hSceneMutex); delete previousScene; } return true; }
bool OBS::SetScene(CTSTR lpScene) { if(bDisableSceneSwitching) return false; HWND hwndScenes = GetDlgItem(hwndMain, ID_SCENES); UINT curSel = (UINT)SendMessage(hwndScenes, LB_GETCURSEL, 0, 0); //------------------------- if(curSel != LB_ERR) { UINT textLen = (UINT)SendMessage(hwndScenes, LB_GETTEXTLEN, curSel, 0); String strLBName; strLBName.SetLength(textLen); SendMessage(hwndScenes, LB_GETTEXT, curSel, (LPARAM)strLBName.Array()); if(!strLBName.CompareI(lpScene)) { UINT id = (UINT)SendMessage(hwndScenes, LB_FINDSTRINGEXACT, -1, (LPARAM)lpScene); if(id == LB_ERR) return false; SendMessage(hwndScenes, LB_SETCURSEL, id, 0); } } else { UINT id = (UINT)SendMessage(hwndScenes, LB_FINDSTRINGEXACT, -1, (LPARAM)lpScene); if(id == LB_ERR) return false; SendMessage(hwndScenes, LB_SETCURSEL, id, 0); } //------------------------- XElement *scenes = scenesConfig.GetElement(TEXT("scenes")); XElement *newSceneElement = scenes->GetElement(lpScene); if(!newSceneElement) return false; if(sceneElement == newSceneElement) return true; sceneElement = newSceneElement; CTSTR lpClass = sceneElement->GetString(TEXT("class")); if(!lpClass) { AppWarning(TEXT("OBS::SetScene: no class found for scene '%s'"), newSceneElement->GetName()); return false; } DWORD sceneChangeStartTime; if(bRunning) { Log(TEXT("++++++++++++++++++++++++++++++++++++++++++++++++++++++")); Log(TEXT(" New Scene")); sceneChangeStartTime = OSGetTime(); } XElement *sceneData = newSceneElement->GetElement(TEXT("data")); //------------------------- Scene *newScene = NULL; if(bRunning) newScene = CreateScene(lpClass, sceneData); //------------------------- HWND hwndSources = GetDlgItem(hwndMain, ID_SOURCES); SendMessage(hwndSources, WM_SETREDRAW, (WPARAM)FALSE, (LPARAM) 0); App->scaleItem = NULL; bChangingSources = true; ListView_DeleteAllItems(hwndSources); bool bSkipTransition = false; XElement *sources = sceneElement->GetElement(TEXT("sources")); if(sources) { UINT numSources = sources->NumElements(); ListView_SetItemCount(hwndSources, numSources); for(UINT i=0; i<numSources; i++) { XElement *sourceElement = sources->GetElementByID(i); String className = sourceElement->GetString(TEXT("class")); if(className == "DeviceCapture") { // There's a capture device in the next scene that isn't a global source, // so let's skip the transition since it won't work anyway. bSkipTransition = true; } } for(UINT i=0; i<numSources; i++) { XElement *sourceElement = sources->GetElementByID(i); bool render = sourceElement->GetInt(TEXT("render"), 1) > 0; InsertSourceItem(i, (LPWSTR)sourceElement->GetName(), render); // Do not add image sources yet in case we're skipping the transition. // This fixes the issue where capture devices sources that used the // same device as one in the previous scene would just go blank // after switching. if(bRunning && newScene && !bSkipTransition) newScene->AddImageSource(sourceElement); } } bChangingSources = false; SendMessage(hwndSources, WM_SETREDRAW, (WPARAM)TRUE, (LPARAM) 0); RedrawWindow(hwndSources, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN); if(scene && newScene && newScene->HasMissingSources()) MessageBox(hwndMain, Str("Scene.MissingSources"), NULL, 0); if(bRunning) { //todo: cache scenes maybe? undecided. not really as necessary with global sources OSEnterMutex(hSceneMutex); UINT numSources; if (scene) { //shutdown previous scene, if any 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)->GlobalSourceLeaveScene(); } } } scene->EndScene(); } Scene *previousScene = scene; scene = newScene; if(newScene && bSkipTransition) { // If we're skipping the transition because of a non-global // DirectShow device, delete the scene here and add the // ImageSources at this point instead. delete previousScene; if(sources) { UINT numSources = sources->NumElements(); for(UINT i=0; i<numSources; i++) { XElement *sourceElement = sources->GetElementByID(i); if(newScene) newScene->AddImageSource(sourceElement); } } } scene->BeginScene(); 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(!bTransitioning && !bSkipTransition) { bTransitioning = true; transitionAlpha = 0.0f; } OSLeaveMutex(hSceneMutex); if(!bSkipTransition) { // Do not delete the previous scene here, since it has already // been deleted. delete previousScene; } DWORD sceneChangeTime = OSGetTime() - sceneChangeStartTime; if (sceneChangeTime >= 500) Log(TEXT("PERFORMANCE WARNING: Scene change took %u ms, maybe some sources should be global sources?"), sceneChangeTime); } if(API != NULL) ReportSwitchScenes(lpScene); return true; }
void SettingsPublish::SetWarningInfo() { int serviceID = (int)SendMessage(GetDlgItem(hwnd, IDC_SERVICE), CB_GETITEMDATA, SendMessage(GetDlgItem(hwnd, IDC_SERVICE), CB_GETCURSEL, 0, 0), 0); bool bUseCBR = AppConfig->GetInt(TEXT("Video Encoding"), TEXT("UseCBR"), 1) != 0; int maxBitRate = AppConfig->GetInt(TEXT("Video Encoding"), TEXT("MaxBitrate"), 1000); int keyframeInt = AppConfig->GetInt(TEXT("Video Encoding"), TEXT("KeyframeInterval"), 0); int audioBitRate = AppConfig->GetInt(TEXT("Audio Encoding"), TEXT("Bitrate"), 96); String currentAudioCodec = AppConfig->GetString(TEXT("Audio Encoding"), TEXT("Codec"), TEXT("AAC")); //ignore for non-livestreams if (data->mode != 0) { SetDlgItemText(hwnd, IDC_WARNINGS, TEXT("")); return; } int errors = 0; String strWarnings; XConfig serverData; if(serverData.Open(TEXT("services.xconfig"))) { XElement *services = serverData.GetElement(TEXT("services")); if(services) { UINT numServices = services->NumElements(); for(UINT i=0; i<numServices; i++) { XElement *service = services->GetElementByID(i); if (service->GetInt(TEXT("id")) == serviceID) { strWarnings = FormattedString(Str("Settings.Publish.Warning.BadSettings"), service->GetName()); //check to see if the service we're using has recommendations if (!service->HasItem(TEXT("recommended"))) { SetDlgItemText(hwnd, IDC_WARNINGS, TEXT("")); return; } XElement *r = service->GetElement(TEXT("recommended")); if (r->HasItem(TEXT("ratecontrol"))) { CTSTR rc = r->GetString(TEXT("ratecontrol")); if (!scmp (rc, TEXT("cbr")) && !bUseCBR) { errors++; strWarnings << Str("Settings.Publish.Warning.UseCBR"); } } if (r->HasItem(TEXT("max bitrate"))) { int max_bitrate = r->GetInt(TEXT("max bitrate")); if (maxBitRate > max_bitrate) { errors++; strWarnings << FormattedString(Str("Settings.Publish.Warning.Maxbitrate"), max_bitrate); } } if (r->HasItem(TEXT("max audio bitrate aac")) && (!scmp(currentAudioCodec, TEXT("AAC")))) { int maxaudioaac = r->GetInt(TEXT("max audio bitrate aac")); if (audioBitRate > maxaudioaac) { errors++; strWarnings << FormattedString(Str("Settings.Publish.Warning.MaxAudiobitrate"), maxaudioaac); } } if (r->HasItem(TEXT("max audio bitrate mp3")) && (!scmp(currentAudioCodec, TEXT("MP3")))) { int maxaudiomp3 = r->GetInt(TEXT("max audio bitrate mp3")); if (audioBitRate > maxaudiomp3) { errors++; strWarnings << FormattedString(Str("Settings.Publish.Warning.MaxAudiobitrate"), maxaudiomp3); } } if (r->HasItem(TEXT("keyint"))) { int keyint = r->GetInt(TEXT("keyint")); if (!keyframeInt || keyframeInt * 1000 > keyint) { errors++; strWarnings << FormattedString(Str("Settings.Publish.Warning.Keyint"), keyint / 1000); } } break; } } } } if (errors) SetDlgItemText(hwnd, IDC_WARNINGS, strWarnings.Array()); else SetDlgItemText(hwnd, IDC_WARNINGS, TEXT("")); }
void SettingsPublish::SetWarningInfo() { int serviceID = (int)SendMessage(GetDlgItem(hwnd, IDC_SERVICE), CB_GETITEMDATA, SendMessage(GetDlgItem(hwnd, IDC_SERVICE), CB_GETCURSEL, 0, 0), 0); bool bUseCBR = AppConfig->GetInt(TEXT("Video Encoding"), TEXT("UseCBR"), 1) != 0; int maxBitRate = AppConfig->GetInt(TEXT("Video Encoding"), TEXT("MaxBitrate"), 1000); int keyframeInt = AppConfig->GetInt(TEXT("Video Encoding"), TEXT("KeyframeInterval"), 0); int audioBitRate = AppConfig->GetInt(TEXT("Audio Encoding"), TEXT("Bitrate"), 96); String currentx264Profile = AppConfig->GetString(TEXT("Video Encoding"), TEXT("X264Profile"), L"high"); String currentAudioCodec = AppConfig->GetString(TEXT("Audio Encoding"), TEXT("Codec"), TEXT("AAC")); //ignore for non-livestreams if (data->mode != 0) { SetDlgItemText(hwnd, IDC_WARNINGS, TEXT("")); return; } int errors = 0; String strWarnings; XConfig serverData; if(serverData.Open(TEXT("services.xconfig"))) { XElement *services = serverData.GetElement(TEXT("services")); if(services) { UINT numServices = services->NumElements(); for(UINT i=0; i<numServices; i++) { XElement *service = services->GetElementByID(i); if (service->GetInt(TEXT("id")) == serviceID) { strWarnings = FormattedString(Str("Settings.Publish.Warning.BadSettings"), service->GetName()); //check to see if the service we're using has recommendations if (!service->HasItem(TEXT("recommended"))) { SetDlgItemText(hwnd, IDC_WARNINGS, TEXT("")); return; } XElement *r = service->GetElement(TEXT("recommended")); if (r->HasItem(TEXT("ratecontrol"))) { CTSTR rc = r->GetString(TEXT("ratecontrol")); if (!scmp (rc, TEXT("cbr")) && !bUseCBR) { errors++; strWarnings << Str("Settings.Publish.Warning.UseCBR"); } } if (r->HasItem(TEXT("max bitrate"))) { int max_bitrate = r->GetInt(TEXT("max bitrate")); if (maxBitRate > max_bitrate) { errors++; strWarnings << FormattedString(Str("Settings.Publish.Warning.Maxbitrate"), max_bitrate); } } if (r->HasItem(TEXT("profile"))) { String expectedProfile = r->GetString(TEXT("profile")); if (!expectedProfile.CompareI(currentx264Profile)) { errors++; strWarnings << Str("Settings.Publish.Warning.RecommendMainProfile"); } } if (r->HasItem(TEXT("max audio bitrate aac")) && (!scmp(currentAudioCodec, TEXT("AAC")))) { int maxaudioaac = r->GetInt(TEXT("max audio bitrate aac")); if (audioBitRate > maxaudioaac) { errors++; strWarnings << FormattedString(Str("Settings.Publish.Warning.MaxAudiobitrate"), maxaudioaac); } } if (r->HasItem(TEXT("max audio bitrate mp3")) && (!scmp(currentAudioCodec, TEXT("MP3")))) { int maxaudiomp3 = r->GetInt(TEXT("max audio bitrate mp3")); if (audioBitRate > maxaudiomp3) { errors++; strWarnings << FormattedString(Str("Settings.Publish.Warning.MaxAudiobitrate"), maxaudiomp3); } } if (r->HasItem(TEXT("keyint"))) { int keyint = r->GetInt(TEXT("keyint")); if (!keyframeInt || keyframeInt * 1000 > keyint) { errors++; strWarnings << FormattedString(Str("Settings.Publish.Warning.Keyint"), keyint / 1000); } } if (r->HasItem(L"supported audio codec")) { StringList codecs; r->GetStringList(L"supported audio codec", codecs); if (codecs.FindValueIndex(currentAudioCodec) == INVALID) { String msg = Str("Settings.Publish.Warning.UnsupportedAudioCodec"); //good thing OBS only supports MP3 (and AAC), otherwise I'd have to come up with a better translation solution msg.FindReplace(L"$1", codecs[0].Array()); msg.FindReplace(L"$2", currentAudioCodec.Array()); errors += 1; strWarnings << msg; } } break; } } } } if (errors) SetDlgItemText(hwnd, IDC_WARNINGS, strWarnings.Array()); else SetDlgItemText(hwnd, IDC_WARNINGS, TEXT("")); }
BOOL ParseUpdateManifest (TCHAR *path, BOOL *updatesAvailable, String &description) { XConfig manifest; XElement *root; if (!manifest.Open(path)) return FALSE; root = manifest.GetRootElement(); DWORD numPackages = root->NumElements(); DWORD totalUpdatableFiles = 0; int priority, bestPriority = 999; for (DWORD i = 0; i < numPackages; i++) { XElement *package; package = root->GetElementByID(i); CTSTR packageName = package->GetName(); //find out if this package is relevant to us String platform = package->GetString(TEXT("platform")); if (!platform) continue; if (scmp(platform, TEXT("all"))) { #ifndef _WIN64 if (scmp(platform, TEXT("Win32"))) continue; #else if (scmp(platform, TEXT("Win64"))) continue; #endif } //what is it? String name = package->GetString(TEXT("name")); String version = package->GetString(TEXT("version")); //figure out where the files belong XDataItem *pathElement = package->GetDataItem(TEXT("path")); if (!pathElement) continue; CTSTR path = pathElement->GetData(); if (path == NULL) path = TEXT(""); if (!IsSafePath(path)) continue; priority = package->GetInt(TEXT("priority"), 999); //get the file list for this package XElement *files = package->GetElement(TEXT("files")); if (!files) continue; DWORD numFiles = files->NumElements(); DWORD numUpdatableFiles = 0; for (DWORD j = 0; j < numFiles; j++) { XElement *file = files->GetElementByID(j); String hash = file->GetString(TEXT("hash")); if (!hash || hash.Length() != 40) continue; String fileName = file->GetName(); if (!fileName) continue; if (!IsSafeFilename(fileName)) continue; String filePath; filePath << path; filePath << fileName; BYTE fileHash[20]; TCHAR fileHashString[41]; if (OSFileExists(filePath)) { if (!CalculateFileHash(filePath, fileHash)) continue; HashToString(fileHash, fileHashString); if (!scmp(fileHashString, hash)) continue; } numUpdatableFiles++; } if (numUpdatableFiles) { if (version.Length()) description << name << TEXT(" (") << version << TEXT(")\r\n"); else description << name << TEXT("\r\n"); if (priority < bestPriority) bestPriority = priority; } totalUpdatableFiles += numUpdatableFiles; numUpdatableFiles = 0; } manifest.Close(); if (totalUpdatableFiles) { if (!FetchUpdaterModule()) return FALSE; } if (bestPriority <= 5) *updatesAvailable = TRUE; else *updatesAvailable = FALSE; return TRUE; }
DWORD WINAPI RTMPPublisher::CreateConnectionThread(RTMPPublisher *publisher) { //------------------------------------------------------ // set up URL bool bRetry = false; bool bSuccess = false; bool bCanRetry = false; String failReason; String strBindIP; int serviceID = AppConfig->GetInt (TEXT("Publish"), TEXT("Service")); String strURL = AppConfig->GetString(TEXT("Publish"), TEXT("URL")); String strPlayPath = AppConfig->GetString(TEXT("Publish"), TEXT("PlayPath")); strURL.KillSpaces(); strPlayPath.KillSpaces(); LPSTR lpAnsiURL = NULL, lpAnsiPlaypath = NULL; RTMP *rtmp = NULL; //-------------------------------- // unbelievably disgusting hack for elgato devices String strOldDirectory; UINT dirSize = GetCurrentDirectory(0, 0); strOldDirectory.SetLength(dirSize); GetCurrentDirectory(dirSize, strOldDirectory.Array()); OSSetCurrentDirectory(API->GetAppPath()); //-------------------------------- if(!strURL.IsValid()) { failReason = TEXT("No server specified to connect to"); goto end; } if(serviceID != 0) { XConfig serverData; if(!serverData.Open(TEXT("services.xconfig"))) { failReason = TEXT("Could not open services.xconfig"); goto end; } XElement *services = serverData.GetElement(TEXT("services")); if(!services) { failReason = TEXT("Could not find any services in services.xconfig"); goto end; } XElement *service = NULL; DWORD numServices = services->NumElements(); for(UINT i=0; i<numServices; i++) { XElement *curService = services->GetElementByID(i); if(curService->GetInt(TEXT("id")) == serviceID) { service = curService; break; } } if(!service) { failReason = TEXT("Could not find the service specified in services.xconfig"); goto end; } XElement *servers = service->GetElement(TEXT("servers")); if(!servers) { failReason = TEXT("Could not find any servers for the service specified in services.xconfig"); goto end; } XDataItem *item = servers->GetDataItem(strURL); if(!item) item = servers->GetDataItemByID(0); strURL = item->GetData(); Log(TEXT("Using RTMP service: %s"), service->GetName()); Log(TEXT(" Server selection: %s"), strURL.Array()); } //------------------------------------------------------ // now back to the elgato directory if it needs the directory changed still to function *sigh* OSSetCurrentDirectory(strOldDirectory); //------------------------------------------------------ rtmp = RTMP_Alloc(); RTMP_Init(rtmp); RTMP_LogSetCallback(librtmpErrorCallback); //RTMP_LogSetLevel(RTMP_LOGERROR); lpAnsiURL = strURL.CreateUTF8String(); lpAnsiPlaypath = strPlayPath.CreateUTF8String(); if(!RTMP_SetupURL2(rtmp, lpAnsiURL, lpAnsiPlaypath)) { failReason = Str("Connection.CouldNotParseURL"); goto end; } char *rtmpUser = AppConfig->GetString(TEXT("Publish"), TEXT("Username")).CreateUTF8String(); char *rtmpPass = AppConfig->GetString(TEXT("Publish"), TEXT("Password")).CreateUTF8String(); if (rtmpUser) { rtmp->Link.pubUser.av_val = rtmpUser; rtmp->Link.pubUser.av_len = (int)strlen(rtmpUser); } if (rtmpPass) { rtmp->Link.pubPasswd.av_val = rtmpPass; rtmp->Link.pubPasswd.av_len = (int)strlen(rtmpPass); } RTMP_EnableWrite(rtmp); //set it to publish rtmp->Link.swfUrl.av_len = rtmp->Link.tcUrl.av_len; rtmp->Link.swfUrl.av_val = rtmp->Link.tcUrl.av_val; /*rtmp->Link.pageUrl.av_len = rtmp->Link.tcUrl.av_len; rtmp->Link.pageUrl.av_val = rtmp->Link.tcUrl.av_val;*/ rtmp->Link.flashVer.av_val = "FMLE/3.0 (compatible; FMSc/1.0)"; rtmp->Link.flashVer.av_len = (int)strlen(rtmp->Link.flashVer.av_val); //----------------------------------------- UINT tcpBufferSize = AppConfig->GetInt(TEXT("Publish"), TEXT("TCPBufferSize"), 64*1024); if(tcpBufferSize < 8192) tcpBufferSize = 8192; else if(tcpBufferSize > 1024*1024) tcpBufferSize = 1024*1024; rtmp->m_outChunkSize = 4096;//RTMP_DEFAULT_CHUNKSIZE;// rtmp->m_bSendChunkSizeInfo = TRUE; rtmp->m_bUseNagle = TRUE; strBindIP = AppConfig->GetString(TEXT("Publish"), TEXT("BindToIP"), TEXT("Default")); if (scmp(strBindIP, TEXT("Default"))) { rtmp->m_bindIP.addr.sin_family = AF_INET; rtmp->m_bindIP.addrLen = sizeof(rtmp->m_bindIP.addr); if (WSAStringToAddress(strBindIP.Array(), AF_INET, NULL, (LPSOCKADDR)&rtmp->m_bindIP.addr, &rtmp->m_bindIP.addrLen) == SOCKET_ERROR) { // no localization since this should rarely/never happen failReason = TEXT("WSAStringToAddress: Could not parse address"); goto end; } } LogInterfaceType(rtmp); //----------------------------------------- if(!RTMP_Connect(rtmp, NULL)) { failReason = Str("Connection.CouldNotConnect"); failReason << TEXT("\r\n\r\n") << RTMPPublisher::GetRTMPErrors(); bCanRetry = true; goto end; } if(!RTMP_ConnectStream(rtmp, 0)) { failReason = Str("Connection.InvalidStream"); failReason << TEXT("\r\n\r\n") << RTMPPublisher::GetRTMPErrors(); bCanRetry = true; goto end; } //----------------------------------------- OSDebugOut(TEXT("Connected: %u\r\n"), OSGetTime()); publisher->RequestKeyframe(1000); //----------------------------------------- bSuccess = true; end: if (lpAnsiURL) Free(lpAnsiURL); if (lpAnsiPlaypath) Free(lpAnsiPlaypath); if(!bSuccess) { if(rtmp) { RTMP_Close(rtmp); RTMP_Free(rtmp); } if(failReason.IsValid()) App->SetStreamReport(failReason); if(!publisher->bStopping) PostMessage(hwndMain, OBS_REQUESTSTOP, bCanRetry ? 0 : 1, 0); Log(TEXT("Connection to %s failed: %s"), strURL.Array(), failReason.Array()); publisher->bStopping = true; } else { publisher->Init(rtmp, tcpBufferSize); publisher->bConnected = true; publisher->bConnecting = false; } return 0; }
void OBS::Start(bool recordingOnly) { if(bRunning && !bRecording) 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 && networkMode == 0 && delayTime == 0 && !recordingOnly) { 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.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) { DisableMenusWhileStreaming(false); OSLeaveMutex (hStartupShutdownMutex); bStartingUp = false; return; } else if (ret == IDRETRY) { goto retryHookTest; } alreadyWarnedAboutModules = true; } String strPatchesError; if (OSIncompatiblePatchesLoaded(strPatchesError)) { DisableMenusWhileStreaming(true); OSLeaveMutex (hStartupShutdownMutex); MessageBox(hwndMain, strPatchesError.Array(), NULL, MB_ICONERROR); Log(TEXT("Incompatible patches detected.")); 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) 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) MessageBox(hwndMain, strError, NULL, MB_ICONERROR); else DialogBox(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 = 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; 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: %uhz", sampleRateHz); //------------------------------------------------------------------ AudioDeviceList playbackDevices; bool useInputDevices = AppConfig->GetInt(L"Audio", L"UseInputDevices", 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.")); } 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) 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); 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()) 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; //------------------------------------------------------------- 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; 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, videoEncoderErrors); else if(AppConfig->GetInt(TEXT("Video Encoding"), TEXT("UseNVENC")) != 0) 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; MessageBox(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)) ReportStartStreamingTrigger(); //------------------------------------------------------------- // Ensure that the render frame is properly sized ResizeRenderFrame(true); //------------------------------------------------------------- StartRecording(); //------------------------------------------------------------- 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(); }
bool OBS::SetScene(CTSTR lpScene) { if(bDisableSceneSwitching) return false; HWND hwndScenes = GetDlgItem(hwndMain, ID_SCENES); UINT curSel = (UINT)SendMessage(hwndScenes, LB_GETCURSEL, 0, 0); //------------------------- if(curSel != LB_ERR) { UINT textLen = (UINT)SendMessage(hwndScenes, LB_GETTEXTLEN, curSel, 0); String strLBName; strLBName.SetLength(textLen); SendMessage(hwndScenes, LB_GETTEXT, curSel, (LPARAM)strLBName.Array()); if(!strLBName.CompareI(lpScene)) { UINT id = (UINT)SendMessage(hwndScenes, LB_FINDSTRINGEXACT, -1, (LPARAM)lpScene); if(id == LB_ERR) return false; SendMessage(hwndScenes, LB_SETCURSEL, id, 0); } } else { UINT id = (UINT)SendMessage(hwndScenes, LB_FINDSTRINGEXACT, -1, (LPARAM)lpScene); if(id == LB_ERR) return false; SendMessage(hwndScenes, LB_SETCURSEL, id, 0); } //------------------------- XElement *scenes = scenesConfig.GetElement(TEXT("scenes")); XElement *newSceneElement = scenes->GetElement(lpScene); if(!newSceneElement) return false; if(sceneElement == newSceneElement) return true; sceneElement = newSceneElement; CTSTR lpClass = sceneElement->GetString(TEXT("class")); if(!lpClass) { AppWarning(TEXT("OBS::SetScene: no class found for scene '%s'"), newSceneElement->GetName()); return false; } DWORD sceneChangeStartTime; if(bRunning) { Log(TEXT("++++++++++++++++++++++++++++++++++++++++++++++++++++++")); Log(TEXT(" New Scene")); sceneChangeStartTime = OSGetTime(); } XElement *sceneData = newSceneElement->GetElement(TEXT("data")); //------------------------- Scene *newScene = NULL; if(bRunning) newScene = CreateScene(lpClass, sceneData); //------------------------- HWND hwndSources = GetDlgItem(hwndMain, ID_SOURCES); SendMessage(hwndSources, WM_SETREDRAW, (WPARAM)FALSE, (LPARAM) 0); bChangingSources = true; ListView_DeleteAllItems(hwndSources); XElement *sources = sceneElement->GetElement(TEXT("sources")); if(sources) { UINT numSources = sources->NumElements(); ListView_SetItemCount(hwndSources, numSources); for(UINT i=0; i<numSources; i++) { XElement *sourceElement = sources->GetElementByID(i); bool render = sourceElement->GetInt(TEXT("render"), 1) > 0; InsertSourceItem(i, (LPWSTR)sourceElement->GetName(), render); if(bRunning && newScene) newScene->AddImageSource(sourceElement); } } bChangingSources = false; SendMessage(hwndSources, WM_SETREDRAW, (WPARAM)TRUE, (LPARAM) 0); RedrawWindow(hwndSources, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN); if(scene && newScene && newScene->HasMissingSources()) MessageBox(hwndMain, Str("Scene.MissingSources"), NULL, 0); if(bRunning) { //todo: cache scenes maybe? undecided. not really as necessary with global sources OSEnterMutex(hSceneMutex); UINT numSources; if (scene) { //shutdown previous scene, if any 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)->GlobalSourceLeaveScene(); } } } scene->EndScene(); } Scene *previousScene = scene; scene = newScene; scene->BeginScene(); 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(!bTransitioning) { bTransitioning = true; transitionAlpha = 0.0f; } OSLeaveMutex(hSceneMutex); delete previousScene; DWORD sceneChangeTime = OSGetTime() - sceneChangeStartTime; if (sceneChangeTime >= 500) Log(TEXT("PERFORMANCE WARNING: Scene change took %u ms, maybe some sources should be global sources?"), sceneChangeTime); } if(API != NULL) ReportSwitchScenes(lpScene); return true; }