From f99d74d00858a95ea95ed7866ac06a5bfc8b0edb Mon Sep 17 00:00:00 2001 From: jim Date: Thu, 20 Sep 2012 23:07:10 -0700 Subject: [PATCH] 0.38a --- AUTHORS | 30 +++- ChangeLog | 16 ++ OBS.rc | 29 ++-- OBSApi/APIInterface.h | 10 +- OBSApi/Scene.cpp | 48 ++++++ OBSApi/Scene.h | 1 + OBSApi/Utility/ConfigFile.cpp | 29 +++- OBSApi/Utility/ConfigFile.h | 3 + OBSApi/Utility/XFile.h | 1 - OBSApi/Utility/XFile_Windows.cpp | 2 +- OBSApi/Utility/XT.cpp | 41 +++-- OBSApi/Utility/XT.h | 5 +- Source/D3D10System.cpp | 10 +- Source/MMDeviceAudioSource.cpp | 6 +- Source/Main.cpp | 286 ++++++++++++++++++++++++------- Source/Main.h | 7 +- Source/OBS.cpp | 165 +++++++++++++----- Source/OBS.h | 14 +- Source/RTMPPublisher.cpp | 13 +- Source/Settings.cpp | 205 +++++++++++++++++++++- Source/WindowStuff.cpp | 53 +++++- TODO | 1 - resource.h | 5 +- rundir/locale/en.txt | 13 +- rundir/locale/ja.txt | 41 +++-- rundir/locale/ru.txt | 84 +++++---- 26 files changed, 891 insertions(+), 227 deletions(-) diff --git a/AUTHORS b/AUTHORS index 91bef4b7..0ec8d1c7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,9 +1,25 @@ -Hugh "Jim" Bailey (obs.jim@gmail.com) - Primary author and currently only guy working on the project right now. Damnit Jim! +Author: + Hugh "Jim" Bailey (obs.jim@gmail.com) - Primary author. Damnit Jim! -Also, special thanks to: +Additional Coding: + Richard "R1CH" Stanway - Tons of bugfixes, and teaching the ways of git enlightenment. + +Translations: + Andres Lasn - Estonian translation + Johannes Grill and Johannes Kroker - German translations + August Kind Svendsen - Norwegian translation + Alan Antunes - French translation + Hath Ington - Bulgarian translation + xuok and R3mbrant - Russian translations + +Notable super awesome testers: + Kyle "Kendobear" Asher - A guy who spent weeks helping pinpoint a bug + specific to his system. This guy deserves a + medal for his patience. + + D2Ultima/Frostshocker/Modnite - These guys helped me test the major network + code updates, as well as helped point out + compatibility issues for certain systems. + The greatly improved network code would not + have been possible without their help. - Andres Lasn - Estonian translation - Johannes Grill and Johannes Kroker - German translation - August Kind Svendsen - Norwegian translation - Alan Antunes - French translation - Hath Ington - Bulgarian translation diff --git a/ChangeLog b/ChangeLog index cf4f92d4..2078d404 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,22 @@ (yyyy-mm-dd) +------------------------------- + +2012-9-20 - 0.38 (alpha) + * Changed the way settings are saved. All settings are now saved + to the windows [user]/AppData/Roaming/OBS directory. + * Made it so up to 20 log files are saved at a given time, so that + older logs are not necessarily just overwritten. + * Added setting profiles so the user can have different settings + for different situations. + * Fixed a major mic mug with mono mics. Don't know how I let that + one slip by me for so long. + * More DShow bug fixes by R1CH + * Added a popup menu when right clicking on the render frame that + lets the user see the FPS or disable the render view. + * Improved API hotkey code + ------------------------------- 2012-9-16 - 0.37 (alpha) diff --git a/OBS.rc b/OBS.rc index 030a9cbb..3041ddb2 100644 --- a/OBS.rc +++ b/OBS.rc @@ -57,11 +57,12 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTI CAPTION "Settings" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - DEFPUSHBUTTON "OK",IDOK,338,294,67,14 - PUSHBUTTON "Cancel",IDCANCEL,411,294,67,14 - PUSHBUTTON "Apply",IDC_APPLY,483,294,67,14 + DEFPUSHBUTTON "OK",IDOK,339,294,67,14 + PUSHBUTTON "Cancel",IDCANCEL,412,294,67,14 + PUSHBUTTON "Apply",IDC_APPLY,484,294,67,14 LISTBOX IDC_SETTINGSLIST,3,4,118,287,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP CONTROL "X^eBbN",IDC_SUBDIALOG,"Static",SS_LEFTNOWORDWRAP | NOT WS_VISIBLE | WS_GROUP,124,5,427,287,WS_EX_STATICEDGE + PUSHBUTTON "Settings.Defaults",IDC_DEFAULTS,238,294,67,14,NOT WS_VISIBLE END IDD_SETTINGS_PUBLISH DIALOGEX 0, 0, 427, 287 @@ -73,8 +74,6 @@ BEGIN COMBOBOX IDC_MODE,144,10,126,59,CBS_DROPDOWNLIST | WS_DISABLED | WS_VSCROLL | WS_TABSTOP RTEXT "Settings.Publish.Service",IDC_SERVICE_STATIC,4,29,138,8 COMBOBOX IDC_SERVICE,144,27,126,59,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - RTEXT "Settings.Publish.Username",IDC_USERNAME_STATIC,4,220,138,8,NOT WS_VISIBLE - EDITTEXT IDC_USERNAME,144,218,226,14,ES_AUTOHSCROLL | NOT WS_VISIBLE RTEXT "Settings.Publish.Playpath",IDC_PLAYPATH_STATIC,4,64,138,8 EDITTEXT IDC_PLAYPATH,144,61,226,14,ES_PASSWORD | ES_AUTOHSCROLL RTEXT "Settings.Publish.ChannelName",IDC_CHANNELNAME_STATIC,4,47,138,8 @@ -83,7 +82,6 @@ BEGIN COMBOBOX IDC_SERVERLIST,144,79,226,59,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT "Settings.Info",IDC_INFO,4,119,408,37,NOT WS_VISIBLE EDITTEXT IDC_SERVEREDIT,144,78,226,14,ES_AUTOHSCROLL - CONTROL "Settings.Publish.BufferSends",IDC_BUFFERSENDS,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,144,98,276,10 END IDD_SETTINGS_ENCODING DIALOGEX 0, 0, 427, 287 @@ -136,9 +134,14 @@ STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU EXSTYLE WS_EX_CONTROLPARENT FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - COMBOBOX IDC_LANGUAGE,144,10,195,126,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP - RTEXT "Settings.General.Language",IDC_STATIC,4,12,138,8 - LTEXT "Settings.General.Restart",IDC_INFO,11,49,409,48,NOT WS_VISIBLE + COMBOBOX IDC_LANGUAGE,147,23,195,126,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + RTEXT "Settings.General.Language",IDC_STATIC,7,25,138,8 + LTEXT "Settings.General.Restart",IDC_INFO,7,90,409,48,NOT WS_VISIBLE + COMBOBOX IDC_PROFILE,147,41,195,126,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP + RTEXT "Settings.General.Profile",IDC_STATIC,7,44,138,8 + PUSHBUTTON "Settings.General.Add",IDC_ADD,174,57,55,14 + PUSHBUTTON "Settings.General.Remove",IDC_REMOVE,288,57,55,14 + PUSHBUTTON "Settings.General.Rename",IDC_RENAME,231,57,55,14 END IDD_SETTINGS_AUDIO DIALOGEX 0, 0, 427, 287 @@ -399,8 +402,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,3,6,0 - PRODUCTVERSION 0,3,6,0 + FILEVERSION 0,3,8,0 + PRODUCTVERSION 0,3,8,0 FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS 0x1L @@ -416,12 +419,12 @@ BEGIN BLOCK "041104b0" BEGIN VALUE "FileDescription", "Open Broadcaster Software" - VALUE "FileVersion", "0, 3, 7, 0" + VALUE "FileVersion", "0, 3, 8, 0" VALUE "InternalName", "OBS" VALUE "LegalCopyright", "Copyright (C) 2012" VALUE "OriginalFilename", "OBS.exe" VALUE "ProductName", "Open Broadcaster Software" - VALUE "ProductVersion", "0, 3, 7, 0" + VALUE "ProductVersion", "0, 3, 8, 0" END END BLOCK "VarFileInfo" diff --git a/OBSApi/APIInterface.h b/OBSApi/APIInterface.h index 3043ba30..8b76d1c2 100644 --- a/OBSApi/APIInterface.h +++ b/OBSApi/APIInterface.h @@ -23,7 +23,11 @@ typedef LPVOID (STDCALL* OBSCREATEPROC)(XElement*); //data typedef bool (STDCALL* OBSCONFIGPROC)(XElement*, bool); //element, bInitializing -typedef void (STDCALL* OBSHOTKEYPROC)(DWORD, UPARAM); +typedef void (STDCALL* OBSHOTKEYPROC)(DWORD, UPARAM, bool); + +#define HOTKEY_SHIFT 0x1 +#define HOTKEY_CONTROL 0x2 +#define HOTKEY_ALT 0x4 class APIInterface { @@ -52,6 +56,7 @@ public: virtual CTSTR GetSceneName() const=0; virtual XElement* GetSceneElement()=0; + //low-order word is VK, high-order word is modifier. equivalent to the value given by hotkey controls virtual bool HotkeyExists(DWORD hotkey) const=0; virtual bool CreateHotkey(DWORD hotkey, OBSHOTKEYPROC hotkeyProc, UPARAM param)=0; virtual void DeleteHotkey(DWORD hotkey)=0; @@ -64,6 +69,9 @@ public: virtual HWND GetMainWindow() const=0; + virtual CTSTR GetAppDataPath() const=0; + virtual String GetPluginDataPath() const=0; + inline bool SSE2Available() {return bSSE2Availabe;} }; diff --git a/OBSApi/Scene.cpp b/OBSApi/Scene.cpp index fc60f3f7..bbca1778 100644 --- a/OBSApi/Scene.cpp +++ b/OBSApi/Scene.cpp @@ -117,12 +117,18 @@ void SceneItem::MoveToBottom() Scene::~Scene() { + traceIn(Scene::~Scene); + for(UINT i=0; iGetName()) != NULL) { AppWarning(TEXT("Scene source '%s' already in scene. actually, no one should get this error. if you do send it to jim immidiately."), sourceElement->GetName()); @@ -154,6 +160,7 @@ SceneItem* Scene::AddImageSource(XElement *sourceElement) item->size = Vect2(cx, cy); API->EnterSceneMutex(); + if(bSceneStarted) source->BeginScene(); sceneItems << item; API->LeaveSceneMutex(); @@ -161,13 +168,20 @@ SceneItem* Scene::AddImageSource(XElement *sourceElement) bMissingSources = true; return item; + + traceOut; } void Scene::RemoveImageSource(SceneItem *item) { + traceIn(Scene::RemoveImageSource); + + if(bSceneStarted) item->source->EndScene(); item->GetElement()->GetParent()->RemoveElement(item->GetElement()); sceneItems.RemoveItem(item); delete item; + + traceOut; } void Scene::RemoveImageSource(CTSTR lpName) @@ -184,26 +198,36 @@ void Scene::RemoveImageSource(CTSTR lpName) void Scene::Preprocess() { + traceIn(Scene::Preprocess); + for(UINT i=0; isource) item->source->Preprocess(); } + + traceOut; } void Scene::Tick(float fSeconds) { + traceIn(Scene::Tick); + for(UINT i=0; isource) item->source->Tick(fSeconds); } + + traceOut; } void Scene::Render() { + traceIn(Scene::Render); + GS->ClearColorBuffer(); for(UINT i=0; isource) item->source->Render(item->pos, item->size); } + + traceOut; } void Scene::RenderSelections() { + traceIn(Scene::RenderSelections); + for(UINT i=0; isource) item->source->BeginScene(); } + + bSceneStarted = true; + + traceOut; } void Scene::EndScene() { + traceIn(Scene::EndScene); + + if(!bSceneStarted) + return; + for(UINT i=0; isource) item->source->EndScene(); } + + bSceneStarted = false; + + traceOut; } diff --git a/OBSApi/Scene.h b/OBSApi/Scene.h index a794acdf..6d73495e 100644 --- a/OBSApi/Scene.h +++ b/OBSApi/Scene.h @@ -91,6 +91,7 @@ class BASE_EXPORT Scene friend class SceneItem; friend class OBS; + bool bSceneStarted; bool bMissingSources; List sceneItems; diff --git a/OBSApi/Utility/ConfigFile.cpp b/OBSApi/Utility/ConfigFile.cpp index 85875bc2..fec1a2bc 100644 --- a/OBSApi/Utility/ConfigFile.cpp +++ b/OBSApi/Utility/ConfigFile.cpp @@ -35,8 +35,6 @@ BOOL ConfigFile::Create(CTSTR lpConfigFile) { strFileName = lpConfigFile; - lpFileData = NULL; - bOpen = 0; if(LoadFile(XFILE_CREATEALWAYS)) LoadData(); @@ -49,8 +47,6 @@ BOOL ConfigFile::Create(CTSTR lpConfigFile) BOOL ConfigFile::Open(CTSTR lpConfigFile, BOOL bOpenAlways) { strFileName = lpConfigFile; - lpFileData = NULL; - bOpen = 0; if(LoadFile(bOpenAlways ? XFILE_OPENALWAYS : XFILE_OPENEXISTING)) LoadData(); @@ -62,9 +58,6 @@ BOOL ConfigFile::Open(CTSTR lpConfigFile, BOOL bOpenAlways) BOOL ConfigFile::LoadFile(DWORD dwOpenMode) { - if(bOpen) - Close(); - XFile file; if(!file.Open(strFileName, XFILE_READ, dwOpenMode)) { @@ -72,6 +65,9 @@ BOOL ConfigFile::LoadFile(DWORD dwOpenMode) return 0; } + if(bOpen) + Close(); + dwLength = file.GetFileSize(); LPSTR lpTempFileData = (LPSTR)Allocate(dwLength+5); @@ -180,6 +176,23 @@ void ConfigFile::Close() bOpen = 0; } +BOOL ConfigFile::SaveAs(CTSTR lpPath) +{ + XFile newFile; + if(!newFile.Open(lpPath, XFILE_WRITE, XFILE_CREATEALWAYS)) + return FALSE; + + strFileName = lpPath; + newFile.Write("\xEF\xBB\xBF", 3); + newFile.WriteAsUTF8(lpFileData); + return TRUE; +} + +void ConfigFile::SetFilePath(CTSTR lpPath) +{ + strFileName = lpPath; +} + String ConfigFile::GetString(CTSTR lpSection, CTSTR lpKey, CTSTR def) { assert(lpSection); @@ -578,7 +591,7 @@ void ConfigFile::SetString(CTSTR lpSection, CTSTR lpKey, CTSTR lpString) return; if(!lpString) - return; + lpString = TEXT(""); SetKey(lpSection, lpKey, lpString); } diff --git a/OBSApi/Utility/ConfigFile.h b/OBSApi/Utility/ConfigFile.h index ffbf6995..c06a01d9 100644 --- a/OBSApi/Utility/ConfigFile.h +++ b/OBSApi/Utility/ConfigFile.h @@ -48,6 +48,9 @@ public: BOOL Open(CTSTR lpConfigFile, BOOL bOpenAlways=FALSE); void Close(); + BOOL SaveAs(CTSTR lpPath); + void SetFilePath(CTSTR lpPath); + String GetString(CTSTR lpSection, CTSTR lpKey, CTSTR def=NULL); CTSTR GetStringPtr(CTSTR lpSection, CTSTR lpKey, CTSTR def=NULL); int GetInt(CTSTR lpSection, CTSTR lpKey, int def=0); diff --git a/OBSApi/Utility/XFile.h b/OBSApi/Utility/XFile.h index 9375e386..07f5a7c1 100644 --- a/OBSApi/Utility/XFile.h +++ b/OBSApi/Utility/XFile.h @@ -67,7 +67,6 @@ public: LPSTR lpFileDataUTF8 = (LPSTR)Allocate(dwFileSize+1); lpFileDataUTF8[dwFileSize] = 0; Read(lpFileDataUTF8, dwFileSize); - Close(); strIn = String(lpFileDataUTF8); Free(lpFileDataUTF8); diff --git a/OBSApi/Utility/XFile_Windows.cpp b/OBSApi/Utility/XFile_Windows.cpp index 69c2651a..4b39f8ad 100644 --- a/OBSApi/Utility/XFile_Windows.cpp +++ b/OBSApi/Utility/XFile_Windows.cpp @@ -182,7 +182,7 @@ BOOL XFile::SetFileSize(DWORD dwSize) if(dwPos > dwSize) dwPos = dwSize; - SetPos(dwSize, FILE_BEGIN); + SetPos(dwSize, XFILE_BEGIN); return SetEndOfFile(hFile); traceOutFast; diff --git a/OBSApi/Utility/XT.cpp b/OBSApi/Utility/XT.cpp index e1ee8c72..2782c1ae 100644 --- a/OBSApi/Utility/XT.cpp +++ b/OBSApi/Utility/XT.cpp @@ -30,7 +30,7 @@ SafeList TickList; Alloc *MainAllocator = NULL; BOOL bBaseLoaded = FALSE; BOOL bDebugBreak = TRUE; -CTSTR lpLogFileName = TEXT("XT.log"); +TCHAR lpLogFileName[260] = TEXT("XT.log"); XFile LogFile; StringList TraceFuncList; @@ -40,25 +40,19 @@ void STDCALL CriticalExit(); void STDCALL OpenLogFile(); void STDCALL CloseLogFile(); +void STDCALL OSInit(); +void STDCALL OSExit(); + BOOL STDCALL InitXT(CTSTR logFile, CTSTR allocatorName) { if(!bBaseLoaded) { if(logFile) - lpLogFileName = logFile; + scpy(lpLogFileName, logFile); OSInit(); - if(scmpi(allocatorName, TEXT("DebugAlloc")) == 0) - MainAllocator = new DebugAlloc; - else if (scmpi(allocatorName, TEXT("DefaultAlloc")) == 0) - MainAllocator = new DefaultAlloc; - else if (scmpi(allocatorName, TEXT("SeriousMemoryDebuggingAlloc")) == 0) - MainAllocator = new SeriousMemoryDebuggingAlloc; - else - MainAllocator = new FastAlloc; - - locale = new LocaleStringLookup; + ResetXTAllocator(allocatorName); bBaseLoaded = 1; } @@ -66,6 +60,29 @@ BOOL STDCALL InitXT(CTSTR logFile, CTSTR allocatorName) } +void STDCALL InitXTLog(CTSTR logFile) +{ + if(logFile) + scpy(lpLogFileName, logFile); +} + +void STDCALL ResetXTAllocator(CTSTR lpAllocator) +{ + delete locale; + delete MainAllocator; + + if(scmpi(lpAllocator, TEXT("DebugAlloc")) == 0) + MainAllocator = new DebugAlloc; + else if (scmpi(lpAllocator, TEXT("DefaultAlloc")) == 0) + MainAllocator = new DefaultAlloc; + else if (scmpi(lpAllocator, TEXT("SeriousMemoryDebuggingAlloc")) == 0) + MainAllocator = new SeriousMemoryDebuggingAlloc; + else + MainAllocator = new FastAlloc; + + locale = new LocaleStringLookup; +} + void STDCALL TerminateXT() { if(bBaseLoaded) diff --git a/OBSApi/Utility/XT.h b/OBSApi/Utility/XT.h index adfa10f1..424815e1 100644 --- a/OBSApi/Utility/XT.h +++ b/OBSApi/Utility/XT.h @@ -80,9 +80,6 @@ struct XRect #define WAIT_INFINITE 0xFFFFFFFF -BASE_EXPORT void STDCALL OSInit(); -BASE_EXPORT void STDCALL OSExit(); - BASE_EXPORT void STDCALL OSLogSystemStats(); BASE_EXPORT DWORD STDCALL OSGetSysPageSize(); BASE_EXPORT LPVOID STDCALL OSVirtualAlloc(size_t dwSize); @@ -158,6 +155,8 @@ BASE_EXPORT void STDCALL TraceCrashEnd(); //Base functions //----------------------------------------- BASE_EXPORT BOOL STDCALL InitXT(CTSTR logFile=NULL, CTSTR allocatorName=NULL); +BASE_EXPORT void STDCALL InitXTLog(CTSTR logFile); +BASE_EXPORT void STDCALL ResetXTAllocator(CTSTR lpAllocator); BASE_EXPORT void STDCALL TerminateXT(); BASE_EXPORT extern BOOL bDebugBreak; diff --git a/Source/D3D10System.cpp b/Source/D3D10System.cpp index 40aab516..d901ecaf 100644 --- a/Source/D3D10System.cpp +++ b/Source/D3D10System.cpp @@ -33,13 +33,13 @@ D3D10System::D3D10System() swapDesc.BufferDesc.Width = App->renderFrameWidth; swapDesc.BufferDesc.Height = App->renderFrameHeight; swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapDesc.Flags = 0; + swapDesc.Flags = DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE; swapDesc.OutputWindow = hwndRenderFrame; swapDesc.SampleDesc.Count = 1; swapDesc.Windowed = TRUE; UINT createFlags = D3D10_CREATE_DEVICE_BGRA_SUPPORT; - if(AppConfig->GetInt(TEXT("General"), TEXT("UseDebugD3D"))) + if(GlobalConfig->GetInt(TEXT("General"), TEXT("UseDebugD3D"))) createFlags |= D3D10_CREATE_DEVICE_DEBUG; //D3D10_CREATE_DEVICE_DEBUG @@ -677,12 +677,14 @@ void D3D10System::ResetViewMatrix() void D3D10System::ResizeView() { + traceIn(D3D10System::ResizeView); + LPVOID nullVal = NULL; d3d->OMSetRenderTargets(1, (ID3D10RenderTargetView**)&nullVal, NULL); SafeRelease(swapRenderView); - swap->ResizeBuffers(2, 0, 0, DXGI_FORMAT_B8G8R8A8_UNORM, 0); + swap->ResizeBuffers(2, 0, 0, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE); ID3D10Texture2D *backBuffer = NULL; HRESULT err = swap->GetBuffer(0, IID_ID3D10Texture2D, (void**)&backBuffer); @@ -694,5 +696,7 @@ void D3D10System::ResizeView() CrashError(TEXT("Unable to get render view from back buffer")); backBuffer->Release(); + + traceOut; } diff --git a/Source/MMDeviceAudioSource.cpp b/Source/MMDeviceAudioSource.cpp index e5234daa..61174d02 100644 --- a/Source/MMDeviceAudioSource.cpp +++ b/Source/MMDeviceAudioSource.cpp @@ -351,8 +351,8 @@ UINT MMDeviceAudioSource::GetNextBuffer() __m128 outVal1 = _mm_unpacklo_ps(inVal, inVal); __m128 outVal2 = _mm_unpackhi_ps(inVal, inVal); - _mm_store_ps(outputTemp+(i*2), outVal1); - _mm_store_ps(outputTemp+(i*2)+4, outVal1); + _mm_store_ps(outputTemp+(i*2), outVal1); + _mm_store_ps(outputTemp+(i*2)+4, outVal2); } numFloats -= alignedFloats; @@ -580,7 +580,7 @@ UINT MMDeviceAudioSource::GetNextBuffer() receiveBuffer.AppendArray(resampleBuffer.Array(), data.output_frames_gen*2); } else - receiveBuffer.AppendArray(outputBuffer, numAudioFrames*2); + receiveBuffer.AppendArray(audioBuffer.Array(), numAudioFrames*2); return receiveBuffer.Num() >= (441*2) ? AudioAvailable : ContinueAudioRequest; } diff --git a/Source/Main.cpp b/Source/Main.cpp index 8126abab..c12441b8 100644 --- a/Source/Main.cpp +++ b/Source/Main.cpp @@ -19,6 +19,7 @@ #include "Main.h" +#include #include @@ -27,8 +28,10 @@ HWND hwndMain = NULL; HWND hwndRenderFrame = NULL; HINSTANCE hinstMain = NULL; +ConfigFile *GlobalConfig = NULL; ConfigFile *AppConfig = NULL; OBS *App = NULL; +TCHAR lpAppDataPath[MAX_PATH]; //---------------------------- @@ -82,12 +85,135 @@ void LogSystemStats() } +void SetupIni() +{ + //first, find out which profile we're using + + String strProfile = GlobalConfig->GetString(TEXT("General"), TEXT("Profile")); + String strIni; + + //-------------------------------------------- + // try to find and open the file, otherwise use the first one available + + bool bFoundProfile = false; + + if(strProfile.IsValid()) + { + strIni << lpAppDataPath << TEXT("\\profiles\\") << strProfile << TEXT(".ini"); + bFoundProfile = OSFileExists(strIni) != 0; + } + + if(!bFoundProfile) + { + OSFindData ofd; + + strIni.Clear() << lpAppDataPath << TEXT("\\profiles\\*.ini"); + HANDLE hFind = OSFindFirstFile(strIni, ofd); + if(hFind) + { + do + { + if(ofd.bDirectory) continue; + + strProfile = GetPathWithoutExtension(ofd.fileName); + GlobalConfig->SetString(TEXT("General"), TEXT("Profile"), strProfile); + bFoundProfile = true; + + break; + + } while(OSFindNextFile(hFind, ofd)); + + OSFindClose(hFind); + } + } + + //-------------------------------------------- + // open, or if no profile found, create one + + if(bFoundProfile) + { + strIni.Clear() << lpAppDataPath << TEXT("\\profiles\\") << strProfile << TEXT(".ini"); + + if(AppConfig->Open(strIni)) + return; + } + + strProfile = TEXT("Untitled"); + GlobalConfig->SetString(TEXT("General"), TEXT("Profile"), strProfile); + + strIni.Clear() << lpAppDataPath << TEXT("\\profiles\\") << strProfile << TEXT(".ini"); + + if(!AppConfig->Create(strIni)) + CrashError(TEXT("Could not create '%s'"), strIni); + + AppConfig->SetString(TEXT("Audio"), TEXT("Device"), TEXT("Default")); + AppConfig->SetFloat (TEXT("Audio"), TEXT("MicVolume"), 1.0f); + AppConfig->SetFloat (TEXT("Audio"), TEXT("DesktopVolume"), 1.0f); + + AppConfig->SetInt (TEXT("Video"), TEXT("Monitor"), 0); + AppConfig->SetInt (TEXT("Video"), TEXT("FPS"), 25); + AppConfig->SetFloat (TEXT("Video"), TEXT("Downscale"), 1.0f); + AppConfig->SetInt (TEXT("Video"), TEXT("DisableAero"), 0); + + AppConfig->SetInt (TEXT("Video Encoding"), TEXT("BufferSize"), 1000); + AppConfig->SetInt (TEXT("Video Encoding"), TEXT("MaxBitrate"), 1000); + AppConfig->SetString(TEXT("Video Encoding"), TEXT("Preset"), TEXT("veryfast")); + AppConfig->SetInt (TEXT("Video Encoding"), TEXT("Quality"), 8); + + AppConfig->SetInt (TEXT("Audio Encoding"), TEXT("Format"), 1); + AppConfig->SetString(TEXT("Audio Encoding"), TEXT("Bitrate"), TEXT("128")); + AppConfig->SetString(TEXT("Audio Encoding"), TEXT("Codec"), TEXT("AAC")); + + AppConfig->SetInt (TEXT("Publish"), TEXT("Service"), 0); + AppConfig->SetInt (TEXT("Publish"), TEXT("Mode"), 0); +}; + +void LoadGlobalIni() +{ + GlobalConfig = new ConfigFile; + + String strGlobalIni; + strGlobalIni << lpAppDataPath << TEXT("\\global.ini"); + + if(!GlobalConfig->Open(strGlobalIni)) + { + if(!GlobalConfig->Create(strGlobalIni)) + CrashError(TEXT("Could not create '%s'"), strGlobalIni.Array()); + + //---------------------- + // first, try to set the app to the system language, defaulting to english if the language doesn't exist + + DWORD bufSize = GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SISO639LANGNAME, NULL, 0); + + String str639Lang; + str639Lang.SetLength(bufSize); + + GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SISO639LANGNAME, str639Lang.Array(), bufSize+1); + + String strLangFile; + strLangFile << TEXT("locale/") << str639Lang << TEXT(".txt"); + + if(!OSFileExists(strLangFile)) + str639Lang = TEXT("en"); + + //---------------------- + + GlobalConfig->SetString(TEXT("General"), TEXT("Language"), str639Lang); + GlobalConfig->SetInt(TEXT("General"), TEXT("MaxLogs"), 20); + } +} + + int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { //make sure only one instance of the application can be open at a time hOBSMutex = CreateMutex(NULL, TRUE, TEXT("OBSMutex")); if(GetLastError() == ERROR_ALREADY_EXISTS) { + hwndMain = FindWindow(OBS_WINDOW_CLASS, NULL); + if(hwndMain) + SetForegroundWindow(hwndMain); + CloseHandle(hOBSMutex); return 0; } @@ -96,68 +222,108 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine hinstMain = hInstance; - //------------------------------------------------------------ - // get the allocator from the config. problem is we need an allocator to load the config - - CTSTR lpAllocatorString = TEXT("FastAlloc"); - - MainAllocator = new DefaultAlloc; - - AppConfig = new ConfigFile; - if(AppConfig->Open(TEXT("OBS.ini"))) - lpAllocatorString = AppConfig->GetStringPtr(TEXT("General"), TEXT("Allocator"), TEXT("FastAlloc")); - - UINT stringSize = ssize(lpAllocatorString); - TSTR lpAllocator = (TSTR)malloc(stringSize); - mcpy(lpAllocator, lpAllocatorString, stringSize); - - delete AppConfig; - delete MainAllocator; - - //------------------------------------------------------------ - - if(InitXT(TEXT("OBS.log"), lpAllocator)) + if(InitXT(NULL, TEXT("FastAlloc"))) { traceIn(Main); + InitSockets(); + //CoInitializeEx(NULL, COINIT_MULTITHREADED); + CoInitialize(0); EnableProfiling(TRUE); + TSTR lpAllocator = NULL; + + { + SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, lpAppDataPath); + scat_n(lpAppDataPath, TEXT("\\OBS"), 4); + + if(!OSFileExists(lpAppDataPath) && !OSCreateDirectory(lpAppDataPath)) + CrashError(TEXT("Couldn't create directory '%s'"), lpAppDataPath); + + String strAppDataPath = lpAppDataPath; + String strProfilesPath = strAppDataPath + TEXT("\\profiles"); + if(!OSFileExists(strProfilesPath) && !OSCreateDirectory(strProfilesPath)) + CrashError(TEXT("Couldn't create directory '%s'"), strProfilesPath.Array()); + + String strLogsPath = strAppDataPath + TEXT("\\logs"); + if(!OSFileExists(strLogsPath) && !OSCreateDirectory(strLogsPath)) + CrashError(TEXT("Couldn't create directory '%s'"), strLogsPath.Array()); + + String strPluginDataPath = strAppDataPath + TEXT("\\pluginData"); + if(!OSFileExists(strPluginDataPath) && !OSCreateDirectory(strPluginDataPath)) + CrashError(TEXT("Couldn't create directory '%s'"), strPluginDataPath.Array()); + + LoadGlobalIni(); + + String strAllocator = GlobalConfig->GetString(TEXT("General"), TEXT("Allocator")); + if(strAllocator.IsValid()) + { + UINT size = strAllocator.DataLength(); + lpAllocator = (TSTR)malloc(size); + mcpy(lpAllocator, strAllocator.Array(), size); + } + } + + if(lpAllocator) + { + delete GlobalConfig; + + ResetXTAllocator(lpAllocator); + free(lpAllocator); + + LoadGlobalIni(); + } + + //-------------------------------------------- + + String strDirectory; + UINT dirSize = GetCurrentDirectory(0, 0); + strDirectory.SetLength(dirSize); + GetCurrentDirectory(dirSize, strDirectory.Array()); + + GlobalConfig->SetString(TEXT("General"), TEXT("LastAppDirectory"), strDirectory.Array()); + + //-------------------------------------------- + + AppConfig = new ConfigFile; + SetupIni(); + + //-------------------------------------------- + + String strLogFileWildcard; + strLogFileWildcard << lpAppDataPath << TEXT("\\logs\\*.log"); + + OSFindData ofd; + HANDLE hFindLogs = OSFindFirstFile(strLogFileWildcard, ofd); + if(hFindLogs) + { + int numLogs = 0; + String strFirstLog; + + do + { + if(ofd.bDirectory) continue; + if(!numLogs++) + strFirstLog << lpAppDataPath << TEXT("\\logs\\") << ofd.fileName; + } while(OSFindNextFile(hFindLogs, ofd)); + + OSFindClose(hFindLogs); + + if(numLogs >= GlobalConfig->GetInt(TEXT("General"), TEXT("MaxLogs"), 20)) + OSDeleteFile(strFirstLog); + } + + SYSTEMTIME st; + GetLocalTime(&st); + + String strLog; + strLog << lpAppDataPath << FormattedString(TEXT("\\logs\\%u-%02u-%02u-%02u%02u-%02u"), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond) << TEXT(".log"); + + InitXTLog(strLog); + LogSystemStats(); - //CoInitializeEx(NULL, COINIT_MULTITHREADED); - CoInitialize(0); - - InitSockets(); - - AppConfig = new ConfigFile; - if(!AppConfig->Open(TEXT("OBS.ini"))) - { - if(!AppConfig->Create(TEXT("OBS.ini"))) - CrashError(TEXT("Could not open OBS.ini")); - - AppConfig->SetString(TEXT("General"), TEXT("Language"), TEXT("en")); - - AppConfig->SetString(TEXT("Audio"), TEXT("Device"), TEXT("Default")); - AppConfig->SetFloat (TEXT("Audio"), TEXT("MicVolume"), 1.0f); - AppConfig->SetFloat (TEXT("Audio"), TEXT("DesktopVolume"), 1.0f); - - AppConfig->SetInt (TEXT("Video"), TEXT("Monitor"), 0); - AppConfig->SetInt (TEXT("Video"), TEXT("FPS"), 25); - AppConfig->SetFloat (TEXT("Video"), TEXT("Downscale"), 1.0f); - AppConfig->SetInt (TEXT("Video"), TEXT("DisableAero"), 0); - - AppConfig->SetInt (TEXT("Video Encoding"), TEXT("BufferSize"), 1000); - AppConfig->SetInt (TEXT("Video Encoding"), TEXT("MaxBitrate"), 1000); - AppConfig->SetString(TEXT("Video Encoding"), TEXT("Preset"), TEXT("veryfast")); - AppConfig->SetInt (TEXT("Video Encoding"), TEXT("Quality"), 8); - - AppConfig->SetInt (TEXT("Audio Encoding"), TEXT("Format"), 1); - AppConfig->SetString(TEXT("Audio Encoding"), TEXT("Bitrate"), TEXT("128")); - AppConfig->SetString(TEXT("Audio Encoding"), TEXT("Codec"), TEXT("AAC")); - - AppConfig->SetInt (TEXT("Publish"), TEXT("Service"), 0); - AppConfig->SetInt (TEXT("Publish"), TEXT("Mode"), 0); - } + //-------------------------------------------- bDisableComposition = AppConfig->GetInt(TEXT("Video"), TEXT("DisableAero"), 0); @@ -168,6 +334,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine DwmEnableComposition(DWM_EC_DISABLECOMPOSITION); } + //-------------------------------------------- + App = new OBS; HACCEL hAccel = LoadAccelerators(hinstMain, MAKEINTRESOURCE(IDR_ACCELERATOR1)); @@ -183,7 +351,11 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine } delete App; + + //-------------------------------------------- + delete AppConfig; + delete GlobalConfig; if(bDisableComposition && bCompositionEnabled) DwmEnableComposition(DWM_EC_ENABLECOMPOSITION); @@ -191,11 +363,9 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine TerminateSockets(); traceOutStop; - - TerminateXT(); } - free(lpAllocator); + TerminateXT(); //------------------------------------------------------------ diff --git a/Source/Main.h b/Source/Main.h index 4b89937e..5dc87342 100644 --- a/Source/Main.h +++ b/Source/Main.h @@ -56,12 +56,17 @@ class OBS; extern HWND hwndMain; extern HWND hwndRenderFrame; extern HINSTANCE hinstMain; +extern ConfigFile *GlobalConfig; extern ConfigFile *AppConfig; extern OBS *App; +extern TCHAR lpAppDataPath[MAX_PATH]; -#define OBS_VERSION_STRING_ANSI "Open Broadcaster Software v0.37a" +#define OBS_VERSION_STRING_ANSI "Open Broadcaster Software v0.38a" #define OBS_VERSION_STRING TEXT(OBS_VERSION_STRING_ANSI) +#define OBS_WINDOW_CLASS TEXT("OBSWindowClass") +#define OBS_RENDERFRAME_CLASS TEXT("RenderFrame") + inline UINT ConvertMSTo100NanoSec(UINT ms) { return ms*1000*10; //1000 microseconds, then 10 "100nanosecond" segments diff --git a/Source/OBS.cpp b/Source/OBS.cpp index 524bd849..07d84673 100644 --- a/Source/OBS.cpp +++ b/Source/OBS.cpp @@ -57,7 +57,7 @@ NetworkStream* CreateNullNetwork(); void Convert444to420(LPBYTE input, int width, int height, LPBYTE *output, bool bSSE2Available); -void STDCALL SceneHotkey(DWORD hotkey, UPARAM param); +void STDCALL SceneHotkey(DWORD hotkey, UPARAM param, bool bDown); inline BOOL CloseDouble(double f1, double f2, double precision=0.001) @@ -72,11 +72,6 @@ WNDPROC listboxProc = NULL; //---------------------------- -#define OBS_WINDOW_CLASS TEXT("OBS") -#define OBS_RENDERFRAME_CLASS TEXT("RenderFrame") - -//---------------------------- - const float defaultBlendFactor[4] = {1.0f, 1.0f, 1.0f, 1.0f}; //---------------------------- @@ -293,6 +288,7 @@ Scene* STDCALL CreateNormalScene(XElement *data) struct HotkeyInfo { + DWORD hotkeyID; DWORD hotkey; OBSHOTKEYPROC hotkeyProc; UPARAM param; @@ -304,6 +300,7 @@ class OBSAPIInterface : public APIInterface friend class OBS; List hotkeys; + DWORD curHotkeyIDVal; void HandleHotkeys(); @@ -347,6 +344,9 @@ public: virtual CTSTR GetLanguage() const {return App->strLanguage;} + virtual CTSTR GetAppDataPath() const {return lpAppDataPath;} + virtual String GetPluginDataPath() const {return String() << lpAppDataPath << TEXT("\\pluginData");} + virtual HWND GetMainWindow() const {return hwndMain;} }; @@ -389,7 +389,7 @@ OBS::OBS() if(!locale->LoadStringFile(TEXT("locale/en.txt"))) AppWarning(TEXT("Could not open locale string file '%s'"), TEXT("locale/en.txt")); - strLanguage = AppConfig->GetString(TEXT("General"), TEXT("Language"), TEXT("en")); + strLanguage = GlobalConfig->GetString(TEXT("General"), TEXT("Language"), TEXT("en")); if(!strLanguage.CompareI(TEXT("en"))) { String langFile; @@ -632,8 +632,11 @@ OBS::OBS() hwndTemp = GetDlgItem(hwndMain, ID_SCENES); - if(!scenesConfig.Open(TEXT("scenes.xconfig"))) - CrashError(TEXT("Could not open scenes.xconfig")); + String strScenesConfig; + strScenesConfig << lpAppDataPath << TEXT("\\scenes.xconfig"); + + if(!scenesConfig.Open(strScenesConfig)) + CrashError(TEXT("Could not open '%s'"), strScenesConfig); XElement *scenes = scenesConfig.GetElement(TEXT("scenes")); if(!scenes) @@ -712,6 +715,11 @@ OBS::OBS() OSFindClose(hFind); } + //----------------------------------------------------- + + bRenderViewEnabled = true; + bShowFPS = AppConfig->GetInt(TEXT("General"), TEXT("ShowFPS")) != 0; + traceOut; } @@ -745,6 +753,13 @@ OBS::~OBS() DeleteObject(Icons[i].hIcon); Icons.Clear(); + for(UINT i=0; iGetSceneListElement(); if(scenes) { @@ -784,7 +801,7 @@ void STDCALL SceneHotkey(DWORD hotkey, UPARAM param) DWORD sceneHotkey = (DWORD)scene->GetInt(TEXT("hotkey")); if(sceneHotkey == hotkey) { - Log(TEXT("hotkey pressed for scene '%s'"), scene->GetName()); + //Log(TEXT("hotkey pressed for scene '%s'"), scene->GetName()); App->SetScene(scene->GetName()); return; } @@ -800,6 +817,8 @@ HICON OBS::GetIcon(HINSTANCE hInst, int resource) return Icons[i].hIcon; } + //--------------------- + IconInfo ii; ii.hInst = hInst; ii.resource = resource; @@ -810,6 +829,38 @@ HICON OBS::GetIcon(HINSTANCE hInst, int resource) return ii.hIcon; } +HFONT OBS::GetFont(CTSTR lpFontFace, int fontSize, int fontWeight) +{ + for(UINT i=0; iGetParameterByName(TEXT("baseDimensionI")); @@ -1338,15 +1386,14 @@ void OBS::MainCaptureLoop() float bpsTime = 0.0f; double lastStrain = 0.0f; - DWORD audioResyncOffset = 0; - - UINT timeAdjust = 0; + DWORD captureFPS = 0; + DWORD fpsCounter = 0; while(bRunning) { DWORD renderStartTime = OSGetTime(); - bool bRenderView = !IsIconic(hwndMain); + bool bRenderView = !IsIconic(hwndMain) && bRenderViewEnabled; profileIn("frame"); @@ -1405,9 +1452,14 @@ void OBS::MainCaptureLoop() lastBytesSent = curBytesSent; + captureFPS = fpsCounter; + fpsCounter = 0; + bUpdateBPS = true; } + fpsCounter++; + double curStrain = network->GetPacketStrain(); if(bUpdateBPS || !CloseDouble(curStrain, lastStrain)) { @@ -1505,6 +1557,38 @@ void OBS::MainCaptureLoop() if(scene) scene->RenderSelections(); } + + if(bShowFPS && !bSizeChanging) + { + D3D10System *d3dSys = static_cast(GS); + + IDXGISurface1 *surface; + if(SUCCEEDED(d3dSys->swap->GetBuffer(0, __uuidof(IDXGISurface1), (void**)&surface))) + { + HDC hDC; + if(SUCCEEDED(surface->GetDC(FALSE, &hDC))) + { + String strFPS; + strFPS << TEXT("FPS: ") << UIntString(captureFPS); + + HFONT hFont = (HFONT)GetFont(TEXT("Arial"), -20, 700); + HFONT hfontOld; + if(hFont) + hfontOld = (HFONT)SelectObject(hDC, hFont); + + SetTextColor(hDC, MAKEBGR(0xFF, 0, 0)); + SetBkMode(hDC, TRANSPARENT); + TextOut(hDC, 4, 4, strFPS, strFPS.Length()); + + if(hFont) + SelectObject(hDC, hfontOld); + + surface->ReleaseDC(NULL); + } + + surface->Release(); + } + } } //------------------------------------ @@ -1535,9 +1619,6 @@ void OBS::MainCaptureLoop() //------------------------------------ - if(!static_cast(GS)->swap) - Log(TEXT("swap is bad?")); - if(bRenderView && !copyWait) static_cast(GS)->swap->Present(0, 0); @@ -1907,32 +1988,33 @@ void OBS::SelectSources() } } -void OBS::CallHotkey(DWORD hotkey) +void OBS::CallHotkey(DWORD hotkeyID, bool bDown) { OBSAPIInterface *apiInterface = (OBSAPIInterface*)API; OBSHOTKEYPROC hotkeyProc = NULL; - UPARAM param; + DWORD hotkey = 0; + UPARAM param = NULL; OSEnterMutex(hHotkeyMutex); for(UINT i=0; ihotkeys.Num(); i++) { HotkeyInfo &hi = apiInterface->hotkeys[i]; - if(hi.hotkey == hotkey) + if(hi.hotkeyID == hotkeyID) { - if(hi.hotkeyProc) - { - hotkeyProc = hi.hotkeyProc; - param = hi.param; - } + if(!hi.hotkeyProc) + return; + + hotkeyProc = hi.hotkeyProc; + param = hi.param; + hotkey = hi.hotkey; break; } } OSLeaveMutex(hHotkeyMutex); - if(hotkeyProc) - hotkeyProc(hotkey, param); + hotkeyProc(hotkey, param, bDown); } bool OBSAPIInterface::HotkeyExists(DWORD hotkey) const @@ -1969,16 +2051,8 @@ bool OBSAPIInterface::CreateHotkey(DWORD hotkey, OBSHOTKEYPROC hotkeyProc, UPARA fsModifiers |= MOD_SHIFT; OSEnterMutex(App->hHotkeyMutex); - for(UINT i=0; ihHotkeyMutex); - return false; - } - } - HotkeyInfo &hi = *hotkeys.CreateNew(); + hi.hotkeyID = ++curHotkeyIDVal; hi.hotkey = hotkey; hi.hotkeyProc = hotkeyProc; hi.param = param; @@ -2028,19 +2102,22 @@ void OBSAPIInterface::HandleHotkeys() { short keyState = GetAsyncKeyState(hotkeyVK); bool bDown = (keyState & 0x8000) != 0; - bool bWasPressed = (keyState & 1) != 0; - if(bDown || bWasPressed) + if(bDown) { if(!info.bDown) - PostMessage(hwndMain, OBS_CALLHOTKEY, 0, info.hotkey); + PostMessage(hwndMain, OBS_CALLHOTKEY, TRUE, info.hotkeyID); info.bDown = bDown; continue; } } - info.bDown = false; + if(info.bDown) //key up + { + PostMessage(hwndMain, OBS_CALLHOTKEY, FALSE, info.hotkeyID); + info.bDown = false; + } } OSLeaveMutex(App->hHotkeyMutex); diff --git a/Source/OBS.h b/Source/OBS.h index 6aab9149..41c5ecc5 100644 --- a/Source/OBS.h +++ b/Source/OBS.h @@ -179,6 +179,14 @@ struct IconInfo int resource; }; +struct FontInfo +{ + HFONT hFont; + String strFontFace; + int fontSize; + int fontWeight; +}; + //------------------------------------------------------------------- struct FrameAudio @@ -344,6 +352,8 @@ class OBS bool bResizeRenderView; bool bEditMode; + bool bRenderViewEnabled; + bool bShowFPS; bool bMouseMoved; bool bMouseDown; bool bItemWasSelected; @@ -377,6 +387,7 @@ class OBS String streamReport; List Icons; + List Fonts; List sceneClasses; List imageSourceClasses; @@ -469,7 +480,7 @@ class OBS static INT_PTR CALLBACK GlobalSourcesProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); static bool STDCALL ConfigGlobalSource(XElement *element, bool bCreating); - void CallHotkey(DWORD hotkey); + void CallHotkey(DWORD hotkeyID, bool bDown); public: OBS(); @@ -499,6 +510,7 @@ public: inline XElement* GetSceneElement() const {return sceneElement;} virtual HICON GetIcon(HINSTANCE hInst, int resource); + virtual HFONT GetFont(CTSTR lpFontFace, int fontSize, int fontWeight); inline void GetVideoHeaders(DataPacket &packet) {videoEncoder->GetHeaders(packet);} inline void GetAudioHeaders(DataPacket &packet) {audioEncoder->GetHeaders(packet);} diff --git a/Source/RTMPPublisher.cpp b/Source/RTMPPublisher.cpp index 5d98390c..287c561d 100644 --- a/Source/RTMPPublisher.cpp +++ b/Source/RTMPPublisher.cpp @@ -58,7 +58,8 @@ class RTMPPublisher : public NetworkStream QWORD bytesSent; - //UINT numBFramesDumped; + UINT numPFramesDumped; + UINT numBFramesDumped; //all data sending is done in yet another separate thread to prevent blocking in the main capture thread void SendLoop() @@ -219,7 +220,7 @@ class RTMPPublisher : public NetworkStream packet.data.Clear(); Packets.Remove(i--); numVideoPackets--; - //numBFramesDumped++; + numPFramesDumped++; } } else if(packet.type == packetWaitType) @@ -227,7 +228,7 @@ class RTMPPublisher : public NetworkStream packet.data.Clear(); Packets.Remove(i--); numVideoPackets--; - //numBFramesDumped++; + numPFramesDumped++; bRemovedPacket = true; } @@ -267,7 +268,7 @@ class RTMPPublisher : public NetworkStream packet.data.Clear(); Packets.Remove(i--); numVideoPackets--; - //numBFramesDumped++; + numBFramesDumped++; } } else if(packet.type == packetWaitType) @@ -275,7 +276,7 @@ class RTMPPublisher : public NetworkStream packet.data.Clear(); Packets.Remove(i--); numVideoPackets--; - //numBFramesDumped++; + numBFramesDumped++; bRemovedPacket = true; } @@ -401,7 +402,7 @@ public: Packets[i].data.Clear(); Packets.Clear(); - //Log(TEXT("num b frames dumped: %u"), numBFramesDumped); + Log(TEXT("Number of b-frames dropped: %u, Number of p-frames dropped: %u"), numBFramesDumped, numPFramesDumped); //-------------------------- diff --git a/Source/Settings.cpp b/Source/Settings.cpp index a851b049..dd3308e5 100644 --- a/Source/Settings.cpp +++ b/Source/Settings.cpp @@ -31,12 +31,41 @@ enum SettingsSelection BOOL CALLBACK MonitorInfoEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, List &monitors); +BOOL IsValidFileName(CTSTR lpFileName) +{ + if(!lpFileName || !*lpFileName) + return FALSE; + + CTSTR lpTemp = lpFileName; + + do + { + if( *lpTemp == '\\' || + *lpTemp == '/' || + *lpTemp == ':' || + *lpTemp == '*' || + *lpTemp == '?' || + *lpTemp == '"' || + *lpTemp == '<' || + *lpTemp == '>') + { + return FALSE; + } + } while (*++lpTemp); + + return TRUE; +} + INT_PTR CALLBACK OBS::GeneralSettingsProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch(message) { case WM_INITDIALOG: { + LocalizeWindow(hwnd); + + //---------------------------------------------- + HWND hwndTemp = GetDlgItem(hwnd, IDC_LANGUAGE); OSFindData ofd; @@ -45,6 +74,8 @@ INT_PTR CALLBACK OBS::GeneralSettingsProc(HWND hwnd, UINT message, WPARAM wParam { do { + if(ofd.bDirectory) continue; + String langCode = GetPathFileName(ofd.fileName); LocaleNativeName *langInfo = GetLocaleNativeName(langCode); @@ -61,7 +92,37 @@ INT_PTR CALLBACK OBS::GeneralSettingsProc(HWND hwnd, UINT message, WPARAM wParam OSFindClose(hFind); } - LocalizeWindow(hwnd); + //---------------------------------------------- + + String strCurProfile = GlobalConfig->GetString(TEXT("General"), TEXT("Profile")); + + hwndTemp = GetDlgItem(hwnd, IDC_PROFILE); + + String strProfilesWildcard = lpAppDataPath; + strProfilesWildcard << TEXT("\\profiles\\*.ini"); + + if(hFind = OSFindFirstFile(strProfilesWildcard, ofd)) + { + do + { + if(ofd.bDirectory) continue; + + String strProfile = GetPathWithoutExtension(ofd.fileName); + UINT id = (UINT)SendMessage(hwndTemp, CB_ADDSTRING, 0, (LPARAM)strProfile.Array()); + if(strProfile.CompareI(strCurProfile)) + SendMessage(hwndTemp, CB_SETCURSEL, id, 0); + } while(OSFindNextFile(hFind, ofd)); + + OSFindClose(hFind); + } + + EnableWindow(GetDlgItem(hwnd, IDC_ADD), FALSE); + EnableWindow(GetDlgItem(hwnd, IDC_RENAME), FALSE); + + UINT numItems = (UINT)SendMessage(GetDlgItem(hwnd, IDC_PROFILE), CB_GETCOUNT, 0, 0); + EnableWindow(GetDlgItem(hwnd, IDC_REMOVE), (numItems > 1)); + //---------------------------------------------- + App->SetChangedSettings(false); return TRUE; } @@ -73,9 +134,147 @@ INT_PTR CALLBACK OBS::GeneralSettingsProc(HWND hwnd, UINT message, WPARAM wParam if(HIWORD(wParam) != CBN_SELCHANGE) break; - App->SetChangedSettings(true); + SetWindowText(GetDlgItem(hwnd, IDC_INFO), Str("Settings.General.Restart")); ShowWindow(GetDlgItem(hwnd, IDC_INFO), SW_SHOW); break; + + case IDC_PROFILE: + if(HIWORD(wParam) == CBN_EDITCHANGE) + { + String strText = GetEditText((HWND)lParam); + + EnableWindow(GetDlgItem(hwnd, IDC_REMOVE), FALSE); + + if(strText.IsValid()) + { + if(IsValidFileName(strText)) + { + String strCurProfile = GlobalConfig->GetString(TEXT("General"), TEXT("Profile")); + + UINT id = (UINT)SendMessage((HWND)lParam, CB_FINDSTRINGEXACT, -1, (LPARAM)strText.Array()); + EnableWindow(GetDlgItem(hwnd, IDC_ADD), (id == CB_ERR)); + EnableWindow(GetDlgItem(hwnd, IDC_RENAME), (id == CB_ERR) || strCurProfile.CompareI(strText)); + + ShowWindow(GetDlgItem(hwnd, IDC_INFO), SW_HIDE); + break; + } + + SetWindowText(GetDlgItem(hwnd, IDC_INFO), Str("Settings.General.InvalidName")); + ShowWindow(GetDlgItem(hwnd, IDC_INFO), SW_SHOW); + } + else + ShowWindow(GetDlgItem(hwnd, IDC_INFO), SW_HIDE); + + EnableWindow(GetDlgItem(hwnd, IDC_ADD), FALSE); + EnableWindow(GetDlgItem(hwnd, IDC_RENAME), FALSE); + } + else if(HIWORD(wParam) == CBN_SELCHANGE) + { + EnableWindow(GetDlgItem(hwnd, IDC_ADD), FALSE); + EnableWindow(GetDlgItem(hwnd, IDC_RENAME), FALSE); + + String strProfile = GetCBText((HWND)lParam); + String strProfilePath; + strProfilePath << lpAppDataPath << TEXT("\\profiles\\") << strProfile << TEXT(".ini"); + + if(!AppConfig->Open(strProfilePath)) + { + MessageBox(hwnd, TEXT("Error - unable to open ini file"), NULL, 0); + break; + } + + SetWindowText(GetDlgItem(hwnd, IDC_INFO), Str("Settings.Info")); + ShowWindow(GetDlgItem(hwnd, IDC_INFO), SW_SHOW); + + GlobalConfig->SetString(TEXT("General"), TEXT("Profile"), strProfile); + + UINT numItems = (UINT)SendMessage(GetDlgItem(hwnd, IDC_PROFILE), CB_GETCOUNT, 0, 0); + EnableWindow(GetDlgItem(hwnd, IDC_REMOVE), (numItems > 1)); + } + break; + + case IDC_RENAME: + case IDC_ADD: + if(HIWORD(wParam) == BN_CLICKED) + { + HWND hwndProfileList = GetDlgItem(hwnd, IDC_PROFILE); + String strProfile = GetEditText(hwndProfileList); + + bool bRenaming = (LOWORD(wParam) == IDC_RENAME); + + String strCurProfile = GlobalConfig->GetString(TEXT("General"), TEXT("Profile")); + String strCurProfilePath; + strCurProfilePath << lpAppDataPath << TEXT("\\profiles\\") << strCurProfile << TEXT(".ini"); + + String strProfilePath; + strProfilePath << lpAppDataPath << TEXT("\\profiles\\") << strProfile << TEXT(".ini"); + + if((!bRenaming || !strProfilePath.CompareI(strCurProfilePath)) && OSFileExists(strProfilePath)) + MessageBox(hwnd, Str("Settings.General.ProfileExists"), NULL, 0); + else + { + if(bRenaming) + { + if(!MoveFile(strCurProfilePath, strProfilePath)) + break; + + AppConfig->SetFilePath(strProfilePath); + + UINT curID = (UINT)SendMessage(hwndProfileList, CB_FINDSTRINGEXACT, -1, (LPARAM)strCurProfile.Array()); + if(curID != CB_ERR) + SendMessage(hwndProfileList, CB_DELETESTRING, curID, 0); + } + else + { + if(!AppConfig->SaveAs(strProfilePath)) + { + MessageBox(hwnd, TEXT("Error - unable to create new profile, could not create file"), NULL, 0); + break; + } + } + + UINT id = (UINT)SendMessage(hwndProfileList, CB_ADDSTRING, 0, (LPARAM)strProfile.Array()); + SendMessage(hwndProfileList, CB_SETCURSEL, id, 0); + GlobalConfig->SetString(TEXT("General"), TEXT("Profile"), strProfile); + + UINT numItems = (UINT)SendMessage(hwndProfileList, CB_GETCOUNT, 0, 0); + EnableWindow(GetDlgItem(hwnd, IDC_REMOVE), (numItems > 1)); + EnableWindow(GetDlgItem(hwnd, IDC_RENAME), FALSE); + EnableWindow(GetDlgItem(hwnd, IDC_ADD), FALSE); + } + } + break; + + case IDC_REMOVE: + { + HWND hwndProfileList = GetDlgItem(hwnd, IDC_PROFILE); + + String strCurProfile = GlobalConfig->GetString(TEXT("General"), TEXT("Profile")); + + UINT numItems = (UINT)SendMessage(hwndProfileList, CB_GETCOUNT, 0, 0); + + String strConfirm = Str("Settings.General.ConfirmDelete"); + strConfirm.FindReplace(TEXT("$1"), strCurProfile); + if(MessageBox(hwnd, strConfirm, Str("DeleteConfirm.Title"), MB_YESNO) == IDYES) + { + UINT id = (UINT)SendMessage(hwndProfileList, CB_FINDSTRINGEXACT, -1, (LPARAM)strCurProfile.Array()); + if(id != CB_ERR) + { + SendMessage(hwndProfileList, CB_DELETESTRING, id, 0); + if(id == numItems-1) + id--; + + SendMessage(hwndProfileList, CB_SETCURSEL, id, 0); + GeneralSettingsProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_PROFILE, CBN_SELCHANGE), (LPARAM)hwndProfileList); + + String strCurProfilePath; + strCurProfilePath << lpAppDataPath << TEXT("\\profiles\\") << strCurProfile << TEXT(".ini"); + OSDeleteFile(strCurProfilePath); + } + } + + break; + } } } @@ -963,7 +1162,7 @@ void OBS::ApplySettings() if(curSel != CB_ERR) { String strLanguageCode = (CTSTR)SendMessage(hwndTemp, CB_GETITEMDATA, curSel, 0); - AppConfig->SetString(TEXT("General"), TEXT("Language"), strLanguageCode); + GlobalConfig->SetString(TEXT("General"), TEXT("Language"), strLanguageCode); } break; } diff --git a/Source/WindowStuff.cpp b/Source/WindowStuff.cpp index 254843f2..e2455310 100644 --- a/Source/WindowStuff.cpp +++ b/Source/WindowStuff.cpp @@ -26,7 +26,7 @@ extern WNDPROC listboxProc; -void STDCALL SceneHotkey(DWORD hotkey, UPARAM param); +void STDCALL SceneHotkey(DWORD hotkey, UPARAM param, bool bDown); enum { @@ -141,10 +141,19 @@ INT_PTR CALLBACK OBS::SceneHotkeyDialogProc(HWND hwnd, UINT message, WPARAM wPar break; } - if(hotkey && API->HotkeyExists(hotkey)) + if(hotkey) { - MessageBox(hwnd, Str("Scene.Hotkey.AlreadyInUse"), NULL, 0); - return 0; + XElement *scenes = API->GetSceneListElement(); + UINT numScenes = scenes->NumElements(); + for(UINT i=0; iGetElementByID(i); + if(sceneElement->GetInt(TEXT("hotkey")) == hotkey) + { + MessageBox(hwnd, Str("Scene.Hotkey.AlreadyInUse"), NULL, 0); + return 0; + } + } } hotkeyInfo->hotkey = hotkey; @@ -590,11 +599,7 @@ LRESULT CALLBACK OBS::ListboxHook(HWND hwnd, UINT message, WPARAM wParam, LPARAM } if(App->bRunning) - { - OSEnterMutex(App->hSceneMutex); App->scene->AddImageSource(newSourceElement); - OSLeaveMutex(App->hSceneMutex); - } UINT newID = (UINT)SendMessage(hwnd, LB_ADDSTRING, 0, (LPARAM)strName.Array()); PostMessage(hwnd, LB_SETCURSEL, newID, 0); @@ -1526,7 +1531,7 @@ LRESULT CALLBACK OBS::OBSProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPa break; case OBS_CALLHOTKEY: - App->CallHotkey((DWORD)lParam); + App->CallHotkey((DWORD)lParam, wParam != 0); break; case WM_CLOSE: @@ -1574,6 +1579,12 @@ ItemModifyType GetItemModifyType(const Vect2 &mousePos, const Vect2 &itemPos, co return ItemModifyType_Move; } +enum +{ + ID_TOGGLERENDERVIEW=1, + ID_SHOWFPS, +}; + LRESULT CALLBACK OBS::RenderFrameProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { traceIn(OBS::RenderFrameProc); @@ -2067,6 +2078,30 @@ LRESULT CALLBACK OBS::RenderFrameProc(HWND hwnd, UINT message, WPARAM wParam, LP } } } + else if(message == WM_RBUTTONDOWN) + { + HMENU hPopup = CreatePopupMenu(); + AppendMenu(hPopup, MF_STRING | (App->bRenderViewEnabled ? MF_CHECKED : 0), ID_TOGGLERENDERVIEW, Str("RenderView.EnableView")); + AppendMenu(hPopup, MF_STRING | (App->bShowFPS ? MF_CHECKED : 0), ID_SHOWFPS, Str("RenderView.ShowFPS")); + + POINT p; + GetCursorPos(&p); + + int ret = (int)TrackPopupMenuEx(hPopup, TPM_RETURNCMD | TPM_LEFTALIGN | TPM_RIGHTBUTTON, p.x, p.y, hwndMain, NULL); + switch(ret) + { + case ID_TOGGLERENDERVIEW: + App->bRenderViewEnabled = !App->bRenderViewEnabled; + break; + + case ID_SHOWFPS: + App->bShowFPS = !App->bShowFPS; + AppConfig->SetInt(TEXT("General"), TEXT("ShowFPS"), App->bShowFPS ? 1 : 0); + break; + } + + DestroyMenu(hPopup); + } return DefWindowProc(hwnd, message, wParam, lParam); diff --git a/TODO b/TODO index 3034fc97..4d7560fc 100644 --- a/TODO +++ b/TODO @@ -4,7 +4,6 @@ More up to date list on the website ------------------------------------------------------- List of features yet to be implemented: - * implement ability to save multiple channel/server settings * add other commonly used server data to the services.xconfig file (livestream, etc), need to find out their server details * Option to output to mp4/avi file. Not going to use ffmpeg by diff --git a/resource.h b/resource.h index 77811816..3205c7e6 100644 --- a/resource.h +++ b/resource.h @@ -2,6 +2,7 @@ // Microsoft Visual C++ generated include file. // Used by OBS.rc // +#define IDC_DEFAULTS 3 #define IDC_APPLY 4 #define IDD_SETTINGS 101 #define IDI_ICON1 109 @@ -44,6 +45,7 @@ #define IDC_LANGUAGE 1028 #define IDC_INFO 1029 #define IDC_FPS 1030 +#define IDC_PROFILE 1030 #define IDC_FPS_EDIT 1031 #define IDC_SIZEX 1033 #define IDC_SIZEY 1034 @@ -90,6 +92,7 @@ #define IDC_HOTKEY1 1073 #define IDC_HOTKEY 1073 #define IDC_CLEAR 1074 +#define IDC_ADDNEW 1077 #define IDA_SOURCE_MOVEUP 40018 #define IDA_SOURCE_MOVEDOWN 40019 #define IDA_SOURCE_MOVETOTOP 40020 @@ -105,7 +108,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 131 #define _APS_NEXT_COMMAND_VALUE 40028 -#define _APS_NEXT_CONTROL_VALUE 1075 +#define _APS_NEXT_CONTROL_VALUE 1078 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/rundir/locale/en.txt b/rundir/locale/en.txt index 5ffbe60f..3c455068 100644 --- a/rundir/locale/en.txt +++ b/rundir/locale/en.txt @@ -50,6 +50,9 @@ Plugins.Configure "Configure" Plugins.Description "Description:" Plugins.Filename "File name:" +RenderView.EnableView "Enable View" +RenderView.ShowFPS "Show FPS" + Scene.Hotkey "Scene Hotkey" Scene.MissingSources "Was unable to load all image sources due to either invalid settings or missing plugins" @@ -86,8 +89,14 @@ Settings.Encoding.Video.Tradeoff "CPU Preset:" Settings.Encoding.Video.TradeoffTooltip "Setting this value higher reduces CPU usage by sacrificing certain aspects of quality. On the other hand, setting this value lower increases quality at the cost of more CPU.\r\n\r\nRecommended: Very Fast" Settings.Encoding.Video.UseI444 "Use YUV 4:4:4" -Settings.General.Language "Language:" -Settings.General.Restart "Changing the language will require restarting OBS.\r\n\r\n..Though I suppose if you're changing the language you can't understand this" +Settings.General.Add "Add New" +Settings.General.ConfirmDelete "Are you sure you wish to remove the profile '$1'?" +Settings.General.InvalidName "Profile names cannot use the following characters:\r\n\\ / : * ? \" < > |" +Settings.General.Language "Language:" +Settings.General.Profile "Setting Profile:" +Settings.General.Remove "Remove" +Settings.General.Rename "Rename" +Settings.General.Restart "Changing the language will require restarting OBS.\r\n\r\n..Though I suppose if you're changing the language you can't understand this" Settings.Publish.ChannelName "Channel Name:" Settings.Publish.Mode "Mode:" diff --git a/rundir/locale/ja.txt b/rundir/locale/ja.txt index 14802bed..cd83b3bd 100644 --- a/rundir/locale/ja.txt +++ b/rundir/locale/ja.txt @@ -22,7 +22,7 @@ Connection.InvalidStream "ストリームのチャンネルやプレイ パ DeleteConfirm.Title "削除しますか?" -GlobalSources.DeleteConfirm "グローバル ソースを削除するとすべてのシーンから外します。続けてよろしいですか?" +GlobalSources.DeleteConfirm "グローバル ソースが削除するなら、それぞれシーンから外します。続けてよろしいですか?" Listbox.Add "$1を追加" Listbox.Center "中央に置く" @@ -50,10 +50,13 @@ Plugins.Configure "構成" Plugins.Description "説明:" Plugins.Filename "ファイル名:" -Scene.Hotkey "シーンのホットキー" -Scene.MissingSources "必要なプラグインが見つからなかったとか、ソースの設定が間違っているとかため、すべてのソースは作成できませんでした" +RenderView.EnableView "ビューを有効にする" +RenderView.ShowFPS "FPSを表示する" -Scene.Hotkey.AlreadyInUse "このホットキーは既に使っています。" +Scene.Hotkey "シーンのホットキー" +Scene.MissingSources "すべてソースは作成できませんでした" + +Scene.Hotkey.AlreadyInUse "このホットキーは既に別のシーンが使っています。" Scene.Hotkey.Clear "消去" Scene.Hotkey.Hotkey "ホットキー:" @@ -61,10 +64,10 @@ Settings.Audio "サウンド" Settings.Encoding "エンコード" Settings.General "一般" Settings.Hotkeys "キーボード ショートカット" -Settings.Info "この設定を再びストリーム開始するまでは使用しません" +Settings.Info "ストリーム再開始するまでこの設定は適用しません。" Settings.Publish "放送設定" -Settings.SaveChangesPrompt "設定を保存して適用しますか?" -Settings.SaveChangesTitle "適用しますか?" +Settings.SaveChangesPrompt "設定を保存しますか?" +Settings.SaveChangesTitle "保存しますか?" Settings.Video "動画" Settings.Audio.Device "マイク/他のサウンド デバイス:" @@ -80,11 +83,17 @@ Settings.Encoding.Video.BufferSize "バッファー 大きさ (kbit):" Settings.Encoding.Video.MaxBitRate "ビットレート 最大限 (kbit/秒):" Settings.Encoding.Video.Quality "動画質:" Settings.Encoding.Video.Tradeoff "CPU プリセット:" -Settings.Encoding.Video.TradeoffTooltip "CPU使用を調整するための設定です。「superfast」や「ultrafast」はCPU使用を減らすために品質を犠牲にする。一方は「faster」や「fast」以下は品質を優先としてCPU使用をふやします。\r\n\r\n推奨:「veryfast」" +Settings.Encoding.Video.TradeoffTooltip "CPUの利用を調整するための設定です。「superfast」や「ultrafast」を使ってはCPU使用を減らすために品質を犠牲にする。一方、「faster」や「fast」以下はCPU利用を増やしますことで品質を優先しようとします。\r\n\r\n推奨:「veryfast」" Settings.Encoding.Video.UseI444 "YUV 4:4:4を使用する:" -Settings.General.Language "言語:" -Settings.General.Restart "言語を変更にはOBSを再起動する必要があります。…これを読めるなら" +Settings.General.Add "追加" +Settings.General.ConfirmDelete "'$1'を削除してよろしいですか?" +Settings.General.InvalidName "プロファイル名には次の文字は使いません:\r\n\\ / : * ? \" < > |" +Settings.General.Language "言語:" +Settings.General.Profile "設定プロファイル:" +Settings.General.Remove "削除" +Settings.General.Rename "名前を変更" +Settings.General.Restart "言語を変更にはOBSを再起動する必要があります。…これを読めるなら" Settings.Publish.ChannelName "チャンネル名:" Settings.Publish.Mode "モード:" @@ -94,13 +103,13 @@ Settings.Publish.Server "サーバー:" Settings.Publish.Service "サービス:" Settings.Publish.Username "ユーザ名 (もしあれば):" -Settings.Publish.Mode.BandwidthInfo "転送量情報" +Settings.Publish.Mode.BandwidthInfo "転送の報告" Settings.Publish.Mode.LiveStream "ライブ ストリーム" Settings.Publish.Mode.Serve "RTMP ローカル サーバー" Settings.Video.Custom "特性:" Settings.Video.DisableAero "起動した時にAeroを無効にする:" -Settings.Video.DisableAeroTooltip "画面ソフトウェア キャプチャーするつもりなら大いに推奨" +Settings.Video.DisableAeroTooltip "画面ソフトウェア キャプチャーを使うつもりなら大いに推奨" Settings.Video.Downscale "解像度の縮小:" Settings.Video.FPS "FPS:" Settings.Video.Monitor "画面:" @@ -112,13 +121,13 @@ Sources.BitmapSource "ビットマップ画像" Sources.GlobalSource "グローバル ソース" Sources.SoftwareCaptureSource "ソフトウェア キャプチャー ソース" -Sources.BitmapSource.Empty "パスは空です。ビットマップ画像を参照して選択してください" +Sources.BitmapSource.Empty "パスは空です。ビットマップ画像ファイルを選択してください" Sources.SoftwareCaptureSource.EntireWindow "ウィンドウ全部" Sources.SoftwareCaptureSource.InnerWindow "ウィンドウ内部" Sources.SoftwareCaptureSource.Monitor "画面:" Sources.SoftwareCaptureSource.MonitorCapture "画面キャプチャー" -Sources.SoftwareCaptureSource.MonitorCaptureTooltip "Aeroを無効にするのは大いに推奨" +Sources.SoftwareCaptureSource.MonitorCaptureTooltip "Aeroを無効にすることは大いに推奨" Sources.SoftwareCaptureSource.MouseCapture "マウスをキャプチャーする" Sources.SoftwareCaptureSource.Position "位置:" Sources.SoftwareCaptureSource.Refresh "再読み込みする" @@ -128,7 +137,7 @@ Sources.SoftwareCaptureSource.SelectRegion "領域を選択する" Sources.SoftwareCaptureSource.SelectRegionTooltip "領域ウィンドウには中央にクリックすれば動けます。枠をクリックすればサイズを調整できます。" Sources.SoftwareCaptureSource.Size "大きさ:" Sources.SoftwareCaptureSource.Window "ウィンドウ:" -Sources.SoftwareCaptureSource.WindowCapture "ウィンドウ キャプチャー (他のウィンドウは表示しません。最小化してはできません)" -Sources.SoftwareCaptureSource.WindowCaptureTooltip "普通ウインドウ キャプチャーだけを使うならAeroは有効にすればいいです。対象ウィンドウは最小化状態なら画像が更新できません。画面キャプチャーより画像の更新が速い。Aeroは有効にするなら上のウィンドウは表示しません。" +Sources.SoftwareCaptureSource.WindowCapture "ウィンドウ キャプチャー" +Sources.SoftwareCaptureSource.WindowCaptureTooltip "普通は、ウインドウ キャプチャーだけを使うならAeroを有効にすればいいです。対象ウィンドウが最小化状態なら画像が更新できません。普通は画面キャプチャーより画像の更新が速い。上のウィンドウはAeroが有効にした場合表示しません。" Sources.SoftwareCaptureSource.WindowMinimized "ウィンドウは最小化します" Sources.SoftwareCaptureSource.WindowNotFound "ウィンドウは見つかりません" diff --git a/rundir/locale/ru.txt b/rundir/locale/ru.txt index 2e246103..12345dff 100644 --- a/rundir/locale/ru.txt +++ b/rundir/locale/ru.txt @@ -1,4 +1,4 @@ -Add "Добавить" +Add "Добавить" Apply "Применить" Browse "Найти" Cancel "Отменить" @@ -15,9 +15,14 @@ Scene "Сцену" Settings "Опции" StreamReport "Отчет" +Connection.CouldNotConnect "Невозможно подключиться к серверу" +Connection.CouldNotParseURL "Не удалось обработать URL" +Connection.Disconnected "Отключен от сервера" +Connection.InvalidStream "Неверный stream channel или playpath" + DeleteConfirm.Title "Удалить выбранное?" -GlobalSources.DeleteConfirm "If you delete a global source, it will be removed from any scene that uses it. Are you sure you want to continue?" +GlobalSources.DeleteConfirm "Если вы удалите глобальный источник, он будет удален из всех сцен, в которых он используется. Вы уверены, что хотите продолжить?" Listbox.Add "Добавить $1" Listbox.Center "По центру" @@ -29,6 +34,7 @@ Listbox.MoveUp "Вверх" Listbox.Remove "Удалить" Listbox.Rename "Переименовать" Listbox.ResetSize "Сбросить размер" +Listbox.SetHotkey "Добавить горячую клавишу" MainWindow.Exit "Выход" MainWindow.Plugins "Плагины" @@ -44,13 +50,18 @@ Plugins.Configure "Конфигурация" Plugins.Description "Описание:" Plugins.Filename "Имя файла:" +Scene.Hotkey "Горячая клавиша сцены" Scene.MissingSources "Изображения не загружены в результате недопустимых настроек или отсутствующих плагинов." +Scene.Hotkey.AlreadyInUse "Уже используется." +Scene.Hotkey.Clear "Сбросить" +Scene.Hotkey.Hotkey "Горячая клавиша:" + Settings.Audio "Аудио" Settings.Encoding "Кодирование" Settings.General "Главное" Settings.Hotkeys "Горячие клавиши" -Settings.Info "Эти параментры будут применены после рестарта программы." +Settings.Info "Эти параментры будут применены после перезапуска программы." Settings.Publish "Настройки вещания" Settings.SaveChangesPrompt "Сохранить и применить изменения?" Settings.SaveChangesTitle "Применить настройки?" @@ -65,11 +76,15 @@ Settings.Encoding.Audio.Bitrate "Битрейт:" Settings.Encoding.Audio.Codec "Кодек:" Settings.Encoding.Audio.Format "Формат:" -Settings.Encoding.Video.BufferSize "Размер буфера (кбит):" -Settings.Encoding.Video.MaxBitRate "Максимальный битрейт (кб/с):" -Settings.Encoding.Video.Quality "Качество:" -Settings.Encoding.Video.Tradeoff "CPU пресет:" -Settings.Encoding.Video.UseI444 "Использовать YUV 4:4:4" +Settings.Encoding.Video.BufferSize "Размер буфера (Кбит):" +Settings.Encoding.Video.BufferSizeTooltip "Размер буфера определяет, сколько данных будет загружено в буфер перед отправкой на стрим сервер. Установка более высокого значения, чем битрейт, может улучшить плавность картинки, но при нехватке исходящей скорости интернета может привести к потере данных. \r\n\r\nРекомендуется: Такое же или меньшее значение что и битрейт." +Settings.Encoding.Video.MaxBitRate "Максимальный битрейт (Кбит/с):" +Settings.Encoding.Video.MaxBitRateTooltip "Установка максимального значения битрейта для видео. Напрямую зависит от вашей исходящей скорости интернета. Отметим, что заданный битрейт будет средним значением, но так же на него будет влиять размера буфера.\r\n\r\nВоспользуйтесь сайтом, например speedtest.net, чтобы узнать свою исходящую скорость интернета." +Settings.Encoding.Video.Quality "Качество:" +Settings.Encoding.Video.QualityTooltip "Эта опция будет подстраивать качество в зависимости от установленных битрейта и буфера. При установки высокого значения и низкого битрейта будет жертвоваться качеством в динамичных сценах в пользу статичных." +Settings.Encoding.Video.Tradeoff "CPU пресет:" +Settings.Encoding.Video.TradeoffTooltip "Установка более быстрого (высокого) значения уменьшает нагрузку процессора, жертвуя некоторыми акпектами качества. С другой стороны, установка медленного (низкого) значения повышает качество за счет увеличения нагрузки на процессор.\r\n\r\nРекомендуется: Very Fast" +Settings.Encoding.Video.UseI444 "Использовать YUV 4:4:4" Settings.General.Language "Язык:" Settings.General.Restart "Для смены языка необходимо перезапустить программу." @@ -83,36 +98,39 @@ Settings.Publish.Service "Сервис вещания:" Settings.Publish.Username "Имя пользователя (если имеется):" Settings.Publish.Mode.BandwidthInfo "Пропускная способность" -Settings.Publish.Mode.LiveStream "Живой эфир" +Settings.Publish.Mode.LiveStream "Прямой эфир" Settings.Publish.Mode.Serve "Локальный RTMP Сервер" -Settings.Video.Custom "Пользовательский:" -Settings.Video.DisableAero "Отключить AERO:" -Settings.Video.Downscale "Масштабировать разрешение:" -Settings.Video.FPS "FPS:" -Settings.Video.Monitor "Монитор:" -Settings.Video.Resolution "Базовое разрешение:" +Settings.Video.Custom "Пользовательский:" +Settings.Video.DisableAero "Отключить Aero:" +Settings.Video.DisableAeroTooltip "Рекомендуется отключить Aero при использовании программного захвата." +Settings.Video.Downscale "Масштабировать разрешение:" +Settings.Video.FPS "Кадры в секунду (FPS):" +Settings.Video.Monitor "Монитор:" +Settings.Video.Resolution "Базовое разрешение:" Settings.Video.Downscale.None "Отсутствует" -Sources.BitmapSource "Изображение" -Sources.GlobalSource "Глобальный источник" +Sources.BitmapSource "Изображение" +Sources.GlobalSource "Глобальный источник" Sources.SoftwareCaptureSource "Программный источник захвата" -Sources.BitmapSource.Empty "Изображение не найдено. Пожалуйста, найдите и выберите картинку." +Sources.BitmapSource.Empty "Изображение не найдено. Пожалуйста, найдите и выберите картинку." -Sources.SoftwareCaptureSource.EntireWindow "Все окно" -Sources.SoftwareCaptureSource.InnerWindow "Часть окна" -Sources.SoftwareCaptureSource.Monitor "Монитор:" -Sources.SoftwareCaptureSource.MonitorCapture "Захват экрана (Отключите Aero в настройках видео что бы максимизировать значение FPS)" -Sources.SoftwareCaptureSource.MouseCapture "Захват мыши" -Sources.SoftwareCaptureSource.Position "Положение:" -Sources.SoftwareCaptureSource.Refresh "Обновить" -Sources.SoftwareCaptureSource.RegionCapture "Конкретная область" -Sources.SoftwareCaptureSource.RegionWindowText "Нажмите клавишу Enter, Esc или щелкните за пределами этого прямоугольника для завершения." -Sources.SoftwareCaptureSource.SelectRegion "Выбрать область" -Sources.SoftwareCaptureSource.Size "Размер:" -Sources.SoftwareCaptureSource.Window "Процесс:" -Sources.SoftwareCaptureSource.WindowCapture "Захват Окна (Другие окна не будут видны. Нельзя свернуть. Больше FPS)" -Sources.SoftwareCaptureSource.WindowMinimized "Окно минимизированно" -Sources.SoftwareCaptureSource.WindowNotFound "Окно не найдено" +Sources.SoftwareCaptureSource.EntireWindow "Все окно" +Sources.SoftwareCaptureSource.InnerWindow "Окно без рамки" +Sources.SoftwareCaptureSource.Monitor "Монитор:" +Sources.SoftwareCaptureSource.MonitorCapture "Захват экрана" +Sources.SoftwareCaptureSource.MonitorCaptureTooltip "Отключите Aero в настройках видео, чтобы максимизировать значение FPS" +Sources.SoftwareCaptureSource.MouseCapture "Захват мыши" +Sources.SoftwareCaptureSource.Position "Положение:" +Sources.SoftwareCaptureSource.Refresh "Обновить" +Sources.SoftwareCaptureSource.RegionCapture "Конкретная область" +Sources.SoftwareCaptureSource.RegionWindowText "Нажмите клавишу Enter, Esc или щелкните за пределами этого прямоугольника для завершения." +Sources.SoftwareCaptureSource.SelectRegion "Выбрать область" +Sources.SoftwareCaptureSource.Size "Размер:" +Sources.SoftwareCaptureSource.Window "Процесс:" +Sources.SoftwareCaptureSource.WindowCapture "Захват окна" +Sources.SoftwareCaptureSource.WindowCaptureTooltip "Aero может быть включено при использовании только функции захвата окна. Изображение не обновляется, если целевое окно свернуто. Захват с помощью этой функции обычно быстрее, чем захват всего экрана или области экрана. Если Aero включено, окна, перекрывающие целевое окно, не будут видны." +Sources.SoftwareCaptureSource.WindowMinimized "Окно минимизированно" +Sources.SoftwareCaptureSource.WindowNotFound "Окно не найдено"