Compare commits
47 commits
master
...
release/29
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fb8bb4b1e | ||
|
|
eef0faf741 | ||
|
|
96aef4f048 | ||
|
|
414dccb2c3 | ||
|
|
ee7557e14c | ||
|
|
78085b38a8 | ||
|
|
3464772d3c | ||
|
|
5ee25058f8 | ||
|
|
d58efd64b1 | ||
|
|
6f8ee7abbe | ||
|
|
6f96ad48ed | ||
|
|
fbeede9349 | ||
|
|
0f4d0eed25 | ||
|
|
8c9de5e255 | ||
|
|
53f6c1a215 | ||
|
|
3c6aaeb142 | ||
|
|
c55ce029af | ||
|
|
d66b9bd8e9 | ||
|
|
ab69f5db05 | ||
|
|
45dd24ecb1 | ||
|
|
a0b12d8751 | ||
|
|
063cc0c865 | ||
|
|
43086717c4 | ||
|
|
80b16e63f5 | ||
|
|
f610bcff38 | ||
|
|
7fb8b7dd75 | ||
|
|
c24c137fa6 | ||
|
|
fb371a340b | ||
|
|
58f50a8bbe | ||
|
|
25fba67fe8 | ||
|
|
a25a4c20b4 | ||
|
|
aa1f7b275b | ||
|
|
2fcac0e363 | ||
|
|
7d579b5a07 | ||
|
|
9ead538009 | ||
|
|
0ce729ea78 | ||
|
|
00b4c9c88a | ||
|
|
8aa806adf8 | ||
|
|
d9a5aaafc9 | ||
|
|
a5808c9444 | ||
|
|
7d81905d4b | ||
|
|
0a7228f1da | ||
|
|
a166ca6b4f | ||
|
|
375da1a036 | ||
|
|
4e4a803b3b | ||
|
|
ab46d09f17 | ||
|
|
37dd362825 |
48 changed files with 549 additions and 244 deletions
53
.github/workflows/docs.yml
vendored
53
.github/workflows/docs.yml
vendored
|
|
@ -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 }}'
|
||||
|
|
|
|||
4
.github/workflows/main.yml
vendored
4
.github/workflows/main.yml
vendored
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -646,6 +646,9 @@
|
|||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetNoConstraint</enum>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cancelButton">
|
||||
<property name="text">
|
||||
|
|
|
|||
|
|
@ -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<Window> 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<Window> 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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<wchar_t> 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<wchar_t> 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<wchar_t> 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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
13
libobs/obs.c
13
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);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#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))
|
||||
|
|
|
|||
|
|
@ -93,6 +93,8 @@
|
|||
|
||||
- (void)handlePortMessage:(NSPortMessage *)message
|
||||
{
|
||||
__strong id<MachClientDelegate> 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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue