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 */