diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 22bf2d79c..36e534a20 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -4,20 +4,34 @@ on: push: paths-ignore: - "cmake/**" + branches: ['*'] + tags: ['*'] pull_request: paths: - "docs/sphinx/**" + workflow_dispatch: jobs: docs: runs-on: ubuntu-latest + outputs: + commitHash: ${{ steps.setup.outputs.commitHash }} + commitBranch: ${{ steps.setup.outputs.commitBranch }} + fullCommitHash: ${{ steps.setup.outputs.fullCommitHash }} steps: - name: Checkout uses: actions/checkout@v3 + - name: Setup Environment id: setup run: | + BRANCH=$(git describe --exact-match --tags 2> /dev/null || git branch --show-current) + # Remove patch version from tag + BRANCH=$(echo ${BRANCH} | sed -e 's/\.[0-9]*$//') + echo "commitBranch=${BRANCH}" >> $GITHUB_OUTPUT echo "commitHash=$(git describe --exact-match --tags 2> /dev/null || git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + echo "fullCommitHash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + - name: Bump Version Number shell: bash if: github.event_name == 'push' @@ -40,6 +54,7 @@ jobs: SCOPY="copyright = '([A-Za-z0-9, ]+)'" RCOPY="copyright = '2017-$(date +"%Y"), Hugh Bailey'" sed -i -E -e "s/${SVER}/${RVER}/g" -e "s/${SREL}/${RREL}/g" -e "s/${SCOPY}/${RCOPY}/g" docs/sphinx/conf.py + - uses: totaldebug/sphinx-publish-action@1.2.0 with: sphinx_src: 'docs/sphinx' @@ -47,9 +62,47 @@ jobs: target_branch: 'master' target_path: '../home/_build' pre_build_commands: 'pip install -Iv sphinx==5.1.1' + - uses: actions/upload-artifact@v3 with: name: 'OBS Studio Docs ${{ steps.setup.outputs.commitHash }}' path: | ${{ runner.temp }}/_github_home/_build !${{ runner.temp }}/_github_home/_build/.doctrees + + deploy: + runs-on: ubuntu-latest + needs: docs + if: ${{ github.event_name == 'workflow_dispatch' || (github.repository_owner == 'obsproject' && startsWith(github.ref, 'refs/tags/') && github.event_name != 'pull_request') }} + environment: + name: cf-pages-deploy + concurrency: + group: "cf-pages-deployment" + cancel-in-progress: true + steps: + - uses: actions/download-artifact@v3 + with: + name: 'OBS Studio Docs ${{ needs.docs.outputs.commitHash }}' + path: docs + + - name: Setup redirects + run: | + echo "/previous/27.2 https://obsproject.com/docs/27.2 302" >> docs/_redirects + echo "/previous/:major.:minor https://:major-:minor.${{ vars.CF_PAGES_PROJECT }}.pages.dev 302" >> docs/_redirects + + - name: Publish to live page + uses: cloudflare/wrangler-action@4c10c1822abba527d820b29e6333e7f5dac2cabd + with: + workingDirectory: docs + apiToken: ${{ secrets.CF_API_TOKEN }} + accountId: ${{ secrets.CF_ACCOUNT_ID }} + command: pages publish . --project-name=${{ vars.CF_PAGES_PROJECT }} --commit-hash='${{ needs.docs.outputs.fullCommitHash }}' + + - name: Publish to tag alias + if: ${{ startsWith(github.ref, 'refs/tags/') }} + uses: cloudflare/wrangler-action@4c10c1822abba527d820b29e6333e7f5dac2cabd + with: + workingDirectory: docs + apiToken: ${{ secrets.CF_API_TOKEN }} + accountId: ${{ secrets.CF_ACCOUNT_ID }} + command: pages publish . --project-name=${{ vars.CF_PAGES_PROJECT }} --commit-hash='${{ needs.docs.outputs.fullCommitHash }}' --branch='${{ needs.docs.outputs.commitBranch }}' diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index daa5b8694..800c2d70a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,11 +22,11 @@ env: QT_HASH_MAC_X86_64: 'c5ed7bc9f6e802910ec539066bcf0a8d64100fafce568071f264c88c22c5859b' QT_HASH_MAC_ARM64: '1ce472fd1e28f947456b72b1d7ab929d6e93cb774c2928e22eca9bb751b12ccf' QT_HASH_MAC_UNIVERSAL: '873f7c9c9f7fcee740a79c075b32a505c932c816d928807fa16f3439c610fbfd' - QT_VERSION_WIN: '6.4.1' + QT_VERSION_WIN: '6.3.1' DEPS_VERSION_MAC: '2022-11-21' DEPS_HASH_MAC_X86_64: 'ed0a145e88496f8975da14a07939dbe5633e60510aada34509a4aef64a66e438' DEPS_HASH_MAC_ARM64: 'f397dc524e5ee7f85684f0b9661c45957446e28d166fcd6dfacf895c9d4d2521' - DEPS_VERSION_WIN: '2022-11-21' + DEPS_VERSION_WIN: '2023-01-06' VLC_VERSION_MAC: '3.0.8' VLC_HASH_MAC: 'e0149ef4a20a19b9ecd87309c2d27787ee3f47dfd47c6639644bc1f6fd95bdf6' VLC_VERSION_WIN: '3.0.0-git' diff --git a/UI/auth-restream.cpp b/UI/auth-restream.cpp index f242deb9e..689ea14e8 100644 --- a/UI/auth-restream.cpp +++ b/UI/auth-restream.cpp @@ -200,7 +200,9 @@ void RestreamAuth::LoadUI() main->Config(), service(), "DockState"); QByteArray dockState = QByteArray::fromBase64(QByteArray(dockStateStr)); - main->restoreState(dockState); + + if (main->isVisible() || !main->isMaximized()) + main->restoreState(dockState); } uiLoaded = true; diff --git a/UI/auth-twitch.cpp b/UI/auth-twitch.cpp index af942e9e2..c01eb6e4e 100644 --- a/UI/auth-twitch.cpp +++ b/UI/auth-twitch.cpp @@ -276,7 +276,9 @@ void TwitchAuth::LoadUI() main->Config(), service(), "DockState"); QByteArray dockState = QByteArray::fromBase64(QByteArray(dockStateStr)); - main->restoreState(dockState); + + if (main->isVisible() || !main->isMaximized()) + main->restoreState(dockState); } TryLoadSecondaryUIPanes(); @@ -405,7 +407,9 @@ void TwitchAuth::LoadSecondaryUIPanes() main->Config(), service(), "DockState"); QByteArray dockState = QByteArray::fromBase64(QByteArray(dockStateStr)); - main->restoreState(dockState); + + if (main->isVisible() || !main->isMaximized()) + main->restoreState(dockState); } } diff --git a/UI/auth-youtube.cpp b/UI/auth-youtube.cpp index 3a8cc856e..f2ee5e42b 100644 --- a/UI/auth-youtube.cpp +++ b/UI/auth-youtube.cpp @@ -164,7 +164,9 @@ void YoutubeAuth::LoadUI() main->Config(), service(), "DockState"); QByteArray dockState = QByteArray::fromBase64(QByteArray(dockStateStr)); - main->restoreState(dockState); + + if (main->isVisible() || !main->isMaximized()) + main->restoreState(dockState); } #endif diff --git a/UI/forms/OBSYoutubeActions.ui b/UI/forms/OBSYoutubeActions.ui index 5825a2314..0594dc942 100644 --- a/UI/forms/OBSYoutubeActions.ui +++ b/UI/forms/OBSYoutubeActions.ui @@ -646,6 +646,9 @@ QLayout::SetNoConstraint + + 10 + diff --git a/UI/frontend-plugins/frontend-tools/auto-scene-switcher-nix.cpp b/UI/frontend-plugins/frontend-tools/auto-scene-switcher-nix.cpp index 2aa0120a6..e5e5f96af 100644 --- a/UI/frontend-plugins/frontend-tools/auto-scene-switcher-nix.cpp +++ b/UI/frontend-plugins/frontend-tools/auto-scene-switcher-nix.cpp @@ -46,8 +46,14 @@ static bool ewmhIsSupported() unsigned long num = 0, bytes = 0; unsigned char *data = NULL; Window ewmh_window = 0; + Window root_window = 0; - int status = XGetWindowProperty(display, DefaultRootWindow(display), + root_window = DefaultRootWindow(display); + if (!root_window) { + return false; + } + + int status = XGetWindowProperty(display, root_window, netSupportingWmCheck, 0L, 1L, false, XA_WINDOW, &actualType, &format, &num, &bytes, &data); @@ -97,6 +103,9 @@ static std::vector getTopLevelWindows() for (int i = 0; i < ScreenCount(disp()); ++i) { Window rootWin = RootWindow(disp(), i); + if (!rootWin) { + continue; + } int status = XGetWindowProperty(disp(), rootWin, netClList, 0L, ~0L, false, AnyPropertyType, @@ -119,6 +128,9 @@ static std::vector getTopLevelWindows() static std::string GetWindowTitle(size_t i) { Window w = getTopLevelWindows().at(i); + if (!w) { + return ""; + } std::string windowTitle; char *name; @@ -164,11 +176,17 @@ void GetCurrentWindowTitle(string &title) char *name; Window rootWin = RootWindow(disp(), 0); + if (!rootWin) { + return; + } XGetWindowProperty(disp(), rootWin, active, 0L, ~0L, false, AnyPropertyType, &actualType, &format, &num, &bytes, (uint8_t **)&data); + if (!data[0]) { + return; + } int status = XFetchName(disp(), data[0], &name); if (status >= Success && name != nullptr) { diff --git a/UI/properties-view.cpp b/UI/properties-view.cpp index 36d5ba1ef..6ceceb237 100644 --- a/UI/properties-view.cpp +++ b/UI/properties-view.cpp @@ -1750,6 +1750,11 @@ bool WidgetInfo::PathChanged(const char *setting) path = SaveFile(view, QT_UTF8(desc), QT_UTF8(default_path), QT_UTF8(filter)); +#ifdef __APPLE__ + // TODO: Revisit when QTBUG-42661 is fixed + widget->window()->raise(); +#endif + if (path.isEmpty()) return false; @@ -2217,6 +2222,10 @@ void WidgetInfo::EditListAddFiles() QStringList files = OpenFiles(App()->GetMainWindow(), title, QT_UTF8(default_path), QT_UTF8(filter)); +#ifdef __APPLE__ + // TODO: Revisit when QTBUG-42661 is fixed + widget->window()->raise(); +#endif if (files.count() == 0) return; @@ -2237,6 +2246,10 @@ void WidgetInfo::EditListAddDir() QString dir = SelectDirectory(App()->GetMainWindow(), title, QT_UTF8(default_path)); +#ifdef __APPLE__ + // TODO: Revisit when QTBUG-42661 is fixed + widget->window()->raise(); +#endif if (dir.isEmpty()) return; diff --git a/UI/win-update/updater/updater.cpp b/UI/win-update/updater/updater.cpp index f65808fa1..44e5a12ac 100644 --- a/UI/win-update/updater/updater.cpp +++ b/UI/win-update/updater/updater.cpp @@ -1289,6 +1289,7 @@ static bool Update(wchar_t *cmdLine) bool bIsPortable = false; wstring branch = L"stable"; + wstring appdata; if (cmdLine[0]) { int argc; @@ -1303,6 +1304,9 @@ static bool Update(wchar_t *cmdLine) } else if (wcsncmp(argv[i], L"--branch=", 9) == 0) { branch = argv[i] + 9; + } else if (wcsncmp(argv[i], L"--appdata=", + 10) == 0) { + appdata = argv[i] + 10; } else if (wcscmp(argv[i], L"--portable") == 0) { bIsPortable = true; @@ -1322,18 +1326,16 @@ static bool Update(wchar_t *cmdLine) GetCurrentDirectory(_countof(lpAppDataPath), lpAppDataPath); StringCbCat(lpAppDataPath, sizeof(lpAppDataPath), L"\\config"); } else { - DWORD ret; - ret = GetEnvironmentVariable(L"OBS_USER_APPDATA_PATH", - lpAppDataPath, - _countof(lpAppDataPath)); - - if (ret >= _countof(lpAppDataPath)) { - Status(L"Update failed: Could not determine AppData " - L"location"); - return false; - } - - if (!ret) { + if (!appdata.empty()) { + HRESULT hr = StringCbCopy(lpAppDataPath, + sizeof(lpAppDataPath), + appdata.c_str()); + if (hr != S_OK) { + Status(L"Update failed: Could not determine AppData " + L"location"); + return false; + } + } else { CoTaskMemPtr pOut; HRESULT hr = SHGetKnownFolderPath( FOLDERID_RoamingAppData, KF_FLAG_DEFAULT, @@ -1648,6 +1650,7 @@ static bool Update(wchar_t *cmdLine) }; if (!bIsPortable) { + Status(L"Installing Virtual Camera..."); wchar_t regsvr[MAX_PATH]; wchar_t src[MAX_PATH]; wchar_t tmp[MAX_PATH]; @@ -1681,11 +1684,13 @@ static bool Update(wchar_t *cmdLine) /* ------------------------------------- * * Update hook files and vulkan registry */ + Status(L"Updating Game Capture hooks..."); UpdateHookFiles(); /* ------------------------------------- * * Finish */ + Status(L"Cleaning up..."); /* If we get here, all updates installed successfully so we can purge * the old versions */ for (update_t &update : updates) { @@ -1754,7 +1759,7 @@ static void CancelUpdate(bool quit) } } -static void LaunchOBS() +static void LaunchOBS(bool portable) { wchar_t cwd[MAX_PATH]; wchar_t newCwd[MAX_PATH]; @@ -1794,6 +1799,9 @@ static void LaunchOBS() execInfo.lpDirectory = newCwd; execInfo.nShow = SW_SHOWNORMAL; + if (portable) + execInfo.lpParameters = L"--portable"; + ShellExecuteEx(&execInfo); } @@ -1842,13 +1850,26 @@ static int RestartAsAdmin(LPCWSTR lpCmdLine, LPCWSTR cwd) return 0; } + /* If the admin is a different user, add the path to the user's + * AppData to the command line so we can load the correct manifest. */ + wstring elevatedCmdLine(lpCmdLine); + CoTaskMemPtr pOut; + HRESULT hr = SHGetKnownFolderPath(FOLDERID_RoamingAppData, + KF_FLAG_DEFAULT, nullptr, &pOut); + if (hr == S_OK) { + elevatedCmdLine += L" \"--appdata="; + elevatedCmdLine += pOut; + elevatedCmdLine += L"\""; + } + SHELLEXECUTEINFO shExInfo = {0}; shExInfo.cbSize = sizeof(shExInfo); shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS; shExInfo.hwnd = 0; - shExInfo.lpVerb = L"runas"; /* Operation to perform */ - shExInfo.lpFile = myPath; /* Application to start */ - shExInfo.lpParameters = lpCmdLine; /* Additional parameters */ + shExInfo.lpVerb = L"runas"; /* Operation to perform */ + shExInfo.lpFile = myPath; /* Application to start */ + shExInfo.lpParameters = + elevatedCmdLine.c_str(); /* Additional parameters */ shExInfo.lpDirectory = cwd; shExInfo.nShow = SW_NORMAL; shExInfo.hInstApp = 0; @@ -1857,14 +1878,6 @@ static int RestartAsAdmin(LPCWSTR lpCmdLine, LPCWSTR cwd) * windows :( */ AllowSetForegroundWindow(ASFW_ANY); - /* if the admin is a different user, save the path to the user's - * appdata so we can load the correct manifest */ - CoTaskMemPtr pOut; - HRESULT hr = SHGetKnownFolderPath(FOLDERID_RoamingAppData, - KF_FLAG_DEFAULT, nullptr, &pOut); - if (hr == S_OK) - SetEnvironmentVariable(L"OBS_USER_APPDATA_PATH", pOut); - if (ShellExecuteEx(&shExInfo)) { DWORD exitCode; @@ -1907,6 +1920,8 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR lpCmdLine, int) GetCurrentDirectoryW(_countof(cwd) - 1, cwd); is32bit = wcsstr(cwd, L"bin\\32bit") != nullptr; + bool isPortable = wcsstr(lpCmdLine, L"Portable") != nullptr || + wcsstr(lpCmdLine, L"--portable") != nullptr; if (!IsWindows10OrGreater()) { MessageBox( @@ -1942,7 +1957,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR lpCmdLine, int) nullptr); SetCurrentDirectory(newPath); - LaunchOBS(); + LaunchOBS(isPortable); } if (hLowMutex) { @@ -1990,7 +2005,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR lpCmdLine, int) WinHandle hMutex = OpenMutex( SYNCHRONIZE, false, L"OBSUpdaterRunningAsNonAdminUser"); if (msg.wParam == 1 && !hMutex) { - LaunchOBS(); + LaunchOBS(isPortable); } return (int)msg.wParam; diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index 6a0985419..a46516737 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -1007,7 +1007,7 @@ bool SimpleOutput::StartStreaming(obs_service_t *service) else lastError = string(); - const char *type = obs_service_get_output_type(service); + const char *type = obs_output_get_id(streamOutput); blog(LOG_WARNING, "Stream output type '%s' failed to start!%s%s", type, hasLastError ? " Last Error: " : "", hasLastError ? error : ""); return false; @@ -1482,7 +1482,6 @@ inline void AdvancedOutput::SetupStreaming() obs_output_set_audio_encoder(streamOutput, streamAudioEnc, 0); obs_encoder_set_scaled_size(videoStreaming, cx, cy); - obs_encoder_set_video(videoStreaming, obs_get_video()); const char *id = obs_service_get_id(main->GetService()); if (strcmp(id, "rtmp_custom") == 0) { @@ -1536,7 +1535,6 @@ inline void AdvancedOutput::SetupRecording() } obs_encoder_set_scaled_size(videoRecording, cx, cy); - obs_encoder_set_video(videoRecording, obs_get_video()); obs_output_set_video_encoder(fileOutput, videoRecording); if (replayBuffer) obs_output_set_video_encoder(replayBuffer, @@ -1906,7 +1904,7 @@ bool AdvancedOutput::StartStreaming(obs_service_t *service) else lastError = string(); - const char *type = obs_service_get_output_type(service); + const char *type = obs_output_get_id(streamOutput); blog(LOG_WARNING, "Stream output type '%s' failed to start!%s%s", type, hasLastError ? " Last Error: " : "", hasLastError ? error : ""); return false; diff --git a/UI/window-basic-settings-stream.cpp b/UI/window-basic-settings-stream.cpp index 8dea18215..f691baefc 100644 --- a/UI/window-basic-settings-stream.cpp +++ b/UI/window-basic-settings-stream.cpp @@ -1481,6 +1481,7 @@ void OBSBasicSettings::ResetEncoders(bool streamOnly) } idx = ui->advOutEncoder->findData(lastAdvEnc); + s2.unblock(); ui->advOutEncoder->setCurrentIndex(idx); } @@ -1494,6 +1495,7 @@ void OBSBasicSettings::ResetEncoders(bool streamOnly) } idx = ui->simpleOutStrEncoder->findData(lastEnc); + s1.unblock(); ui->simpleOutStrEncoder->setCurrentIndex(idx); } } diff --git a/cmake/Modules/FindLibva.cmake b/cmake/Modules/FindLibva.cmake index afba96b5d..0e49cb42e 100644 --- a/cmake/Modules/FindLibva.cmake +++ b/cmake/Modules/FindLibva.cmake @@ -1,9 +1,10 @@ -# * Try to find Libva, once done this will define # -# * LIBVA_FOUND - system has Libva -# * LIBVA_INCLUDE_DIRS - the Libva include directory -# * LIBVA_LIBRARIES - the libraries needed to use Libva -# * LIBVA_DEFINITIONS - Compiler switches required for using Libva +# This module defines the following variables: +# +# * LIBVA_FOUND - The component was found +# * LIBVA_INCLUDE_DIRS - The component include directory +# * LIBVA_LIBRARIES - The component library Libva +# * LIBVA_DRM_LIBRARIES - The component library Libva DRM # Use pkg-config to get the directories and then use these values in the # find_path() and find_library() calls @@ -11,14 +12,14 @@ find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) pkg_check_modules(_LIBVA libva) + pkg_check_modules(_LIBVA_DRM libva-drm) endif() find_path( LIBVA_INCLUDE_DIR - NAMES va.h + NAMES va/va.h va/va_drm.h HINTS ${_LIBVA_INCLUDE_DIRS} - PATHS /usr/include /usr/local/include /opt/local/include - PATH_SUFFIXES va/) + PATHS /usr/include /usr/local/include /opt/local/include) find_library( LIBVA_LIB @@ -26,14 +27,21 @@ find_library( HINTS ${_LIBVA_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib /opt/local/lib) +find_library( + LIBVA_DRM_LIB + NAMES ${_LIBVA_DRM_LIBRARIES} libva-drm + HINTS ${_LIBVA_DRM_LIBRARY_DIRS} + PATHS /usr/lib /usr/local/lib /opt/local/lib) + include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Libva REQUIRED_VARS LIBVA_LIB - LIBVA_INCLUDE_DIR) -mark_as_advanced(LIBVA_INCLUDE_DIR LIBVA_LIB) +find_package_handle_standard_args(Libva REQUIRED_VARS LIBVA_INCLUDE_DIR + LIBVA_LIB LIBVA_DRM_LIB) +mark_as_advanced(LIBVA_INCLUDE_DIR LIBVA_LIB LIBVA_DRM_LIB) if(LIBVA_FOUND) set(LIBVA_INCLUDE_DIRS ${LIBVA_INCLUDE_DIR}) set(LIBVA_LIBRARIES ${LIBVA_LIB}) + set(LIBVA_DRM_LIBRARIES ${LIBVA_DRM_LIB}) if(NOT TARGET Libva::va) if(IS_ABSOLUTE "${LIBVA_LIBRARIES}") @@ -45,5 +53,23 @@ if(LIBVA_FOUND) set_target_properties(Libva::va PROPERTIES IMPORTED_LIBNAME "${LIBVA_LIBRARIES}") endif() + + set_target_properties(Libva::va PROPERTIES INTERFACE_INCLUDE_DIRECTORIES + "${LIBVA_INCLUDE_DIRS}") + endif() + + if(NOT TARGET Libva::drm) + if(IS_ABSOLUTE "${LIBVA_DRM_LIBRARIES}") + add_library(Libva::drm UNKNOWN IMPORTED) + set_target_properties(Libva::drm PROPERTIES IMPORTED_LOCATION + "${LIBVA_DRM_LIBRARIES}") + else() + add_library(Libva::drm INTERFACE IMPORTED) + set_target_properties(Libva::drm PROPERTIES IMPORTED_LIBNAME + "${LIBVA_DRM_LIBRARIES}") + endif() + + set_target_properties(Libva::drm PROPERTIES INTERFACE_INCLUDE_DIRECTORIES + "${LIBVA_INCLUDE_DIRS}") endif() endif() diff --git a/docs/sphinx/reference-libobs-graphics-graphics.rst b/docs/sphinx/reference-libobs-graphics-graphics.rst index 16d183fe7..aeba70545 100644 --- a/docs/sphinx/reference-libobs-graphics-graphics.rst +++ b/docs/sphinx/reference-libobs-graphics-graphics.rst @@ -1009,7 +1009,7 @@ Texture Functions .. function:: gs_texture_t *gs_texture_create_from_dmabuf(unsigned int width, unsigned int height, uint32_t drm_format, enum gs_color_format color_format, uint32_t n_planes, const int *fds, const uint32_t *strides, const uint32_t *offsets, const uint64_t *modifiers) - **Linux only:** Creates a texture from DMA-BUF metadata. + **only Linux, FreeBSD, DragonFly:** Creates a texture from DMA-BUF metadata. Exchanging DMA-BUFs is a verbose process because of its multiplanar nature. For example, YUV can have each plane as a color channel, or a monitor buffer @@ -1049,7 +1049,7 @@ Texture Functions .. function:: bool *gs_query_dmabuf_capabilities(enum gs_dmabuf_flags *dmabuf_flags, uint32_t **drm_formats, size_t *n_formats) - **Linux only:** Queries the capabilities for DMA-BUFs. + **only Linux, FreeBSD, DragonFly:** Queries the capabilities for DMA-BUFs. Graphics cards can optimize frame buffers by storing them in custom layouts, depending on their hardware features. These layouts can make these frame @@ -1068,7 +1068,7 @@ Texture Functions .. function:: bool *gs_query_dmabuf_modifiers_for_format(uint32_t drm_format, uint64_t **modifiers, size_t *n_modifiers) - **Linux only:** Queries the supported DMA-BUF modifiers for a given format. + **only Linux, FreeBSD, DragonFly:** Queries the supported DMA-BUF modifiers for a given format. This function queries all supported explicit modifiers for a format, stores them as an array and returns the number of supported modifiers. diff --git a/docs/sphinx/reference-outputs.rst b/docs/sphinx/reference-outputs.rst index a0766ef48..730a6bb4b 100644 --- a/docs/sphinx/reference-outputs.rst +++ b/docs/sphinx/reference-outputs.rst @@ -373,12 +373,26 @@ General Output Functions --------------------- +.. function:: bool obs_weak_output_references_output(obs_weak_output_t *weak, obs_output_t *output) + + Compares a weak output reference with an output. + + :return: Whether the weak output reference ties back to the specified output + +--------------------- + .. function:: const char *obs_output_get_name(const obs_output_t *output) :return: The name of the output --------------------- +.. function:: const char *obs_output_get_id(const obs_output_t *output) + + :return: The output's type identifier string + +--------------------- + .. function:: bool obs_output_start(obs_output_t *output) Starts the output. @@ -621,6 +635,18 @@ General Output Functions --------------------- +.. function:: void obs_output_output_caption_text1(obs_output_t *output, const char *text) + void obs_output_output_caption_text2(obs_output_t *output, const char *text, double display_duration) + + Outputs captions from the specified text input. *text1* is the same as + *text2*, except that the *display_duration* is hardcoded to 2.0 seconds. + + *display_duration* represents the minimum quantity of time that a given + caption can be displayed for before moving onto the next caption in the + queue. + +--------------------- + .. function:: float obs_output_get_congestion(obs_output_t *output) :return: The congestion value. This value is used to visualize the diff --git a/libobs-d3d11/d3d11-subsystem.cpp b/libobs-d3d11/d3d11-subsystem.cpp index 271d9998e..4028bcc5a 100644 --- a/libobs-d3d11/d3d11-subsystem.cpp +++ b/libobs-d3d11/d3d11-subsystem.cpp @@ -1224,6 +1224,7 @@ static inline void LogAdapterMonitors(IDXGIAdapter1 *adapter) target.monitorFriendlyDeviceName[0] = 0; } + UINT bits_per_color = 8; DXGI_COLOR_SPACE_TYPE type = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; FLOAT min_luminance = 0.f; @@ -1231,6 +1232,7 @@ static inline void LogAdapterMonitors(IDXGIAdapter1 *adapter) FLOAT max_full_frame_luminance = 0.f; DXGI_OUTPUT_DESC1 desc1; if (GetOutputDesc1(output, &desc1)) { + bits_per_color = desc1.BitsPerColor; type = desc1.ColorSpace; min_luminance = desc1.MinLuminance; max_luminance = desc1.MaxLuminance; @@ -1260,13 +1262,14 @@ static inline void LogAdapterMonitors(IDXGIAdapter1 *adapter) "\t size={%d, %d}\n" "\t attached=%s\n" "\t refresh=%u\n" + "\t bits_per_color=%u\n" "\t space=%s\n" "\t sdr_white_nits=%lu\n" "\t nit_range=[min=%f, max=%f, max_full_frame=%f]", i, target.monitorFriendlyDeviceName, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, - desc.AttachedToDesktop ? "true" : "false", refresh, space, - nits, min_luminance, max_luminance, + desc.AttachedToDesktop ? "true" : "false", refresh, + bits_per_color, space, nits, min_luminance, max_luminance, max_full_frame_luminance); } } diff --git a/libobs-opengl/gl-cocoa.m b/libobs-opengl/gl-cocoa.m index caaf3bf85..599ba9525 100644 --- a/libobs-opengl/gl-cocoa.m +++ b/libobs-opengl/gl-cocoa.m @@ -265,6 +265,8 @@ void device_leave_context(gs_device_t *device) { glFlush(); [NSOpenGLContext clearCurrentContext]; + device->cur_vertex_buffer = NULL; + device->cur_index_buffer = NULL; device->cur_render_target = NULL; device->cur_zstencil_buffer = NULL; device->cur_swap = NULL; diff --git a/libobs-opengl/gl-nix.c b/libobs-opengl/gl-nix.c index 52a300346..c775079fc 100644 --- a/libobs-opengl/gl-nix.c +++ b/libobs-opengl/gl-nix.c @@ -86,6 +86,12 @@ extern void device_enter_context(gs_device_t *device) extern void device_leave_context(gs_device_t *device) { + device->cur_render_target = NULL; + device->cur_zstencil_buffer = NULL; + device->cur_vertex_buffer = NULL; + device->cur_index_buffer = NULL; + device->cur_swap = NULL; + device->cur_fbo = NULL; gl_vtable->device_leave_context(device); } diff --git a/libobs-opengl/gl-x11-egl.c b/libobs-opengl/gl-x11-egl.c index df722f376..56086a997 100644 --- a/libobs-opengl/gl-x11-egl.c +++ b/libobs-opengl/gl-x11-egl.c @@ -82,6 +82,7 @@ struct gl_platform { EGLConfig config; EGLContext context; EGLSurface pbuffer; + bool close_xdisplay; }; /* The following utility function is copied verbatim from GLX code. */ @@ -269,15 +270,19 @@ static void gl_x11_egl_windowinfo_destroy(struct gl_windowinfo *info) bfree(info); } -static Display *open_windowless_display(Display *platform_display) +static Display *open_windowless_display(bool *should_close) { + Display *platform_display = obs_get_nix_platform_display(); Display *display; xcb_connection_t *xcb_conn; - if (platform_display) + if (platform_display) { display = platform_display; - else + *should_close = false; + } else { display = XOpenDisplay(NULL); + *should_close = true; + } if (!display) { blog(LOG_ERROR, "Unable to open new X connection!"); @@ -298,7 +303,8 @@ static Display *open_windowless_display(Display *platform_display) return display; error: - XCloseDisplay(display); + if (*should_close) + XCloseDisplay(display); return NULL; } @@ -325,8 +331,7 @@ static struct gl_platform *gl_x11_egl_platform_create(gs_device_t *device, For an explanation see here: http://xcb.freedesktop.org/MixingCalls/ Essentially, EGL requires Xlib. Everything else we use xcb. */ struct gl_platform *plat = bmalloc(sizeof(struct gl_platform)); - Display *platform_display = obs_get_nix_platform_display(); - Display *display = open_windowless_display(platform_display); + Display *display = open_windowless_display(&plat->close_xdisplay); if (!display) { goto fail_display_open; @@ -363,7 +368,8 @@ fail_make_current: gl_context_destroy(plat); fail_context_create: fail_load_gl: - XCloseDisplay(display); + if (plat->close_xdisplay) + XCloseDisplay(plat->xdisplay); fail_display_open: bfree(plat); plat = NULL; @@ -379,6 +385,8 @@ static void gl_x11_egl_platform_destroy(struct gl_platform *plat) gl_context_destroy(plat); eglTerminate(plat->edisplay); + if (plat->close_xdisplay) + XCloseDisplay(plat->xdisplay); bfree(plat); } @@ -590,7 +598,7 @@ static bool gl_x11_egl_device_query_dmabuf_capabilities( { struct gl_platform *plat = device->plat; - return gl_egl_query_dmabuf_capabilities(plat->xdisplay, dmabuf_flags, + return gl_egl_query_dmabuf_capabilities(plat->edisplay, dmabuf_flags, drm_formats, n_formats); } @@ -601,7 +609,7 @@ static bool gl_x11_egl_device_query_dmabuf_modifiers_for_format( struct gl_platform *plat = device->plat; return gl_egl_query_dmabuf_modifiers_for_format( - plat->xdisplay, drm_format, modifiers, n_modifiers); + plat->edisplay, drm_format, modifiers, n_modifiers); } static const struct gl_winsys_vtable egl_x11_winsys_vtable = { diff --git a/libobs/graphics/device-exports.h b/libobs/graphics/device-exports.h index 6200796ed..4fcae2e6a 100644 --- a/libobs/graphics/device-exports.h +++ b/libobs/graphics/device-exports.h @@ -178,7 +178,7 @@ EXPORT void device_debug_marker_begin(gs_device_t *device, const float color[4]); EXPORT void device_debug_marker_end(gs_device_t *device); -#if __linux__ +#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) EXPORT gs_texture_t *device_texture_create_from_dmabuf( gs_device_t *device, unsigned int width, unsigned int height, diff --git a/libobs/graphics/graphics-imports.c b/libobs/graphics/graphics-imports.c index b7a32890e..00681680c 100644 --- a/libobs/graphics/graphics-imports.c +++ b/libobs/graphics/graphics-imports.c @@ -234,7 +234,7 @@ bool load_graphics_imports(struct gs_exports *exports, void *module, GRAPHICS_IMPORT_OPTIONAL(device_stagesurface_create_p010); GRAPHICS_IMPORT_OPTIONAL(device_register_loss_callbacks); GRAPHICS_IMPORT_OPTIONAL(device_unregister_loss_callbacks); -#elif __linux__ +#elif defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) GRAPHICS_IMPORT(device_texture_create_from_dmabuf); GRAPHICS_IMPORT(device_query_dmabuf_capabilities); GRAPHICS_IMPORT(device_query_dmabuf_modifiers_for_format); diff --git a/libobs/graphics/graphics-internal.h b/libobs/graphics/graphics-internal.h index 434b76187..137a02196 100644 --- a/libobs/graphics/graphics-internal.h +++ b/libobs/graphics/graphics-internal.h @@ -348,7 +348,7 @@ struct gs_exports { gs_device_t *device, const struct gs_device_loss *callbacks); void (*device_unregister_loss_callbacks)(gs_device_t *device, void *data); -#elif __linux__ +#elif defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) struct gs_texture *(*device_texture_create_from_dmabuf)( gs_device_t *device, unsigned int width, unsigned int height, uint32_t drm_format, enum gs_color_format color_format, diff --git a/libobs/graphics/graphics.c b/libobs/graphics/graphics.c index c68b26afc..7bc03dff1 100644 --- a/libobs/graphics/graphics.c +++ b/libobs/graphics/graphics.c @@ -1387,7 +1387,7 @@ gs_texture_t *gs_texture_create(uint32_t width, uint32_t height, levels, data, flags); } -#if __linux__ +#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) gs_texture_t *gs_texture_create_from_dmabuf( unsigned int width, unsigned int height, uint32_t drm_format, diff --git a/libobs/graphics/graphics.h b/libobs/graphics/graphics.h index b18f5d9bb..5b309d002 100644 --- a/libobs/graphics/graphics.h +++ b/libobs/graphics/graphics.h @@ -967,7 +967,7 @@ EXPORT gs_stagesurf_t *gs_stagesurface_create_p010(uint32_t width, EXPORT void gs_register_loss_callbacks(const struct gs_device_loss *callbacks); EXPORT void gs_unregister_loss_callbacks(void *data); -#elif defined(__linux__) || defined(__FreeBSD__) +#elif defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) EXPORT gs_texture_t *gs_texture_create_from_dmabuf( unsigned int width, unsigned int height, uint32_t drm_format, diff --git a/libobs/obs-config.h b/libobs/obs-config.h index 5fcc6bea0..1772f4790 100644 --- a/libobs/obs-config.h +++ b/libobs/obs-config.h @@ -41,7 +41,7 @@ * * Reset to zero each major or minor version */ -#define LIBOBS_API_PATCH_VER 0 +#define LIBOBS_API_PATCH_VER 2 #define MAKE_SEMANTIC_VERSION(major, minor, patch) \ ((major << 24) | (minor << 16) | patch) diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index dd33e3344..cafe2b2b2 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -175,16 +175,6 @@ static inline void get_video_info(struct obs_encoder *encoder, obs_encoder_set_scaled_size(encoder, info->width, info->height); } -static inline bool has_scaling(const struct obs_encoder *encoder) -{ - uint32_t video_width = video_output_get_width(encoder->media); - uint32_t video_height = video_output_get_height(encoder->media); - - return encoder->scaled_width && encoder->scaled_height && - (video_width != encoder->scaled_width || - video_height != encoder->scaled_height); -} - static inline bool gpu_encode_available(const struct obs_encoder *encoder) { struct obs_core_video_mix *video = get_mix_for_video(encoder->media); @@ -453,6 +443,13 @@ static THREAD_LOCAL bool can_reroute = false; static inline bool obs_encoder_initialize_internal(obs_encoder_t *encoder) { + if (!encoder->media) { + blog(LOG_ERROR, + "obs_encoder_initialize_internal: encoder '%s' has no media set", + encoder->context.name); + return false; + } + if (encoder_active(encoder)) return true; if (encoder->initialized) @@ -562,7 +559,7 @@ static inline void obs_encoder_start_internal( struct encoder_callback cb = {false, new_packet, param}; bool first = false; - if (!encoder->context.data) + if (!encoder->context.data || !encoder->media) return; pthread_mutex_lock(&encoder->callbacks_mutex); @@ -792,14 +789,24 @@ void obs_encoder_set_video(obs_encoder_t *encoder, video_t *video) obs_encoder_get_name(encoder)); return; } - if (!video) + if (encoder_active(encoder)) { + blog(LOG_WARNING, + "encoder '%s': Cannot apply a new video_t " + "object while the encoder is active", + obs_encoder_get_name(encoder)); return; + } - voi = video_output_get_info(video); - - encoder->media = video; - encoder->timebase_num = voi->fps_den; - encoder->timebase_den = voi->fps_num; + if (video) { + voi = video_output_get_info(video); + encoder->media = video; + encoder->timebase_num = voi->fps_den; + encoder->timebase_den = voi->fps_num; + } else { + encoder->media = NULL; + encoder->timebase_num = 0; + encoder->timebase_den = 0; + } } void obs_encoder_set_audio(obs_encoder_t *encoder, audio_t *audio) @@ -813,12 +820,23 @@ void obs_encoder_set_audio(obs_encoder_t *encoder, audio_t *audio) obs_encoder_get_name(encoder)); return; } - if (!audio) + if (encoder_active(encoder)) { + blog(LOG_WARNING, + "encoder '%s': Cannot apply a new audio_t " + "object while the encoder is active", + obs_encoder_get_name(encoder)); return; + } - encoder->media = audio; - encoder->timebase_num = 1; - encoder->timebase_den = audio_output_get_sample_rate(audio); + if (audio) { + encoder->media = audio; + encoder->timebase_num = 1; + encoder->timebase_den = audio_output_get_sample_rate(audio); + } else { + encoder->media = NULL; + encoder->timebase_num = 0; + encoder->timebase_den = 0; + } } video_t *obs_encoder_video(const obs_encoder_t *encoder) @@ -1316,7 +1334,7 @@ end: void obs_encoder_add_output(struct obs_encoder *encoder, struct obs_output *output) { - if (!encoder) + if (!encoder || !output) return; pthread_mutex_lock(&encoder->outputs_mutex); @@ -1327,7 +1345,7 @@ void obs_encoder_add_output(struct obs_encoder *encoder, void obs_encoder_remove_output(struct obs_encoder *encoder, struct obs_output *output) { - if (!encoder) + if (!encoder || !output) return; pthread_mutex_lock(&encoder->outputs_mutex); diff --git a/libobs/obs-nix-x11.c b/libobs/obs-nix-x11.c index ce571b977..a60048a5f 100644 --- a/libobs/obs-nix-x11.c +++ b/libobs/obs-nix-x11.c @@ -878,6 +878,7 @@ static void obs_nix_x11_hotkeys_platform_free(struct obs_core_hotkeys *hotkeys) da_free(context->keycodes[i].list); bfree(context->keysyms); + XCloseDisplay(context->display); bfree(context); hotkeys->platform_context = NULL; diff --git a/libobs/obs-output.c b/libobs/obs-output.c index 3253a3b25..9c096726c 100644 --- a/libobs/obs-output.c +++ b/libobs/obs-output.c @@ -2417,7 +2417,7 @@ static void output_reconnect(struct obs_output *output) blog(LOG_WARNING, "Failed to create reconnect thread"); os_atomic_set_bool(&output->reconnecting, false); } else { - blog(LOG_INFO, "Output '%s': Reconnecting in %.02f seconds..", + blog(LOG_INFO, "Output '%s': Reconnecting in %.02f seconds..", output->context.name, (float)(output->reconnect_retry_cur_msec / 1000.0)); diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index f07e38c97..fd787a720 100644 --- a/libobs/obs-scene.c +++ b/libobs/obs-scene.c @@ -1103,6 +1103,12 @@ static void scene_load(void *data, obs_data_t *settings) remove_all_items(scene); + if (obs_data_get_bool(settings, "custom_size")) { + scene->cx = (uint32_t)obs_data_get_int(settings, "cx"); + scene->cy = (uint32_t)obs_data_get_int(settings, "cy"); + scene->custom_size = true; + } + if (!items) return; @@ -1117,12 +1123,6 @@ static void scene_load(void *data, obs_data_t *settings) if (obs_data_has_user_value(settings, "id_counter")) scene->id_counter = obs_data_get_int(settings, "id_counter"); - if (obs_data_get_bool(settings, "custom_size")) { - scene->cx = (uint32_t)obs_data_get_int(settings, "cx"); - scene->cy = (uint32_t)obs_data_get_int(settings, "cy"); - scene->custom_size = true; - } - obs_data_array_release(items); } diff --git a/libobs/obs-source-transition.c b/libobs/obs-source-transition.c index 45869bc47..9c50f4a99 100644 --- a/libobs/obs-source-transition.c +++ b/libobs/obs-source-transition.c @@ -439,7 +439,7 @@ void obs_transition_set(obs_source_t *transition, obs_source_t *source) obs_source_t *s[2]; bool active[2]; - if (!transition_valid(transition, "obs_transition_clear")) + if (!transition_valid(transition, "obs_transition_set")) return; source = obs_source_get_ref(source); @@ -893,6 +893,9 @@ bool obs_transition_video_render_direct(obs_source_t *transition, transition->transitioning_video = false; video_stopped = true; + if (!obs_source_active(transition)) + transition->transitioning_audio = false; + if (!transition->transitioning_audio) { obs_transition_stop(transition); stopped = true; diff --git a/libobs/obs-source.c b/libobs/obs-source.c index 3fe724bf8..3657b2349 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -2478,6 +2478,8 @@ static inline void obs_source_render_async_video(obs_source_t *source) bool nonlinear_alpha = false; switch (source_space) { case GS_CS_SRGB: + linear_srgb = linear_srgb || + (current_space != GS_CS_SRGB); nonlinear_alpha = linear_srgb && !source->async_linear_alpha; switch (current_space) { @@ -2492,7 +2494,6 @@ static inline void obs_source_render_async_video(obs_source_t *source) nonlinear_alpha ? "DrawNonlinearAlphaMultiply" : "DrawMultiply"; - linear_srgb = true; multiplier = obs_get_video_sdr_white_level() / 80.0f; } diff --git a/libobs/obs.c b/libobs/obs.c index 291e25682..2d284ea30 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -774,13 +774,16 @@ void obs_free_video_mix(struct obs_core_video_mix *video) static void obs_free_video(void) { pthread_mutex_lock(&obs->video.mixes_mutex); - size_t num = obs->video.mixes.num; - if (num) - blog(LOG_WARNING, "%zu views remain at shutdown", num); - for (size_t i = 0; i < num; i++) { - obs_free_video_mix(obs->video.mixes.array[i]); + size_t num_views = 0; + for (size_t i = 0; i < obs->video.mixes.num; i++) { + struct obs_core_video_mix *video = obs->video.mixes.array[i]; + if (video && video->view) + num_views++; + obs_free_video_mix(video); obs->video.mixes.array[i] = NULL; } + if (num_views > 0) + blog(LOG_WARNING, "Number of remaining views: %ld", num_views); pthread_mutex_unlock(&obs->video.mixes_mutex); pthread_mutex_destroy(&obs->video.mixes_mutex); diff --git a/libobs/util/text-lookup.c b/libobs/util/text-lookup.c index 433aebd7f..6fc144f78 100644 --- a/libobs/util/text-lookup.c +++ b/libobs/util/text-lookup.c @@ -14,6 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include #include "dstr.h" #include "text-lookup.h" #include "lexer.h" @@ -159,11 +160,6 @@ static void lookup_addstring(const char *lookup_val, struct text_leaf *leaf, char val1 = child->str.array[len], val2 = lookup_val[len]; - if (val1 >= 'A' && val1 <= 'Z') - val1 += 0x20; - if (val2 >= 'A' && val2 <= 'Z') - val2 += 0x20; - if (val1 != val2) break; } @@ -323,6 +319,9 @@ static void lookup_addfiledata(struct text_lookup *lookup, leaf->lookup = bstrdup_n(name.array, name.len); leaf->value = convert_string(value.array, value.len); + for (size_t i = 0; i < name.len; i++) + leaf->lookup[i] = toupper(leaf->lookup[i]); + lookup_addstring(leaf->lookup, leaf, lookup->top); if (!lookup_goto_nextline(&lex)) diff --git a/plugins/mac-virtualcam/src/dal-plugin/OBSDALMachClient.mm b/plugins/mac-virtualcam/src/dal-plugin/OBSDALMachClient.mm index 9514eee9f..303a3a4dc 100644 --- a/plugins/mac-virtualcam/src/dal-plugin/OBSDALMachClient.mm +++ b/plugins/mac-virtualcam/src/dal-plugin/OBSDALMachClient.mm @@ -93,6 +93,8 @@ - (void)handlePortMessage:(NSPortMessage *)message { + __strong id strongDelegate = self.delegate; + VLogFunc(@""); NSArray *components = message.components; switch (message.msgid) { @@ -113,57 +115,60 @@ IOSurfaceRef surface = IOSurfaceLookupFromMachPort( [framePort machPort]); + + if (surface) { + /* + * IOSurfaceLocks are only necessary on non Apple Silicon devices, as those have + * unified memory. On Intel machines, the lock ensures that the IOSurface is copied back + * from GPU memory to CPU memory so we can process the pixel buffer. + */ +#ifndef __aarch64__ + IOSurfaceLock(surface, kIOSurfaceLockReadOnly, + NULL); +#endif + CVPixelBufferRef frame; + CVPixelBufferCreateWithIOSurface( + kCFAllocatorDefault, surface, NULL, + &frame); +#ifndef __aarch64__ + IOSurfaceUnlock(surface, kIOSurfaceLockReadOnly, + NULL); +#endif + CFRelease(surface); + + uint64_t timestamp; + [components[1] getBytes:×tamp + length:sizeof(timestamp)]; + + VLog(@"Received frame data: %zux%zu (%llu)", + CVPixelBufferGetWidth(frame), + CVPixelBufferGetHeight(frame), timestamp); + + uint32_t fpsNumerator; + [components[2] getBytes:&fpsNumerator + length:sizeof(fpsNumerator)]; + uint32_t fpsDenominator; + [components[3] getBytes:&fpsDenominator + length:sizeof(fpsDenominator)]; + + [strongDelegate + receivedPixelBuffer:frame + timestamp:timestamp + fpsNumerator:fpsNumerator + fpsDenominator:fpsDenominator]; + + CVPixelBufferRelease(frame); + } else { + ELog(@"Failed to obtain IOSurface from Mach port"); + } [framePort invalidate]; mach_port_deallocate(mach_task_self(), [framePort machPort]); - - if (!surface) { - ELog(@"Failed to obtain IOSurface from Mach port"); - return; - } - - /* - * IOSurfaceLocks are only necessary on non Apple Silicon devices, as those have - * unified memory. On Intel machines, the lock ensures that the IOSurface is copied back - * from GPU memory to CPU memory so we can process the pixel buffer. - */ -#ifndef __aarch64__ - IOSurfaceLock(surface, kIOSurfaceLockReadOnly, NULL); -#endif - CVPixelBufferRef frame; - CVPixelBufferCreateWithIOSurface(kCFAllocatorDefault, - surface, NULL, &frame); -#ifndef __aarch64__ - IOSurfaceUnlock(surface, kIOSurfaceLockReadOnly, NULL); -#endif - CFRelease(surface); - - uint64_t timestamp; - [components[1] getBytes:×tamp - length:sizeof(timestamp)]; - - VLog(@"Received frame data: %zux%zu (%llu)", - CVPixelBufferGetWidth(frame), - CVPixelBufferGetHeight(frame), timestamp); - - uint32_t fpsNumerator; - [components[2] getBytes:&fpsNumerator - length:sizeof(fpsNumerator)]; - uint32_t fpsDenominator; - [components[3] getBytes:&fpsDenominator - length:sizeof(fpsDenominator)]; - - [self.delegate receivedPixelBuffer:frame - timestamp:timestamp - fpsNumerator:fpsNumerator - fpsDenominator:fpsDenominator]; - - CVPixelBufferRelease(frame); } break; case MachMsgIdStop: DLog(@"Received stop message"); - [self.delegate receivedStop]; + [strongDelegate receivedStop]; break; default: ELog(@"Received unexpected response msgid %u", diff --git a/plugins/mac-virtualcam/src/obs-plugin/OBSDALMachServer.mm b/plugins/mac-virtualcam/src/obs-plugin/OBSDALMachServer.mm index efd33c1b4..450e99466 100644 --- a/plugins/mac-virtualcam/src/obs-plugin/OBSDALMachServer.mm +++ b/plugins/mac-virtualcam/src/obs-plugin/OBSDALMachServer.mm @@ -148,6 +148,9 @@ length:sizeof(fpsDenominator)]; IOSurfaceRef surface = CVPixelBufferGetIOSurface(frame); +#ifndef __aarch64__ + IOSurfaceLock(surface, 0, NULL); +#endif if (!surface) { blog(LOG_ERROR, @@ -174,6 +177,10 @@ ]]; mach_port_deallocate(mach_task_self(), framePort); + +#ifndef __aarch64__ + IOSurfaceUnlock(surface, 0, NULL); +#endif } } diff --git a/plugins/obs-ffmpeg/CMakeLists.txt b/plugins/obs-ffmpeg/CMakeLists.txt index 9671ef978..244c6df92 100644 --- a/plugins/obs-ffmpeg/CMakeLists.txt +++ b/plugins/obs-ffmpeg/CMakeLists.txt @@ -123,7 +123,7 @@ elseif(OS_POSIX AND NOT OS_MACOS) find_package(Libpci REQUIRED) target_sources(obs-ffmpeg PRIVATE obs-ffmpeg-vaapi.c vaapi-utils.c vaapi-utils.h) - target_link_libraries(obs-ffmpeg PRIVATE Libva::va LIBPCI::LIBPCI) + target_link_libraries(obs-ffmpeg PRIVATE Libva::va Libva::drm LIBPCI::LIBPCI) endif() setup_plugin_target(obs-ffmpeg) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-output.c b/plugins/obs-ffmpeg/obs-ffmpeg-output.c index 10591e3a9..9f3a807d0 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-output.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-output.c @@ -241,8 +241,10 @@ static bool create_video_stream(struct ffmpeg_data *data) closest_format = data->config.format; if (data->vcodec->pix_fmts) { + const int has_alpha = closest_format == AV_PIX_FMT_BGRA; closest_format = avcodec_find_best_pix_fmt_of_list( - data->vcodec->pix_fmts, data->config.format, 0, NULL); + data->vcodec->pix_fmts, closest_format, has_alpha, + NULL); } #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101) @@ -640,9 +642,7 @@ bool ffmpeg_data_init(struct ffmpeg_data *data, struct ffmpeg_cfg *config) } #else if (is_rtmp) { - data->config.audio_encoder = "aac"; data->config.audio_encoder_id = AV_CODEC_ID_AAC; - data->config.video_encoder = "libx264"; data->config.video_encoder_id = AV_CODEC_ID_H264; } #endif diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-srt.h b/plugins/obs-ffmpeg/obs-ffmpeg-srt.h index 2046d56c6..66a8d83ad 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-srt.h +++ b/plugins/obs-ffmpeg/obs-ffmpeg-srt.h @@ -485,7 +485,13 @@ static int libsrt_setup(URLContext *h, const char *uri) if (ret) { blog(LOG_ERROR, "[obs-ffmpeg mpegts muxer / libsrt]: Failed to resolve hostname %s: %s", - hostname, gai_strerror(ret)); + hostname, +#ifdef _WIN32 + gai_strerrorA(ret) +#else + gai_strerror(ret) +#endif + ); return OBS_OUTPUT_CONNECT_FAILED; } diff --git a/plugins/obs-filters/data/locale/en-US.ini b/plugins/obs-filters/data/locale/en-US.ini index 99fb894ca..a3def5ff5 100644 --- a/plugins/obs-filters/data/locale/en-US.ini +++ b/plugins/obs-filters/data/locale/en-US.ini @@ -115,6 +115,7 @@ Expander.None="None" Expander.Presets="Presets" Expander.Presets.Expander="Expander" Expander.Presets.Gate="Gate" +Expander.Knee.Width="Knee Width" LumaKeyFilter="Luma Key" Luma.LumaMax="Luma Max" Luma.LumaMin="Luma Min" diff --git a/plugins/obs-filters/expander-filter.c b/plugins/obs-filters/expander-filter.c index 157cf5640..e162d4426 100644 --- a/plugins/obs-filters/expander-filter.c +++ b/plugins/obs-filters/expander-filter.c @@ -34,6 +34,7 @@ #define S_OUTPUT_GAIN "output_gain" #define S_DETECTOR "detector" #define S_PRESETS "presets" +#define S_KNEE "knee_width" #define MT_ obs_module_text #define TEXT_RATIO MT_("expander.Ratio") @@ -48,6 +49,7 @@ #define TEXT_PRESETS MT_("expander.Presets") #define TEXT_PRESETS_EXP MT_("expander.Presets.Expander") #define TEXT_PRESETS_GATE MT_("expander.Presets.Gate") +#define TEXT_KNEE MT_("expander.Knee.Width") #define MIN_RATIO 1.0f #define MAX_RATIO 20.0f @@ -95,6 +97,7 @@ struct expander_data { float *env_in; size_t env_in_len; bool is_upwcomp; + float knee; }; enum { @@ -179,6 +182,7 @@ static void upward_compressor_defaults(obs_data_t *s) obs_data_set_default_int(s, S_RELEASE_TIME, 50); obs_data_set_default_double(s, S_OUTPUT_GAIN, 0.0); obs_data_set_default_string(s, S_DETECTOR, "RMS"); + obs_data_set_default_int(s, S_KNEE, 10); } static void expander_update(void *data, obs_data_t *s) @@ -208,6 +212,8 @@ static void expander_update(void *data, obs_data_t *s) (float)obs_data_get_int(s, S_RELEASE_TIME); const float output_gain_db = (float)obs_data_get_double(s, S_OUTPUT_GAIN); + const float knee = cd->is_upwcomp ? (float)obs_data_get_int(s, S_KNEE) + : 0.0f; cd->ratio = (float)obs_data_get_double(s, S_RATIO); @@ -220,6 +226,7 @@ static void expander_update(void *data, obs_data_t *s) cd->num_channels = num_channels; cd->sample_rate = sample_rate; cd->slope = 1.0f - cd->ratio; + cd->knee = knee; const char *detect_mode = obs_data_get_string(s, S_DETECTOR); if (strcmp(detect_mode, "RMS") == 0) @@ -343,7 +350,8 @@ static inline void process_sample(size_t idx, float *samples, float *env_buf, float channel_gain, float threshold, float slope, float attack_gain, float inv_attack_gain, float release_gain, - float inv_release_gain, float output_gain) + float inv_release_gain, float output_gain, + float knee) { /* --------------------------------- */ /* gain stage of expansion */ @@ -351,42 +359,48 @@ static inline void process_sample(size_t idx, float *samples, float *env_buf, float env_db = mul_to_db(env_buf[idx]); float diff = threshold - env_db; - if (is_upwcomp && env_db <= -60.0f) - diff = 0.0f; + if (is_upwcomp && env_db <= (threshold - 60.0f) / 2) + diff = env_db + 60.0f > 0 ? env_db + 60.0f : 0.0f; - float gain = diff > 0.0f ? fmaxf(slope * diff, -60.0f) : 0.0f; - float prev_gain = gain_db[idx - 1]; + float gain = 0.0f; + float prev_gain = 0.0f; + // Note that the gain is always >= 0 for the upward compressor + // but is always <=0 for the expander. + if (is_upwcomp) { + prev_gain = idx > 0 ? fmaxf(gain_db[idx - 1], 0) + : fmaxf(channel_gain, 0); + // gain above knee (included for clarity): + if (env_db >= threshold + knee / 2) + gain = 0.0f; + // gain below knee: + if (threshold - knee / 2 >= env_db) + gain = slope * diff; + // gain in knee: + if (env_db > threshold - knee / 2 && + threshold + knee / 2 > env_db) + gain = slope * powf(diff + knee / 2, 2) / (2.0f * knee); + } else { + prev_gain = idx > 0 ? gain_db[idx - 1] : channel_gain; + gain = diff > 0.0f ? fmaxf(slope * diff, -60.0f) : 0.0f; + } /* --------------------------------- */ /* ballistics (attack/release) */ - if (idx > 0) { - if (gain > prev_gain) - gain_db[idx] = attack_gain * prev_gain + - inv_attack_gain * gain; - else - gain_db[idx] = release_gain * prev_gain + - inv_release_gain * gain; - } else { - if (gain > channel_gain) - gain_db[idx] = attack_gain * channel_gain + - inv_attack_gain * gain; - else - gain_db[idx] = release_gain * channel_gain + - inv_release_gain * gain; - } + if (gain > prev_gain) + gain_db[idx] = attack_gain * prev_gain + inv_attack_gain * gain; + else + gain_db[idx] = + release_gain * prev_gain + inv_release_gain * gain; /* --------------------------------- */ /* output */ - float min_val = - is_upwcomp ? fminf(diff, threshold - mul_to_db(samples[idx])) - : 0.0f; - gain = db_to_mul(fminf(min_val, gain_db[idx])); - - // above threshold, don't process expander nor upward compressor - if (threshold - env_db <= 0) - gain = 1.0f; + if (!is_upwcomp) { + gain = db_to_mul(fminf(0, gain_db[idx])); + } else { + gain = db_to_mul(gain_db[idx]); + } samples[idx] *= gain * output_gain; } @@ -403,6 +417,7 @@ static inline void process_expansion(struct expander_data *cd, float **samples, const float slope = cd->slope; const float output_gain = cd->output_gain; const bool is_upwcomp = cd->is_upwcomp; + const float knee = cd->knee; if (cd->gain_db_len < num_samples) resize_gain_db_buffer(cd, num_samples); @@ -422,7 +437,7 @@ static inline void process_expansion(struct expander_data *cd, float **samples, is_upwcomp, channel_gain, threshold, slope, attack_gain, inv_attack_gain, release_gain, inv_release_gain, - output_gain); + output_gain, knee); } cd->gain_db_buf[chan] = gain_db[num_samples - 1]; } @@ -487,6 +502,10 @@ static obs_properties_t *expander_properties(void *data) OBS_COMBO_FORMAT_STRING); obs_property_list_add_string(detect, TEXT_RMS, "RMS"); obs_property_list_add_string(detect, TEXT_PEAK, "peak"); + } else { + p = obs_properties_add_int_slider(props, S_KNEE, TEXT_KNEE, 0, + 20, 1); + obs_property_float_set_suffix(p, " dB"); } return props; } diff --git a/plugins/obs-filters/noise-suppress-filter.c b/plugins/obs-filters/noise-suppress-filter.c index d019a2272..2b042bf2b 100644 --- a/plugins/obs-filters/noise-suppress-filter.c +++ b/plugins/obs-filters/noise-suppress-filter.c @@ -634,7 +634,7 @@ bool load_nvafx(void) blog(LOG_INFO, "[noise suppress]: NVIDIA AUDIO FX version: %i.%i.%i.%i", major, minor, build, revision); - if (version < (MIN_AFX_SDK_VERSION)) { + if (version < MIN_AFX_SDK_VERSION) { blog(LOG_INFO, "[noise suppress]: NVIDIA AUDIO Effects SDK is outdated. Please update both audio & video SDK."); } @@ -1227,7 +1227,7 @@ static obs_properties_t *noise_suppress_properties(void *data) 1.0f, 0.01f); } unsigned int version = get_lib_version(); - if (version < (MIN_AFX_SDK_VERSION)) { + if (version && version < MIN_AFX_SDK_VERSION) { obs_property_t *warning = obs_properties_add_text( ppts, "deprecation", NULL, OBS_TEXT_INFO); obs_property_text_set_info_type(warning, OBS_TEXT_INFO_WARNING); diff --git a/plugins/obs-filters/nvafx-load.h b/plugins/obs-filters/nvafx-load.h index 7fc94c007..0b02c426e 100644 --- a/plugins/obs-filters/nvafx-load.h +++ b/plugins/obs-filters/nvafx-load.h @@ -9,7 +9,7 @@ #define NVAFX_API #ifdef LIBNVAFX_ENABLED -#define MIN_AFX_SDK_VERSION 1 << 24 | 2 << 16 | 13 << 0 +#define MIN_AFX_SDK_VERSION (1 << 24 | 2 << 16 | 13 << 0) static HMODULE nv_audiofx = NULL; static HMODULE nv_cuda = NULL; @@ -312,15 +312,26 @@ static bool load_lib(void) static unsigned int get_lib_version(void) { + static unsigned int version = 0; + static bool version_checked = false; + + if (version_checked) + return version; + + version_checked = true; + char path[MAX_PATH]; if (!nvafx_get_sdk_path(path, sizeof(path))) return 0; SetDllDirectoryA(path); + struct win_version_info nto_ver = {0}; - get_dll_ver(L"NVAudioEffects.dll", &nto_ver); - unsigned int version = nto_ver.major << 24 | nto_ver.minor << 16 | - nto_ver.build << 8 | nto_ver.revis << 0; + if (get_dll_ver(L"NVAudioEffects.dll", &nto_ver)) + version = nto_ver.major << 24 | nto_ver.minor << 16 | + nto_ver.build << 8 | nto_ver.revis << 0; + + SetDllDirectoryA(NULL); return version; } #endif diff --git a/plugins/obs-filters/nvidia-greenscreen-filter.c b/plugins/obs-filters/nvidia-greenscreen-filter.c index dc51d6997..aadc957f5 100644 --- a/plugins/obs-filters/nvidia-greenscreen-filter.c +++ b/plugins/obs-filters/nvidia-greenscreen-filter.c @@ -256,14 +256,14 @@ static void init_images_greenscreen(struct nv_greenscreen_data *filter) filter->render = gs_texrender_create( gs_get_format_from_space(filter->space), GS_ZS_NONE); if (!filter->render) { - error("Failed to create render texrenderer", vfxErr); + error("Failed to create render texrenderer"); goto fail; } if (filter->render_unorm) gs_texrender_destroy(filter->render_unorm); filter->render_unorm = gs_texrender_create(GS_BGRA_UNORM, GS_ZS_NONE); if (!filter->render_unorm) { - error("Failed to create render_unorm texrenderer", vfxErr); + error("Failed to create render_unorm texrenderer"); goto fail; } @@ -479,7 +479,7 @@ static void *nv_greenscreen_filter_create(obs_data_t *settings, uint8_t build = (filter->version >> 8) & 0x0000ff; uint8_t revision = (filter->version >> 0) & 0x000000ff; // sanity check - nvvfx_new_sdk = filter->version >= (MIN_VFX_SDK_VERSION) && + nvvfx_new_sdk = filter->version >= MIN_VFX_SDK_VERSION && nvvfx_new_sdk; } @@ -551,7 +551,7 @@ static obs_properties_t *nv_greenscreen_filter_properties(void *data) props, S_PROCESSING, TEXT_PROCESSING, 1, 4, 1); obs_property_set_long_description(partial, TEXT_PROCESSING_HINT); unsigned int version = get_lib_version(); - if (version < (MIN_VFX_SDK_VERSION)) { + if (version && version < MIN_VFX_SDK_VERSION) { obs_property_t *warning = obs_properties_add_text( props, "deprecation", NULL, OBS_TEXT_INFO); obs_property_text_set_info_type(warning, OBS_TEXT_INFO_WARNING); @@ -896,7 +896,7 @@ bool load_nvvfx(void) blog(LOG_INFO, "[NVIDIA VIDEO FX]: NVIDIA VIDEO FX version: %i.%i.%i.%i", major, minor, build, revision); - if (version < (MIN_VFX_SDK_VERSION)) { + if (version < MIN_VFX_SDK_VERSION) { blog(LOG_INFO, "[NVIDIA VIDEO FX]: NVIDIA VIDEO Effects SDK is outdated. Please update both audio & video SDK."); } diff --git a/plugins/obs-filters/nvvfx-load.h b/plugins/obs-filters/nvvfx-load.h index 4feb5afc6..cd1df985b 100644 --- a/plugins/obs-filters/nvvfx-load.h +++ b/plugins/obs-filters/nvvfx-load.h @@ -39,7 +39,7 @@ extern "C" { #define CUDARTAPI #ifdef LIBNVVFX_ENABLED -#define MIN_VFX_SDK_VERSION 0 << 24 | 7 << 16 | 1 << 8 | 0 << 0 +#define MIN_VFX_SDK_VERSION (0 << 24 | 7 << 16 | 1 << 8 | 0 << 0) static HMODULE nv_videofx = NULL; static HMODULE nv_cvimage = NULL; static HMODULE nv_cudart = NULL; @@ -817,14 +817,25 @@ static inline bool load_nv_vfx_libs() static unsigned int get_lib_version(void) { + static unsigned int version = 0; + static bool version_checked = false; + + if (version_checked) + return version; + + version_checked = true; + char path[MAX_PATH]; nvvfx_get_sdk_path(path, sizeof(path)); SetDllDirectoryA(path); + struct win_version_info nto_ver = {0}; - get_dll_ver(L"NVVideoEffects.dll", &nto_ver); - unsigned int version = nto_ver.major << 24 | nto_ver.minor << 16 | - nto_ver.build << 8 | nto_ver.revis << 0; + if (get_dll_ver(L"NVVideoEffects.dll", &nto_ver)) + version = nto_ver.major << 24 | nto_ver.minor << 16 | + nto_ver.build << 8 | nto_ver.revis << 0; + + SetDllDirectoryA(NULL); return version; } #endif diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index d4b5cfee4..53ea4db42 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,11 +1,11 @@ { "$schema": "schema/package-schema.json", "url": "https://obsproject.com/obs2_update/rtmp-services/v4", - "version": 216, + "version": 217, "files": [ { "name": "services.json", - "version": 216 + "version": 217 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index 7022f9cbf..3801c0a10 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -2381,8 +2381,8 @@ "stream_key_link": "https://www.mildom.com/creator/live", "servers": [ { - "name": "Asia: Tokyo, Japan", - "url": "rtmp://live-tyo-tct.mildom.tv/live" + "name": "Global", + "url": "rtmp://txlvb-push.mildom.tv/live" } ], "recommended": { diff --git a/plugins/win-capture/duplicator-monitor-capture.c b/plugins/win-capture/duplicator-monitor-capture.c index a1c9ccec8..02e2c9bb0 100644 --- a/plugins/win-capture/duplicator-monitor-capture.c +++ b/plugins/win-capture/duplicator-monitor-capture.c @@ -92,7 +92,7 @@ struct duplicator_capture { struct winrt_capture *capture_winrt; }; -struct wgc_monitor_info { +struct duplicator_monitor_info { char device_id[128]; char name[64]; RECT rect; @@ -187,7 +187,8 @@ static BOOL CALLBACK enum_monitor(HMONITOR handle, HDC hdc, LPRECT rect, { UNUSED_PARAMETER(hdc); - struct wgc_monitor_info *monitor = (struct wgc_monitor_info *)param; + struct duplicator_monitor_info *monitor = + (struct duplicator_monitor_info *)param; bool match = false; @@ -198,8 +199,8 @@ static BOOL CALLBACK enum_monitor(HMONITOR handle, HDC hdc, LPRECT rect, device.cb = sizeof(device); if (EnumDisplayDevicesA(mi.szDevice, 0, &device, EDD_GET_DEVICE_INTERFACE_NAME)) { - const bool match = strcmp(monitor->device_id, - device.DeviceID) == 0; + match = strcmp(monitor->device_id, device.DeviceID) == + 0; if (match) { monitor->rect = *rect; monitor->handle = handle; @@ -212,6 +213,31 @@ static BOOL CALLBACK enum_monitor(HMONITOR handle, HDC hdc, LPRECT rect, return !match; } +static BOOL CALLBACK enum_monitor_fallback(HMONITOR handle, HDC hdc, + LPRECT rect, LPARAM param) +{ + UNUSED_PARAMETER(hdc); + + struct duplicator_monitor_info *monitor = + (struct duplicator_monitor_info *)param; + + bool match = false; + + MONITORINFOEXA mi; + mi.cbSize = sizeof(mi); + if (GetMonitorInfoA(handle, (LPMONITORINFO)&mi)) { + match = strcmp(monitor->device_id, mi.szDevice) == 0; + if (match) { + monitor->rect = *rect; + monitor->handle = handle; + GetMonitorName(handle, monitor->name, + _countof(monitor->name)); + } + } + + return !match; +} + static void log_settings(struct duplicator_capture *capture, const char *monitor, LONG width, LONG height) { @@ -257,15 +283,26 @@ choose_method(enum display_capture_method method, bool wgc_supported, extern bool wgc_supported; +static struct duplicator_monitor_info find_monitor(const char *monitor_id) +{ + struct duplicator_monitor_info monitor = {0}; + strcpy_s(monitor.device_id, _countof(monitor.device_id), monitor_id); + EnumDisplayMonitors(NULL, NULL, &enum_monitor, (LPARAM)&monitor); + if (monitor.handle == NULL) { + EnumDisplayMonitors(NULL, NULL, &enum_monitor_fallback, + (LPARAM)&monitor); + } + + return monitor; +} + static inline void update_settings(struct duplicator_capture *capture, obs_data_t *settings) { pthread_mutex_lock(&capture->update_mutex); - struct wgc_monitor_info monitor = {0}; - strcpy_s(monitor.device_id, _countof(monitor.device_id), - obs_data_get_string(settings, "monitor_id")); - EnumDisplayMonitors(NULL, NULL, enum_monitor, (LPARAM)&monitor); + struct duplicator_monitor_info monitor = + find_monitor(obs_data_get_string(settings, "monitor_id")); capture->method = choose_method((int)obs_data_get_int(settings, "method"), @@ -463,11 +500,7 @@ static void free_capture_data(struct duplicator_capture *capture) static void update_monitor_handle(struct duplicator_capture *capture) { - struct wgc_monitor_info monitor = {0}; - strcpy_s(monitor.device_id, _countof(monitor.device_id), - capture->monitor_id); - EnumDisplayMonitors(NULL, NULL, enum_monitor, (LPARAM)&monitor); - capture->handle = monitor.handle; + capture->handle = find_monitor(capture->monitor_id).handle; } static void duplicator_capture_tick(void *data, float seconds) @@ -727,40 +760,41 @@ static BOOL CALLBACK enum_monitor_props(HMONITOR handle, HDC hdc, LPRECT rect, UNUSED_PARAMETER(hdc); UNUSED_PARAMETER(rect); + char monitor_name[64]; + GetMonitorName(handle, monitor_name, sizeof(monitor_name)); + MONITORINFOEXA mi; mi.cbSize = sizeof(mi); if (GetMonitorInfoA(handle, (LPMONITORINFO)&mi)) { + obs_property_t *monitor_list = (obs_property_t *)param; + struct dstr monitor_desc = {0}; + dstr_printf(&monitor_desc, "%s: %dx%d @ %d,%d", monitor_name, + mi.rcMonitor.right - mi.rcMonitor.left, + mi.rcMonitor.bottom - mi.rcMonitor.top, + mi.rcMonitor.left, mi.rcMonitor.top); + if (mi.dwFlags == MONITORINFOF_PRIMARY) + dstr_catf(&monitor_desc, " (%s)", TEXT_PRIMARY_MONITOR); + DISPLAY_DEVICEA device; device.cb = sizeof(device); if (EnumDisplayDevicesA(mi.szDevice, 0, &device, EDD_GET_DEVICE_INTERFACE_NAME)) { - obs_property_t *monitor_list = (obs_property_t *)param; - struct dstr monitor_desc = {0}; - struct dstr resolution = {0}; - - dstr_catf(&resolution, "%dx%d @ %d,%d", - mi.rcMonitor.right - mi.rcMonitor.left, - mi.rcMonitor.bottom - mi.rcMonitor.top, - mi.rcMonitor.left, mi.rcMonitor.top); - - char monitor_name[64]; - GetMonitorName(handle, monitor_name, - sizeof(monitor_name)); - dstr_catf(&monitor_desc, "%s: %s", monitor_name, - resolution.array); - - if (mi.dwFlags == MONITORINFOF_PRIMARY) { - dstr_catf(&monitor_desc, " (%s)", - TEXT_PRIMARY_MONITOR); - } - obs_property_list_add_string(monitor_list, monitor_desc.array, device.DeviceID); - - dstr_free(&monitor_desc); - dstr_free(&resolution); + } else { + blog(LOG_WARNING, + "[duplicator-monitor-capture] EnumDisplayDevices failed for monitor (%s), falling back to szDevice", + monitor_name); + obs_property_list_add_string( + monitor_list, monitor_desc.array, mi.szDevice); } + + dstr_free(&monitor_desc); + } else { + blog(LOG_WARNING, + "[duplicator-monitor-capture] GetMonitorInfo failed for monitor: %s", + monitor_name); } return TRUE; diff --git a/plugins/win-capture/window-capture.c b/plugins/win-capture/window-capture.c index 33a2dd06b..f548a6c22 100644 --- a/plugins/win-capture/window-capture.c +++ b/plugins/win-capture/window-capture.c @@ -401,7 +401,7 @@ static void update_settings_visibility(obs_properties_t *props, obs_property_set_visible(p, wgc_options); p = obs_properties_get(props, "force_sdr"); - obs_property_set_visible(p, wgc_cursor_toggle); + obs_property_set_visible(p, wgc_options); pthread_mutex_unlock(&wc->update_mutex); } diff --git a/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp b/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp index 5e432b7cb..9c36ac843 100644 --- a/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp +++ b/plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp @@ -270,6 +270,11 @@ void VCamFilter::Frame(uint64_t ts) the format we present to match */ SetVideoFormat(GetVideoFormat(), new_obs_cx, new_obs_cy, new_obs_interval); + + /* Update the new filter size immediately since we + know it just changed above */ + new_filter_cx = new_obs_cx; + new_filter_cy = new_obs_cy; } /* Re-initialize the main scaler to use the new resolution */