mirror of
https://github.com/obsproject/obs-studio.git
synced 2024-07-01 00:53:31 +00:00
Compare commits
98 commits
cd401a2131
...
46e47c07c9
Author | SHA1 | Date | |
---|---|---|---|
|
46e47c07c9 | ||
|
3572842641 | ||
|
1f477724ef | ||
|
0020df98f4 | ||
|
be48558755 | ||
|
0bd6e0ba8a | ||
|
85651a7d4b | ||
|
1407fb315c | ||
|
6cca6f88d9 | ||
|
21b47c4f91 | ||
|
4b1e16203f | ||
|
326d5f4e31 | ||
|
ab05714919 | ||
|
bef7de616e | ||
|
b0aac7e794 | ||
|
9d67bf2662 | ||
|
8892edde05 | ||
|
06291c7201 | ||
|
fb5bbc8575 | ||
|
7d19add10b | ||
|
4830d6903e | ||
|
47fb194223 | ||
|
edcda5a825 | ||
|
2e6e79b4f5 | ||
|
b34fbb116e | ||
|
4517918c7a | ||
|
6cc0e2b803 | ||
|
ed2478535f | ||
|
fd34bab615 | ||
|
5f98d34e2c | ||
|
6c389271b3 | ||
|
6457d7b429 | ||
|
92352f18b8 | ||
|
a189489dd2 | ||
|
aa096e2ad0 | ||
|
9d1ac8816e | ||
|
8a8019db3f | ||
|
a9b5968552 | ||
|
fc05ca601a | ||
|
32b53ea936 | ||
|
fb3e571ce8 | ||
|
48b1298faf | ||
|
608d3bfc26 | ||
|
e215502b62 | ||
|
751dbdad10 | ||
|
9d88b632ae | ||
|
4eef796f80 | ||
|
0d7478c017 | ||
|
d1bf6f951a | ||
|
62830cd8c7 | ||
|
dcd2f19c83 | ||
|
7cd5ede1e0 | ||
|
f462ffb224 | ||
|
600a564039 | ||
|
d7e2636316 | ||
|
d2a7f01295 | ||
|
2d489fc54e | ||
|
e4305b0a50 | ||
|
508f9c2e3c | ||
|
b16516a3fa | ||
|
40bf8b3c06 | ||
|
021adac2d0 | ||
|
b1643c2ac9 | ||
|
14d2c80560 | ||
|
ce4171908b | ||
|
77d31fa33f | ||
|
70307a5d25 | ||
|
0ea90380b4 | ||
|
8fcdfb815f | ||
|
718bd0b265 | ||
|
4a46d2d722 | ||
|
e454f488aa | ||
|
21adf0930f | ||
|
cfd692ca15 | ||
|
0f4e33c33e | ||
|
0cfae862cd | ||
|
6779052902 | ||
|
051c11e7b2 | ||
|
e774bd90b8 | ||
|
5bc1d31a98 | ||
|
d2b05a6e0c | ||
|
86502764b9 | ||
|
c677bac875 | ||
|
00c68981ab | ||
|
4e13cff8f1 | ||
|
c7dc09e862 | ||
|
a989fefa0b | ||
|
86c337d4e7 | ||
|
56d6fb4c62 | ||
|
4b187ed38c | ||
|
7d55942601 | ||
|
14fa71f749 | ||
|
0680b642e9 | ||
|
298e858f63 | ||
|
7a4cb085ba | ||
|
f9f4171d56 | ||
|
51e3bd5e3d | ||
|
30f174b8bb |
|
@ -9,7 +9,7 @@ env:
|
|||
|
||||
task:
|
||||
install_script:
|
||||
- pkg-install -y
|
||||
- pkg install -y
|
||||
cmake ninja binutils pkgconf curl
|
||||
ffmpeg qt6-base qt6-svg jansson libsysinfo e2fsprogs-libuuid pulseaudio
|
||||
alsa-lib pipewire v4l_compat libpci librist srt nlohmann-json uthash
|
||||
|
|
12
.github/actions/windows-patches/action.yaml
vendored
12
.github/actions/windows-patches/action.yaml
vendored
|
@ -25,6 +25,7 @@ runs:
|
|||
with:
|
||||
path: "repo"
|
||||
fetch-depth: 0
|
||||
ref: ${{ inputs.tagName }}
|
||||
|
||||
- name: Download Release Artifact
|
||||
shell: pwsh
|
||||
|
@ -40,9 +41,9 @@ runs:
|
|||
- name: Setup bouf
|
||||
shell: pwsh
|
||||
env:
|
||||
BOUF_TAG: 'v0.6.3'
|
||||
BOUF_HASH: '7f1d266467620aa553a705391ee06128e8ee14af66129a0e64a282997fb6fd83'
|
||||
BOUF_NSIS_HASH: 'a234126de89f122b6a552df3416de3eabcb4195217626c7f4eaec71b20fe36eb'
|
||||
BOUF_TAG: 'v0.6.4'
|
||||
BOUF_HASH: 'aca6810e741dc38ff843fab7b25a0ad8570ee84f5595132cf0cc4a5b0131b4c4'
|
||||
BOUF_NSIS_HASH: 'ed453784486556bd959d56743a8478ad3f68fe0305e9b43ac19d8771d0515257'
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
# Download bouf release
|
||||
|
@ -86,7 +87,10 @@ runs:
|
|||
run: |
|
||||
# Release notes are just the tag body on Windows
|
||||
Set-Location repo
|
||||
git tag -l --format='%(contents:body)' ${{ inputs.version }} > "${{ github.workspace }}/notes.rst"
|
||||
git tag -l --format='%(contents:subject)' ${{ inputs.tagName }} > "${{ github.workspace }}/notes.rst"
|
||||
Write-Output "###################################################" >> "${{ github.workspace }}/notes.rst"
|
||||
Write-Output "" >> "${{ github.workspace }}/notes.rst"
|
||||
git tag -l --format='%(contents:body)' ${{ inputs.tagName }} >> "${{ github.workspace }}/notes.rst"
|
||||
|
||||
- name: Run bouf
|
||||
shell: pwsh
|
||||
|
|
6
.github/actions/windows-signing/action.yaml
vendored
6
.github/actions/windows-signing/action.yaml
vendored
|
@ -27,9 +27,9 @@ runs:
|
|||
- name: Setup bouf
|
||||
shell: pwsh
|
||||
env:
|
||||
BOUF_TAG: 'v0.6.3'
|
||||
BOUF_HASH: '7f1d266467620aa553a705391ee06128e8ee14af66129a0e64a282997fb6fd83'
|
||||
BOUF_NSIS_HASH: 'a234126de89f122b6a552df3416de3eabcb4195217626c7f4eaec71b20fe36eb'
|
||||
BOUF_TAG: 'v0.6.3-nsis-update'
|
||||
BOUF_HASH: 'c5b58f613e7d7ad8090900cbd6b5f4b057528ee9f0b7e4ba52038ddfdd50b924'
|
||||
BOUF_NSIS_HASH: 'd77cda42d33af77774beddb3fd6edb89e75050f03c174260df7d66bc8247334f'
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
# Download bouf release
|
||||
|
|
1
.github/workflows/publish.yaml
vendored
1
.github/workflows/publish.yaml
vendored
|
@ -20,6 +20,7 @@ jobs:
|
|||
outputs:
|
||||
validTag: ${{ steps.check.outputs.validTag }}
|
||||
flatpakMatrix: ${{ steps.check.outputs.flatpakMatrix }}
|
||||
updateChannel: ${{ steps.check.outputs.updateChannel }}
|
||||
steps:
|
||||
- name: Check Release Tag ☑️
|
||||
id: check
|
||||
|
|
2
.github/workflows/push.yaml
vendored
2
.github/workflows/push.yaml
vendored
|
@ -207,7 +207,7 @@ jobs:
|
|||
|
||||
sign-windows-build:
|
||||
name: Windows Signing ✍️
|
||||
uses: obsproject/obs-studio/.github/workflows/sign-windows.yaml@b5b457d7b059397b70f6e3dd09b65e172ad734c3
|
||||
uses: obsproject/obs-studio/.github/workflows/sign-windows.yaml@d2b05a6e0c6a0f51c42f247efd581bed8f4a1877
|
||||
if: github.repository_owner == 'obsproject' && github.ref_type == 'tag'
|
||||
needs: build-project
|
||||
permissions:
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -9,6 +9,7 @@
|
|||
!/docs
|
||||
!/libobs*
|
||||
!/plugins
|
||||
!/shared
|
||||
!/test
|
||||
!/UI
|
||||
!.cirrus.xml
|
||||
|
|
|
@ -65,9 +65,6 @@ bool AbsoluteSlider::eventFilter(QObject *obj, QEvent *event)
|
|||
}
|
||||
}
|
||||
|
||||
if (event->type() == QEvent::Wheel)
|
||||
return true;
|
||||
|
||||
return QSlider::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include "slider-ignorewheel.hpp"
|
||||
#include <slider-ignorewheel.hpp>
|
||||
|
||||
class AbsoluteSlider : public SliderIgnoreScroll {
|
||||
Q_OBJECT
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include <QComboBox>
|
||||
#include <QCheckBox>
|
||||
#include <cmath>
|
||||
#include "qt-wrappers.hpp"
|
||||
#include <qt-wrappers.hpp>
|
||||
#include "obs-app.hpp"
|
||||
#include "adv-audio-control.hpp"
|
||||
#include "window-basic-main.hpp"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include <obs-frontend-internal.hpp>
|
||||
#include <qt-wrappers.hpp>
|
||||
#include "obs-app.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "window-basic-main.hpp"
|
||||
#include "window-basic-main-outputs.hpp"
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
#include <QRegularExpressionMatch>
|
||||
#include <QString>
|
||||
#include <QtNetwork/QTcpSocket>
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
#include "obs-app.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
|
||||
#define LOGO_URL "https://obsproject.com/assets/images/new_icon_small-r.png"
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <QHBoxLayout>
|
||||
#include <QUrl>
|
||||
#include <QRandomGenerator>
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
|
@ -18,7 +19,6 @@
|
|||
|
||||
#include "auth-listener.hpp"
|
||||
#include "obs-app.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "ui-config.h"
|
||||
#include "youtube-api-wrappers.hpp"
|
||||
#include "window-basic-main.hpp"
|
||||
|
@ -350,7 +350,7 @@ std::shared_ptr<Auth> YoutubeAuth::Login(QWidget *owner,
|
|||
}
|
||||
|
||||
#ifdef BROWSER_AVAILABLE
|
||||
void YoutubeChatDock::SetWidget(QCefWidget *widget_)
|
||||
YoutubeChatDock::YoutubeChatDock(const QString &title) : BrowserDock(title)
|
||||
{
|
||||
lineEdit = new LineEditAutoResize();
|
||||
lineEdit->setVisible(false);
|
||||
|
@ -364,6 +364,14 @@ void YoutubeChatDock::SetWidget(QCefWidget *widget_)
|
|||
chatLayout->addWidget(lineEdit, 1);
|
||||
chatLayout->addWidget(sendButton);
|
||||
|
||||
QWidget::connect(lineEdit, &LineEditAutoResize::returnPressed, this,
|
||||
&YoutubeChatDock::SendChatMessage);
|
||||
QWidget::connect(sendButton, &QPushButton::pressed, this,
|
||||
&YoutubeChatDock::SendChatMessage);
|
||||
}
|
||||
|
||||
void YoutubeChatDock::SetWidget(QCefWidget *widget_)
|
||||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout();
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->addWidget(widget_, 1);
|
||||
|
@ -373,11 +381,6 @@ void YoutubeChatDock::SetWidget(QCefWidget *widget_)
|
|||
widget->setLayout(layout);
|
||||
setWidget(widget);
|
||||
|
||||
QWidget::connect(lineEdit, &LineEditAutoResize::returnPressed, this,
|
||||
&YoutubeChatDock::SendChatMessage);
|
||||
QWidget::connect(sendButton, &QPushButton::pressed, this,
|
||||
&YoutubeChatDock::SendChatMessage);
|
||||
|
||||
cefWidget.reset(widget_);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ private:
|
|||
QHBoxLayout *chatLayout;
|
||||
|
||||
public:
|
||||
inline YoutubeChatDock(const QString &title) : BrowserDock(title) {}
|
||||
YoutubeChatDock(const QString &title);
|
||||
void SetWidget(QCefWidget *widget_);
|
||||
void SetApiChatId(const std::string &id);
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ OBSBasicControls::OBSBasicControls(OBSBasic *main)
|
|||
|
||||
connect(main, &OBSBasic::ReplayBufStarted, this,
|
||||
&OBSBasicControls::ReplayBufferStarted);
|
||||
connect(main, &OBSBasic::ReplayBufferStopping, this,
|
||||
connect(main, &OBSBasic::ReplayBufStopping, this,
|
||||
&OBSBasicControls::ReplayBufferStopping);
|
||||
connect(main, &OBSBasic::ReplayBufStopped, this,
|
||||
&OBSBasicControls::ReplayBufferStopped);
|
||||
|
|
|
@ -71,6 +71,28 @@ find_package(CURL REQUIRED)
|
|||
add_subdirectory(frontend-plugins)
|
||||
add_executable(obs)
|
||||
|
||||
if(NOT TARGET OBS::properties-view)
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/shared/properties-view" "${CMAKE_BINARY_DIR}/shared/properties-view")
|
||||
endif()
|
||||
|
||||
if(NOT TARGET OBS::qt-plain-text-edit)
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit")
|
||||
endif()
|
||||
|
||||
if(NOT TARGET OBS::qt-slider-ignorewheel)
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/slider-ignorewheel"
|
||||
"${CMAKE_BINARY_DIR}/shared/qt/slider-ignorewheel")
|
||||
endif()
|
||||
|
||||
if(NOT TARGET OBS::qt-vertical-scroll-area)
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area"
|
||||
"${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area")
|
||||
endif()
|
||||
|
||||
if(NOT TARGET OBS::qt-wrappers)
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers")
|
||||
endif()
|
||||
|
||||
find_qt(COMPONENTS Widgets Network Svg Xml COMPONENTS_LINUX Gui DBus)
|
||||
|
||||
target_link_libraries(obs PRIVATE Qt::Widgets Qt::Svg Qt::Xml Qt::Network)
|
||||
|
@ -147,8 +169,6 @@ target_sources(
|
|||
platform.hpp
|
||||
qt-display.cpp
|
||||
qt-display.hpp
|
||||
qt-wrappers.cpp
|
||||
qt-wrappers.hpp
|
||||
ui-validation.cpp
|
||||
ui-validation.hpp
|
||||
multiview.cpp
|
||||
|
@ -171,8 +191,6 @@ target_sources(
|
|||
basic-controls.cpp
|
||||
basic-controls.hpp
|
||||
clickable-label.hpp
|
||||
double-slider.cpp
|
||||
double-slider.hpp
|
||||
horizontal-scroll-area.cpp
|
||||
horizontal-scroll-area.hpp
|
||||
item-widget-helpers.cpp
|
||||
|
@ -193,22 +211,13 @@ target_sources(
|
|||
menu-button.hpp
|
||||
mute-checkbox.hpp
|
||||
noncheckable-button.hpp
|
||||
plain-text-edit.cpp
|
||||
plain-text-edit.hpp
|
||||
properties-view.cpp
|
||||
properties-view.hpp
|
||||
properties-view.moc.hpp
|
||||
remote-text.cpp
|
||||
remote-text.hpp
|
||||
scene-tree.cpp
|
||||
scene-tree.hpp
|
||||
screenshot-obj.hpp
|
||||
slider-ignorewheel.cpp
|
||||
slider-ignorewheel.hpp
|
||||
source-label.cpp
|
||||
source-label.hpp
|
||||
spinbox-ignorewheel.cpp
|
||||
spinbox-ignorewheel.hpp
|
||||
source-tree.cpp
|
||||
source-tree.hpp
|
||||
url-push-button.cpp
|
||||
|
@ -217,8 +226,6 @@ target_sources(
|
|||
undo-stack-obs.hpp
|
||||
volume-control.cpp
|
||||
volume-control.hpp
|
||||
vertical-scroll-area.cpp
|
||||
vertical-scroll-area.hpp
|
||||
visibility-item-widget.cpp
|
||||
visibility-item-widget.hpp)
|
||||
|
||||
|
@ -306,8 +313,19 @@ target_compile_features(obs PRIVATE cxx_std_17)
|
|||
|
||||
target_include_directories(obs PRIVATE ${CMAKE_SOURCE_DIR}/deps/json11)
|
||||
|
||||
target_link_libraries(obs PRIVATE CURL::libcurl FFmpeg::avcodec FFmpeg::avutil FFmpeg::avformat OBS::libobs
|
||||
OBS::frontend-api)
|
||||
target_link_libraries(
|
||||
obs
|
||||
PRIVATE CURL::libcurl
|
||||
FFmpeg::avcodec
|
||||
FFmpeg::avutil
|
||||
FFmpeg::avformat
|
||||
OBS::libobs
|
||||
OBS::frontend-api
|
||||
OBS::qt-wrappers
|
||||
OBS::qt-plain-text-edit
|
||||
OBS::qt-vertical-scroll-area
|
||||
OBS::qt-slider-ignorewheel
|
||||
OBS::properties-view)
|
||||
|
||||
set_target_properties(obs PROPERTIES FOLDER "frontend")
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
target_sources(obs-studio PRIVATE platform-x11.cpp)
|
||||
target_compile_definitions(obs-studio PRIVATE OBS_INSTALL_PREFIX="${OBS_INSTALL_PREFIX}")
|
||||
target_link_libraries(obs-studio PRIVATE Qt::GuiPrivate procstat)
|
||||
target_link_libraries(obs-studio PRIVATE Qt::GuiPrivate Qt::DBus procstat)
|
||||
|
||||
target_sources(obs-studio PRIVATE system-info-posix.cpp)
|
||||
|
||||
|
|
|
@ -1,32 +1,23 @@
|
|||
add_library(obs-ui-support INTERFACE)
|
||||
add_library(OBS::ui-support ALIAS obs-ui-support)
|
||||
if(NOT TARGET OBS::properties-view)
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/shared/properties-view" "${CMAKE_BINARY_DIR}/shared/properties-view")
|
||||
endif()
|
||||
|
||||
target_sources(
|
||||
obs-ui-support
|
||||
INTERFACE # cmake-format: sortable
|
||||
clickable-label.hpp
|
||||
double-slider.cpp
|
||||
double-slider.hpp
|
||||
horizontal-scroll-area.cpp
|
||||
horizontal-scroll-area.hpp
|
||||
plain-text-edit.cpp
|
||||
plain-text-edit.hpp
|
||||
properties-view.cpp
|
||||
properties-view.hpp
|
||||
properties-view.moc.hpp
|
||||
qt-wrappers.cpp
|
||||
qt-wrappers.hpp
|
||||
slider-ignorewheel.cpp
|
||||
slider-ignorewheel.hpp
|
||||
spinbox-ignorewheel.cpp
|
||||
spinbox-ignorewheel.hpp
|
||||
vertical-scroll-area.cpp
|
||||
vertical-scroll-area.hpp)
|
||||
if(NOT TARGET OBS::qt-plain-text-edit)
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit")
|
||||
endif()
|
||||
|
||||
target_include_directories(obs-ui-support INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
target_compile_options(obs-ui-support INTERFACE $<$<PLATFORM_ID:Linux>:-Wno-error=enum-conversion>)
|
||||
if(NOT TARGET OBS::qt-slider-ignorewheel)
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/slider-ignorewheel"
|
||||
"${CMAKE_BINARY_DIR}/shared/qt/slider-ignorewheel")
|
||||
endif()
|
||||
|
||||
target_link_libraries(obs-studio PRIVATE OBS::ui-support)
|
||||
if(NOT TARGET OBS::qt-vertical-scroll-area)
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area"
|
||||
"${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area")
|
||||
endif()
|
||||
|
||||
target_link_libraries(obs-studio PRIVATE OBS::properties-view OBS::qt-plain-text-edit OBS::qt-slider-ignorewheel
|
||||
OBS::qt-vertical-scroll-area)
|
||||
|
||||
target_sources(
|
||||
obs-studio
|
||||
|
@ -40,10 +31,13 @@ target_sources(
|
|||
balance-slider.hpp
|
||||
basic-controls.cpp
|
||||
basic-controls.hpp
|
||||
clickable-label.hpp
|
||||
context-bar-controls.cpp
|
||||
context-bar-controls.hpp
|
||||
focus-list.cpp
|
||||
focus-list.hpp
|
||||
horizontal-scroll-area.cpp
|
||||
horizontal-scroll-area.hpp
|
||||
hotkey-edit.cpp
|
||||
hotkey-edit.hpp
|
||||
item-widget-helpers.cpp
|
||||
|
|
|
@ -8,7 +8,11 @@ if(OS_LINUX
|
|||
find_package(Qt6 REQUIRED Gui DBus)
|
||||
endif()
|
||||
|
||||
target_link_libraries(obs-studio PRIVATE Qt::Widgets Qt::Svg Qt::Xml Qt::Network)
|
||||
if(NOT TARGET OBS::qt-wrappers)
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers")
|
||||
endif()
|
||||
|
||||
target_link_libraries(obs-studio PRIVATE Qt::Widgets Qt::Svg Qt::Xml Qt::Network OBS::qt-wrappers)
|
||||
|
||||
set_target_properties(
|
||||
obs-studio
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#include "window-basic-main.hpp"
|
||||
#include "context-bar-controls.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "obs-app.hpp"
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <QStandardItemModel>
|
||||
#include <QColorDialog>
|
||||
#include <QFontDialog>
|
||||
|
|
|
@ -736,7 +736,7 @@ Basic.Main.StopRecording="Stop Recording"
|
|||
Basic.Main.PauseRecording="Pause Recording"
|
||||
Basic.Main.UnpauseRecording="Unpause Recording"
|
||||
Basic.Main.SplitFile="Split Recording File"
|
||||
Basic.Main.AddChapterMarker="Add Chapter Marker"
|
||||
Basic.Main.AddChapterMarker="Add Chapter Marker (Hybrid MP4 only)"
|
||||
Basic.Main.StoppingRecording="Stopping Recording..."
|
||||
Basic.Main.StopReplayBuffer="Stop Replay Buffer"
|
||||
Basic.Main.StoppingReplayBuffer="Stopping Replay Buffer..."
|
||||
|
@ -1566,10 +1566,10 @@ FailedToStartStream.MissingConfigURL="No config URL available for the current se
|
|||
FailedToStartStream.NoCustomRTMPURLInSettings="Custom RTMP URL not specified"
|
||||
FailedToStartStream.InvalidCustomConfig="Invalid custom config"
|
||||
FailedToStartStream.FailedToCreateMultitrackVideoService="Failed to create multitrack video service"
|
||||
FailedToStartStream.FailedToCreateMultitrackVideoOutput="Failed to create multitrack video rtmp output"
|
||||
FailedToStartStream.FailedToCreateMultitrackVideoOutput="Failed to create multitrack video RTMP output"
|
||||
FailedToStartStream.EncoderNotAvailable="NVENC not available.\n\nFailed to find encoder type '%1'"
|
||||
FailedToStartStream.FailedToCreateVideoEncoder="Failed to create video encoder '%1' (type: '%2')"
|
||||
FailedToStartStream.FailedToGetOBSVideoInfo="Failed to get obs video info while creating encoder '%1' (type: '%2')"
|
||||
FailedToStartStream.FailedToGetOBSVideoInfo="Failed to get OBS video info while creating encoder '%1' (type: '%2')"
|
||||
FailedToStartStream.FailedToCreateAudioEncoder="Failed to create audio encoder"
|
||||
FailedToStartStream.NoRTMPURLInConfig="Config does not contain stream target RTMP(S) URL"
|
||||
FailedToStartStream.FallbackToDefault="Starting the stream using %1 failed; do you want to retry using single encode settings?"
|
||||
|
@ -1585,3 +1585,6 @@ MultitrackVideo.IncompatibleSettings.Title="Incompatible Settings"
|
|||
MultitrackVideo.IncompatibleSettings.Text="%1 is not currently compatible with:\n\n%2\nTo continue streaming with %1, disable incompatible settings:\n\n%3\nand Start Streaming again."
|
||||
MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Disable for this stream and Start Streaming"
|
||||
MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Update Settings and Start Streaming"
|
||||
MultitrackVideo.IncompatibleSettings.AudioChannels="%1 is not currently compatible with [Audio → General → Channels] set to '%2', %3"
|
||||
MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[Audio → General → Channels] needs to be set to '%1'"
|
||||
MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 requires multiple different settings for [Audio → General → Channels]"
|
||||
|
|
|
@ -396,13 +396,13 @@ QCalendarWidget QToolButton {
|
|||
QCalendarWidget #qt_calendar_prevmonth {
|
||||
padding: 2px;
|
||||
qproperty-icon: url(theme:Dark/left.svg);
|
||||
icon-size: 16px, 16px;
|
||||
icon-size: 16px;
|
||||
}
|
||||
|
||||
QCalendarWidget #qt_calendar_nextmonth {
|
||||
padding: 2px;
|
||||
qproperty-icon: url(theme:Dark/right.svg);
|
||||
icon-size: 16px, 16px;
|
||||
icon-size: 16px;
|
||||
}
|
||||
|
||||
/* Status Bar */
|
||||
|
|
|
@ -127,13 +127,14 @@
|
|||
|
||||
--padding_base_border: calc(var(--padding_base) + 1px);
|
||||
|
||||
--spinbox_button_height: calc(var(--input_height) - 2px);
|
||||
--spinbox_button_height: calc(var(--input_height_half) - 1px);
|
||||
|
||||
--volume_slider: calc(calc(6px + var(--font_base_value)) / 2);
|
||||
--volume_slider: calc(calc(10px + var(--font_base_value)) / 4);
|
||||
--volume_slider_box: calc(var(--volume_slider) * 4);
|
||||
--volume_slider_label: calc(var(--volume_slider) * 6);
|
||||
--volume_slider_label: calc(var(--volume_slider_box) * 2);
|
||||
|
||||
--scrollbar_size: 12px;
|
||||
--settings_scrollbar_size: calc(var(--scrollbar_size) + 9px);
|
||||
|
||||
/* Inputs / Controls */
|
||||
--border_color: var(--grey4);
|
||||
|
@ -451,6 +452,16 @@ OBSBasicSettings QListWidget::item {
|
|||
padding: var(--padding_large);
|
||||
}
|
||||
|
||||
OBSBasicSettings QScrollBar:vertical {
|
||||
width: var(--settings_scrollbar_size);
|
||||
margin-left: 9px;
|
||||
}
|
||||
|
||||
OBSBasicSettings QScrollBar:horizontal {
|
||||
height: var(--settings_scrollbar_size);
|
||||
margin-top: 9px;
|
||||
}
|
||||
|
||||
/* Settings properties view */
|
||||
OBSBasicSettings #PropertiesContainer {
|
||||
background-color: var(--bg_base);
|
||||
|
@ -466,7 +477,7 @@ OBSDock > QWidget {
|
|||
}
|
||||
|
||||
#transitionsFrame {
|
||||
padding: 4px 8px;
|
||||
padding: var(--padding_large);
|
||||
}
|
||||
|
||||
OBSDock QLabel {
|
||||
|
@ -638,12 +649,12 @@ QScrollBar::handle:horizontal {
|
|||
|
||||
QPushButton#sourcePropertiesButton {
|
||||
qproperty-icon: url(theme:Dark/settings/general.svg);
|
||||
icon-size: var(--icon_base), var(--icon_base);
|
||||
icon-size: var(--icon_base);
|
||||
}
|
||||
|
||||
QPushButton#sourceFiltersButton {
|
||||
qproperty-icon: url(theme:Dark/filter.svg);
|
||||
icon-size: var(--icon_base), var(--icon_base);
|
||||
icon-size: var(--icon_base);
|
||||
}
|
||||
|
||||
/* Scenes and Sources toolbar */
|
||||
|
@ -798,7 +809,6 @@ QDateTimeEdit {
|
|||
border-radius: var(--border_radius);
|
||||
padding: var(--padding_large) var(--padding_large);
|
||||
padding-left: 10px;
|
||||
max-height: var(--input_height);
|
||||
}
|
||||
|
||||
QComboBox QAbstractItemView::item:selected,
|
||||
|
@ -817,7 +827,7 @@ QComboBox::drop-down,
|
|||
QDateTimeEdit::drop-down {
|
||||
border:none;
|
||||
border-left: 1px solid var(--grey6);
|
||||
width: 32px;
|
||||
width: var(--input_height);
|
||||
}
|
||||
|
||||
QComboBox::down-arrow,
|
||||
|
@ -861,6 +871,7 @@ QPlainTextEdit {
|
|||
border: none;
|
||||
border-radius: var(--border_radius);
|
||||
padding: var(--input_padding) var(--padding_small) var(--input_padding) var(--input_padding);
|
||||
padding-left: 8px;
|
||||
border: 1px solid var(--input_bg);
|
||||
height: var(--input_height);
|
||||
}
|
||||
|
@ -890,10 +901,9 @@ QDoubleSpinBox {
|
|||
background-color: var(--input_bg);
|
||||
border: 1px solid var(--input_bg);
|
||||
border-radius: var(--border_radius);
|
||||
margin-right: var(--spacing_base);
|
||||
padding: var(--input_padding) 0px var(--input_padding) var(--input_padding);
|
||||
height: var(--spinbox_button_height);
|
||||
max-height: var(--spinbox_button_height);
|
||||
padding-left: 8px;
|
||||
max-height: var(--input_height);
|
||||
}
|
||||
|
||||
QSpinBox:hover,
|
||||
|
@ -913,11 +923,12 @@ QDoubleSpinBox::up-button {
|
|||
subcontrol-origin: padding;
|
||||
subcontrol-position: top right; /* position at the top right corner */
|
||||
|
||||
width: 32px;
|
||||
width: var(--input_height);
|
||||
height: var(--spinbox_button_height);
|
||||
border-left: 1px solid var(--grey6);
|
||||
border-bottom: 1px solid transparent;
|
||||
border-radius: 0px;
|
||||
margin-top: -1px;
|
||||
border-top-right-radius: var(--border_radius_small);
|
||||
}
|
||||
|
||||
QSpinBox::down-button,
|
||||
|
@ -925,11 +936,12 @@ QDoubleSpinBox::down-button {
|
|||
subcontrol-origin: padding;
|
||||
subcontrol-position: bottom right; /* position at the top right corner */
|
||||
|
||||
width: 32px;
|
||||
width: var(--input_height);
|
||||
height: var(--spinbox_button_height);
|
||||
border-left: 1px solid var(--grey6);
|
||||
border-top: 1px solid var(--grey6);
|
||||
border-radius: 0px;
|
||||
margin-bottom: -1px;
|
||||
border-bottom-right-radius: var(--border_radius_small);
|
||||
}
|
||||
|
||||
QSpinBox::up-button:hover,
|
||||
|
@ -1029,7 +1041,7 @@ QPushButton {
|
|||
height: var(--input_height);
|
||||
max-height: var(--input_height);
|
||||
padding: var(--input_padding) var(--padding_wide);
|
||||
icon-size: var(--icon_base), var(--icon_base);
|
||||
icon-size: var(--icon_base);
|
||||
}
|
||||
|
||||
QPushButton {
|
||||
|
@ -1047,7 +1059,7 @@ QPushButton[toolButton="true"] {
|
|||
margin: 0px var(--spacing_base);
|
||||
border: 1px solid var(--button_border);
|
||||
border-radius: var(--border_radius);
|
||||
icon-size: var(--icon_base), var(--icon_base);
|
||||
icon-size: var(--icon_base);
|
||||
}
|
||||
|
||||
QToolButton:last-child,
|
||||
|
@ -1066,6 +1078,8 @@ QPushButton:hover {
|
|||
|
||||
QToolButton:hover,
|
||||
QToolButton:focus,
|
||||
QPushButton[toolButton="true"]:hover,
|
||||
QPushButton[toolButton="true"]:focus,
|
||||
MuteCheckBox::indicator:hover,
|
||||
MuteCheckBox::indicator:focus {
|
||||
border-color: var(--button_border);
|
||||
|
@ -1092,7 +1106,9 @@ QPushButton:pressed:hover {
|
|||
}
|
||||
|
||||
QToolButton:pressed,
|
||||
QToolButton:pressed:hover {
|
||||
QToolButton:pressed:hover,
|
||||
QPushButton[toolButton="true"]:pressed,
|
||||
QPushButton[toolButton="true"]:pressed:hover {
|
||||
background-color: var(--button_bg_down);
|
||||
border-color: var(--button_border);
|
||||
}
|
||||
|
@ -1197,10 +1213,10 @@ QSlider::handle:disabled {
|
|||
height: var(--icon_base);
|
||||
background-color: var(--button_bg);
|
||||
padding: var(--padding_base_border) var(--padding_base_border);
|
||||
margin: 0px var(--spacing_base);
|
||||
margin: 0px;
|
||||
border: 1px solid var(--button_border);
|
||||
border-radius: var(--border_radius);
|
||||
icon-size: var(--icon_base), var(--icon_base);
|
||||
icon-size: var(--icon_base);
|
||||
}
|
||||
|
||||
/* This is an incredibly cursed but necessary fix */
|
||||
|
@ -1232,12 +1248,12 @@ VolControl #volLabel {
|
|||
|
||||
/* Horizontal Mixer */
|
||||
#hMixerScrollArea VolControl {
|
||||
padding: 0px var(--padding_large);
|
||||
padding: 0px var(--padding_xlarge) var(--padding_base);
|
||||
border-bottom: 1px solid var(--border_color);
|
||||
}
|
||||
|
||||
#hMixerScrollArea VolControl QSlider {
|
||||
margin: 0px 0px;
|
||||
margin: 0px 0px var(--padding_base);
|
||||
}
|
||||
|
||||
#hMixerScrollArea VolControl QSlider::groove:horizontal {
|
||||
|
@ -1245,10 +1261,6 @@ VolControl #volLabel {
|
|||
height: var(--volume_slider);
|
||||
}
|
||||
|
||||
#hMixerScrollArea VolControl QPushButton {
|
||||
margin-right: var(--padding_xlarge);
|
||||
}
|
||||
|
||||
/* Vertical Mixer */
|
||||
#stackedMixerArea QScrollBar:vertical {
|
||||
border-left: 1px solid var(--border_color);
|
||||
|
@ -1261,13 +1273,13 @@ VolControl #volLabel {
|
|||
|
||||
#vMixerScrollArea VolControl QSlider {
|
||||
width: var(--volume_slider_box);
|
||||
margin: 0px var(--padding_xlarge);
|
||||
}
|
||||
|
||||
#vMixerScrollArea VolControl #volLabel {
|
||||
padding: var(--padding_base) 0px var(--padding_base);
|
||||
min-width: var(--volume_slider_label);
|
||||
max-width: var(--volume_slider_label);
|
||||
margin-right: 0;
|
||||
margin-left: var(--padding_xlarge);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
@ -1277,7 +1289,7 @@ VolControl #volLabel {
|
|||
}
|
||||
|
||||
#vMixerScrollArea VolControl #volMeterFrame {
|
||||
padding: var(--padding_large) var(--padding_xlarge);
|
||||
padding: var(--padding_large) var(--padding_xlarge) var(--padding_large) 0px;
|
||||
}
|
||||
|
||||
#vMixerScrollArea VolControl QLabel {
|
||||
|
@ -1384,9 +1396,14 @@ QLabel#errorLabel {
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
QFrame [themeID="notice"] {
|
||||
QFrame [noticeFrame="true"] {
|
||||
background: var(--bg_preview);
|
||||
border-radius: var(--border_radius);
|
||||
padding: var(--padding_xlarge) var(--padding_large);
|
||||
}
|
||||
|
||||
QFrame [noticeFrame="true"] QLabel {
|
||||
padding: var(--padding_large) 0px;
|
||||
}
|
||||
|
||||
/* About dialog */
|
||||
|
@ -1586,19 +1603,19 @@ MuteCheckBox::indicator:unchecked {
|
|||
height: var(--icon_base);
|
||||
background-color: var(--button_bg);
|
||||
padding: var(--padding_base_border) var(--padding_base_border);
|
||||
margin: 0px var(--spacing_base);
|
||||
margin: 0px;
|
||||
border: 1px solid var(--button_border);
|
||||
border-radius: var(--border_radius);
|
||||
icon-size: var(--icon_base), var(--icon_base);
|
||||
icon-size: var(--icon_base);
|
||||
}
|
||||
|
||||
MuteCheckBox::indicator:hover,
|
||||
MuteCheckBox::indicator:unchecked:hover {
|
||||
background-color: var(--button_bg_hover);
|
||||
padding: var(--padding_base_border) var(--padding_base_border);
|
||||
margin: 0px var(--spacing_base);
|
||||
margin: 0px;
|
||||
border: 1px solid var(--button_border_hover);
|
||||
icon-size: var(--icon_base), var(--icon_base);
|
||||
icon-size: var(--icon_base);
|
||||
}
|
||||
|
||||
MuteCheckBox::indicator:pressed,
|
||||
|
@ -1869,13 +1886,13 @@ QCalendarWidget QToolButton {
|
|||
QCalendarWidget #qt_calendar_prevmonth {
|
||||
padding: var(--padding_small);
|
||||
qproperty-icon: url(theme:Dark/left.svg);
|
||||
icon-size: var(--icon_base), var(--icon_base);
|
||||
icon-size: var(--icon_base);
|
||||
}
|
||||
|
||||
QCalendarWidget #qt_calendar_nextmonth {
|
||||
padding: var(--padding_small);
|
||||
qproperty-icon: url(theme:Dark/right.svg);
|
||||
icon-size: var(--icon_base), var(--icon_base);
|
||||
icon-size: var(--icon_base);
|
||||
}
|
||||
|
||||
QCalendarWidget QToolButton:hover {
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
--grey2: rgb(134,135,134);
|
||||
--grey3: rgb(122,121,122);
|
||||
--grey4: rgb(76,76,76);
|
||||
--grey5: rgb(88,87,88);
|
||||
--grey5: rgb(70,69,70);
|
||||
--grey6: rgb(31,30,31);
|
||||
--grey7: rgb(58,57,58);
|
||||
--grey8: rgb(46,45,46);
|
||||
|
@ -31,8 +31,12 @@
|
|||
/* OS Fixes */
|
||||
--os_mac_font_base_value: 11;
|
||||
|
||||
--font_small: calc(0.8pt * var(--font_base_value));
|
||||
|
||||
--icon_base: calc(6px + var(--font_base_value));
|
||||
|
||||
--padding_xlarge: calc(2px + calc(0.5px * var(--padding_base_value)));
|
||||
|
||||
--padding_wide: calc(18px + calc(0.25 * var(--padding_base_value)));
|
||||
--padding_menu: calc(8px + calc(1 * var(--padding_base_value)));
|
||||
|
||||
|
@ -45,14 +49,14 @@
|
|||
--border_radius_large: 2px;
|
||||
|
||||
--input_bg: var(--grey4);
|
||||
--input_bg_hover: var(--grey5);
|
||||
--input_bg_hover: var(--grey1);
|
||||
--input_bg_focus: var(--grey6);
|
||||
|
||||
--list_item_bg_selected: var(--primary);
|
||||
--list_item_bg_hover: var(--primary_light);
|
||||
|
||||
--input_border: var(--grey4);
|
||||
--input_border_hover: var(--grey5);
|
||||
--input_border_hover: var(--grey1);
|
||||
--input_border_focus: var(--grey6);
|
||||
|
||||
--spacing_input: var(--spacing_base);
|
||||
|
@ -91,11 +95,17 @@ QStatusBar {
|
|||
background-color: var(--bg_window);
|
||||
}
|
||||
|
||||
OBSDock > QWidget {
|
||||
border-top: 1px solid var(--border_color);
|
||||
padding-top: var(--spacing_large);
|
||||
}
|
||||
|
||||
QDockWidget {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
QDockWidget::title {
|
||||
background-color: var(--grey5);
|
||||
padding: var(--dock_title_padding);
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -104,6 +114,10 @@ QDockWidget > QWidget {
|
|||
background: var(--bg_window);
|
||||
}
|
||||
|
||||
#transitionsFrame {
|
||||
padding: var(--padding_xlarge);
|
||||
}
|
||||
|
||||
SceneTree::item,
|
||||
SourceTreeItem {
|
||||
border-width: 0px;
|
||||
|
@ -113,6 +127,10 @@ QMenu::item {
|
|||
padding: var(--padding_menu_y) var(--padding_menu);
|
||||
}
|
||||
|
||||
QMenu::item {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
QGroupBox {
|
||||
background: var(--bg_window);
|
||||
border: 1px solid var(--border_color);
|
||||
|
@ -189,16 +207,24 @@ OBSBasicSettings QListWidget::item {
|
|||
padding: 4px;
|
||||
}
|
||||
|
||||
QPushButton:checked {
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
QToolButton,
|
||||
QPushButton[toolButton="true"] {
|
||||
background-color: var(--bg_window);
|
||||
border-color: var(--bg_window);
|
||||
}
|
||||
|
||||
#stackedMixerArea QScrollArea {
|
||||
background: var(--bg_base);
|
||||
}
|
||||
|
||||
#stackedMixerArea QPushButton {
|
||||
min-width: var(--icon_base);
|
||||
padding: var(--padding_large) var(--padding_large);
|
||||
icon-size: var(--icon_base), var(--icon_base);
|
||||
icon-size: var(--icon_base);
|
||||
}
|
||||
|
||||
#stackedMixerArea QPushButton:!hover {
|
||||
|
@ -211,13 +237,40 @@ QPushButton[toolButton="true"] {
|
|||
border: none;
|
||||
}
|
||||
|
||||
#hMixerScrollArea VolControl {
|
||||
padding: 0px 2px;
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
#hMixerScrollArea QLabel {
|
||||
margin: var(--padding_xlarge) 0px;
|
||||
}
|
||||
|
||||
#hMixerScrollArea #volMeterFrame {
|
||||
margin-top: var(--spacing_large);
|
||||
}
|
||||
|
||||
#vMixerScrollArea VolControl {
|
||||
padding: 0px var(--padding_xlarge) var(--spacing_large);
|
||||
border-right: 1px solid var(--bg_window);
|
||||
}
|
||||
|
||||
#vMixerScrollArea QLabel {
|
||||
font-size: var(--font_small);
|
||||
margin: var(--padding_xlarge) 0px;
|
||||
}
|
||||
|
||||
#vMixerScrollArea #volLabel {
|
||||
font-size: var(--font_base);
|
||||
}
|
||||
|
||||
MuteCheckBox::indicator,
|
||||
MuteCheckBox::indicator:unchecked {
|
||||
background-color: var(--bg_base);
|
||||
border: none;
|
||||
width: var(--icon_base_mixer);
|
||||
height: var(--icon_base_mixer);
|
||||
icon-size: var(--icon_base_mixer), var(--icon_base_mixer);
|
||||
icon-size: var(--icon_base_mixer);
|
||||
}
|
||||
|
||||
MuteCheckBox::indicator:checked {
|
||||
|
@ -231,7 +284,7 @@ MuteCheckBox::indicator:unchecked:hover {
|
|||
|
||||
MuteCheckBox::indicator:hover,
|
||||
MuteCheckBox::indicator:unchecked:hover {
|
||||
icon-size: var(--icon_base_mixer), var(--icon_base_mixer);
|
||||
icon-size: var(--icon_base_mixer);
|
||||
border: none;
|
||||
}
|
||||
|
||||
|
@ -251,3 +304,7 @@ VolumeMeter {
|
|||
qproperty-minorTickColor: rgb(122,121,122); /* light */
|
||||
qproperty-meterThickness: 3;
|
||||
}
|
||||
|
||||
OBSBasicStats {
|
||||
background: var(--bg_window);
|
||||
}
|
||||
|
|
|
@ -128,6 +128,9 @@
|
|||
<property name="themeID" stdset="0">
|
||||
<string>addIconSmall</string>
|
||||
</property>
|
||||
<property name="toolButton" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -157,6 +160,9 @@
|
|||
<property name="themeID" stdset="0">
|
||||
<string>removeIconSmall</string>
|
||||
</property>
|
||||
<property name="toolButton" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -186,6 +192,9 @@
|
|||
<property name="themeID" stdset="0">
|
||||
<string>upArrowIconSmall</string>
|
||||
</property>
|
||||
<property name="toolButton" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -215,6 +224,9 @@
|
|||
<property name="themeID" stdset="0">
|
||||
<string>downArrowIconSmall</string>
|
||||
</property>
|
||||
<property name="toolButton" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -350,6 +362,9 @@
|
|||
<property name="themeID" stdset="0">
|
||||
<string>addIconSmall</string>
|
||||
</property>
|
||||
<property name="toolButton" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -379,6 +394,9 @@
|
|||
<property name="themeID" stdset="0">
|
||||
<string>removeIconSmall</string>
|
||||
</property>
|
||||
<property name="toolButton" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -408,6 +426,9 @@
|
|||
<property name="themeID" stdset="0">
|
||||
<string>upArrowIconSmall</string>
|
||||
</property>
|
||||
<property name="toolButton" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -437,6 +458,9 @@
|
|||
<property name="themeID" stdset="0">
|
||||
<string>downArrowIconSmall</string>
|
||||
</property>
|
||||
<property name="toolButton" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,9 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <properties-view.hpp>
|
||||
|
||||
#include "ui_output.h"
|
||||
#include "../../UI/properties-view.hpp"
|
||||
|
||||
namespace aja {
|
||||
class CardManager;
|
||||
|
|
|
@ -23,6 +23,10 @@ if(NOT TARGET OBS::aja-support)
|
|||
add_subdirectory("${CMAKE_SOURCE_DIR}/plugins/aja" "${CMAKE_BINARY_DIR}/plugins/aja")
|
||||
endif()
|
||||
|
||||
if(NOT TARGET OBS::properties-view)
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/shared/properties-view" "${CMAKE_BINARY_DIR}/shared/properties-view")
|
||||
endif()
|
||||
|
||||
add_library(aja-output-ui MODULE)
|
||||
add_library(OBS::aja-output-ui ALIAS aja-output-ui)
|
||||
|
||||
|
@ -40,7 +44,7 @@ target_link_libraries(
|
|||
PRIVATE OBS::libobs
|
||||
OBS::aja-support
|
||||
OBS::frontend-api
|
||||
OBS::ui-support
|
||||
OBS::properties-view
|
||||
Qt::Widgets
|
||||
AJA::LibAJANTV2
|
||||
$<$<PLATFORM_ID:Linux,FreeBSD,OpenBSD>:X11::X11>
|
||||
|
|
|
@ -9,6 +9,10 @@ find_package(LibAJANTV2 REQUIRED)
|
|||
add_library(aja-output-ui MODULE)
|
||||
add_library(OBS::aja-output-ui ALIAS aja-output-ui)
|
||||
|
||||
if(NOT TARGET OBS::properties-view)
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/shared/properties-view" "${CMAKE_BINARY_DIR}/shared/properties-view")
|
||||
endif()
|
||||
|
||||
find_qt(COMPONENTS Widgets COMPONENTS_LINUX Gui)
|
||||
|
||||
set_target_properties(
|
||||
|
@ -45,24 +49,10 @@ target_sources(
|
|||
${CMAKE_SOURCE_DIR}/plugins/aja/aja-vpid-data.cpp
|
||||
${CMAKE_SOURCE_DIR}/plugins/aja/aja-vpid-data.hpp
|
||||
${CMAKE_SOURCE_DIR}/plugins/aja/aja-widget-io.cpp
|
||||
${CMAKE_SOURCE_DIR}/plugins/aja/aja-widget-io.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/double-slider.cpp
|
||||
${CMAKE_SOURCE_DIR}/UI/double-slider.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/plain-text-edit.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/plain-text-edit.cpp
|
||||
${CMAKE_SOURCE_DIR}/UI/properties-view.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/properties-view.cpp
|
||||
${CMAKE_SOURCE_DIR}/UI/properties-view.moc.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/qt-wrappers.cpp
|
||||
${CMAKE_SOURCE_DIR}/UI/qt-wrappers.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.cpp
|
||||
${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.cpp
|
||||
${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/vertical-scroll-area.cpp
|
||||
${CMAKE_SOURCE_DIR}/UI/vertical-scroll-area.hpp)
|
||||
${CMAKE_SOURCE_DIR}/plugins/aja/aja-widget-io.hpp)
|
||||
|
||||
target_link_libraries(aja-output-ui PRIVATE OBS::libobs OBS::frontend-api Qt::Widgets AJA::LibAJANTV2)
|
||||
target_link_libraries(aja-output-ui PRIVATE OBS::libobs OBS::frontend-api OBS::properties-view Qt::Widgets
|
||||
AJA::LibAJANTV2)
|
||||
|
||||
if(OS_MACOS)
|
||||
find_library(IOKIT_FRAMEWORK Iokit)
|
||||
|
|
|
@ -20,6 +20,10 @@ endif()
|
|||
add_library(decklink-output-ui MODULE)
|
||||
add_library(OBS::decklink-output-ui ALIAS decklink-output-ui)
|
||||
|
||||
if(NOT TARGET OBS::properties-view)
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/shared/properties-view" "${CMAKE_BINARY_DIR}/shared/properties-view")
|
||||
endif()
|
||||
|
||||
target_sources(decklink-output-ui PRIVATE forms/output.ui)
|
||||
|
||||
target_sources(decklink-output-ui PRIVATE DecklinkOutputUI.cpp DecklinkOutputUI.h decklink-ui-main.cpp
|
||||
|
@ -31,7 +35,7 @@ target_link_libraries(
|
|||
decklink-output-ui
|
||||
PRIVATE OBS::libobs
|
||||
OBS::frontend-api
|
||||
OBS::ui-support
|
||||
OBS::properties-view
|
||||
Qt::Widgets
|
||||
"$<$<PLATFORM_ID:Darwin>:$<LINK_LIBRARY:FRAMEWORK,Cocoa.framework>>"
|
||||
$<$<PLATFORM_ID:Linux,FreeBSD,OpenBSD>:X11::X11>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <properties-view.hpp>
|
||||
|
||||
#include "ui_output.h"
|
||||
#include "../../UI/properties-view.hpp"
|
||||
|
||||
class DecklinkOutputUI : public QDialog {
|
||||
Q_OBJECT
|
||||
|
|
|
@ -7,6 +7,10 @@ endif()
|
|||
add_library(decklink-output-ui MODULE)
|
||||
add_library(OBS::decklink-output-ui ALIAS decklink-output-ui)
|
||||
|
||||
if(NOT TARGET OBS::properties-view)
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/shared/properties-view" "${CMAKE_BINARY_DIR}/shared/properties-view")
|
||||
endif()
|
||||
|
||||
find_qt(COMPONENTS Widgets COMPONENTS_LINUX Gui)
|
||||
|
||||
set_target_properties(
|
||||
|
@ -22,29 +26,10 @@ endif()
|
|||
|
||||
target_sources(decklink-output-ui PRIVATE forms/output.ui)
|
||||
|
||||
target_sources(
|
||||
decklink-output-ui
|
||||
PRIVATE DecklinkOutputUI.cpp
|
||||
DecklinkOutputUI.h
|
||||
decklink-ui-main.cpp
|
||||
decklink-ui-main.h
|
||||
${CMAKE_SOURCE_DIR}/UI/double-slider.cpp
|
||||
${CMAKE_SOURCE_DIR}/UI/double-slider.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/plain-text-edit.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/plain-text-edit.cpp
|
||||
${CMAKE_SOURCE_DIR}/UI/properties-view.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/properties-view.cpp
|
||||
${CMAKE_SOURCE_DIR}/UI/properties-view.moc.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/qt-wrappers.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/qt-wrappers.cpp
|
||||
${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.cpp
|
||||
${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.cpp
|
||||
${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/vertical-scroll-area.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/vertical-scroll-area.cpp)
|
||||
target_sources(decklink-output-ui PRIVATE DecklinkOutputUI.cpp DecklinkOutputUI.h decklink-ui-main.cpp
|
||||
decklink-ui-main.h)
|
||||
|
||||
target_link_libraries(decklink-output-ui PRIVATE OBS::libobs OBS::frontend-api Qt::Widgets)
|
||||
target_link_libraries(decklink-output-ui PRIVATE OBS::libobs OBS::frontend-api OBS::properties-view Qt::Widgets)
|
||||
|
||||
target_compile_features(decklink-output-ui PRIVATE cxx_std_17)
|
||||
|
||||
|
|
|
@ -14,6 +14,18 @@ endif()
|
|||
add_library(frontend-tools MODULE)
|
||||
add_library(OBS::frontend-tools ALIAS frontend-tools)
|
||||
|
||||
if(NOT TARGET OBS::properties-view)
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/shared/properties-view" "${CMAKE_BINARY_DIR}/shared/properties-view")
|
||||
endif()
|
||||
|
||||
if(NOT TARGET OBS::qt-plain-text-edit)
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit")
|
||||
endif()
|
||||
|
||||
if(NOT TARGET OBS::qt-wrappers)
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers")
|
||||
endif()
|
||||
|
||||
target_sources(
|
||||
frontend-tools
|
||||
PRIVATE # cmake-format: sortable
|
||||
|
@ -45,13 +57,15 @@ target_link_libraries(
|
|||
frontend-tools
|
||||
PRIVATE OBS::frontend-api
|
||||
OBS::libobs
|
||||
OBS::ui-support
|
||||
OBS::properties-view
|
||||
OBS::qt-plain-text-edit
|
||||
OBS::qt-wrappers
|
||||
Qt::Widgets
|
||||
"$<$<PLATFORM_ID:Darwin>:$<LINK_LIBRARY:FRAMEWORK,Cocoa>>"
|
||||
$<$<PLATFORM_ID:Linux,FreeBSD,OpenBSD>:X11::X11>
|
||||
$<$<PLATFORM_ID:Linux,FreeBSD,OpenBSD>:Qt::GuiPrivate>)
|
||||
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/deps/obs-scripting" "${CMAKE_BINARY_DIR}/deps/obs-scripting")
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/shared/obs-scripting" "${CMAKE_BINARY_DIR}/shared/obs-scripting")
|
||||
|
||||
if(ENABLE_SCRIPTING AND TARGET OBS::scripting)
|
||||
target_sources(frontend-tools PRIVATE scripts.cpp scripts.hpp)
|
||||
|
|
|
@ -3,6 +3,18 @@ project(frontend-tools)
|
|||
add_library(frontend-tools MODULE)
|
||||
add_library(OBS::frontend-tools ALIAS frontend-tools)
|
||||
|
||||
if(NOT TARGET OBS::properties-view)
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/shared/properties-view" "${CMAKE_BINARY_DIR}/shared/properties-view")
|
||||
endif()
|
||||
|
||||
if(NOT TARGET OBS::qt-plain-text-edit)
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit")
|
||||
endif()
|
||||
|
||||
if(NOT TARGET OBS::qt-wrappers)
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers")
|
||||
endif()
|
||||
|
||||
find_qt(COMPONENTS Widgets COMPONENTS_LINUX Gui)
|
||||
|
||||
set_target_properties(
|
||||
|
@ -19,40 +31,20 @@ endif()
|
|||
target_sources(frontend-tools PRIVATE forms/auto-scene-switcher.ui forms/captions.ui forms/output-timer.ui
|
||||
forms/scripts.ui)
|
||||
|
||||
target_sources(
|
||||
frontend-tools
|
||||
PRIVATE frontend-tools.c
|
||||
auto-scene-switcher.hpp
|
||||
auto-scene-switcher.cpp
|
||||
output-timer.hpp
|
||||
tool-helpers.hpp
|
||||
output-timer.cpp
|
||||
${CMAKE_SOURCE_DIR}/UI/double-slider.cpp
|
||||
${CMAKE_SOURCE_DIR}/UI/double-slider.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/horizontal-scroll-area.cpp
|
||||
${CMAKE_SOURCE_DIR}/UI/horizontal-scroll-area.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/properties-view.cpp
|
||||
${CMAKE_SOURCE_DIR}/UI/properties-view.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/properties-view.moc.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/qt-wrappers.cpp
|
||||
${CMAKE_SOURCE_DIR}/UI/qt-wrappers.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.cpp
|
||||
${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.cpp
|
||||
${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/vertical-scroll-area.hpp
|
||||
${CMAKE_SOURCE_DIR}/UI/vertical-scroll-area.cpp
|
||||
${CMAKE_SOURCE_DIR}/UI/plain-text-edit.cpp
|
||||
${CMAKE_SOURCE_DIR}/UI/plain-text-edit.hpp)
|
||||
target_sources(frontend-tools PRIVATE frontend-tools.c auto-scene-switcher.hpp auto-scene-switcher.cpp output-timer.hpp
|
||||
tool-helpers.hpp output-timer.cpp)
|
||||
|
||||
target_compile_features(frontend-tools PRIVATE cxx_std_17)
|
||||
|
||||
target_link_libraries(frontend-tools PRIVATE OBS::frontend-api OBS::libobs Qt::Widgets)
|
||||
target_link_libraries(frontend-tools PRIVATE OBS::frontend-api OBS::qt-wrappers OBS::qt-plain-text-edit
|
||||
OBS::properties-view OBS::libobs Qt::Widgets)
|
||||
|
||||
if(OS_POSIX AND NOT OS_MACOS)
|
||||
target_link_libraries(frontend-tools PRIVATE Qt::GuiPrivate)
|
||||
endif()
|
||||
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/shared/obs-scripting" "${CMAKE_BINARY_DIR}/shared/obs-scripting")
|
||||
|
||||
if(ENABLE_SCRIPTING AND TARGET OBS::scripting)
|
||||
target_compile_definitions(frontend-tools PRIVATE ENABLE_SCRIPTING)
|
||||
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
#include "obs-module.h"
|
||||
#include "scripts.hpp"
|
||||
#include "../../properties-view.hpp"
|
||||
#include "../../qt-wrappers.hpp"
|
||||
#include "../../plain-text-edit.hpp"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QHBoxLayout>
|
||||
|
@ -18,6 +15,9 @@
|
|||
#include <QMenu>
|
||||
#include <QUrl>
|
||||
#include <QDesktopServices>
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <plain-text-edit.hpp>
|
||||
#include <properties-view.hpp>
|
||||
|
||||
#include <obs.hpp>
|
||||
#include <obs-module.h>
|
||||
|
|
|
@ -128,7 +128,8 @@ GoLiveApi::Config DownloadGoLiveConfig(QWidget *parent, QString url,
|
|||
|
||||
QString MultitrackVideoAutoConfigURL(obs_service_t *service)
|
||||
{
|
||||
static const QString url = [service]() -> QString {
|
||||
static const std::optional<QString> cli_url =
|
||||
[]() -> std::optional<QString> {
|
||||
auto args = qApp->arguments();
|
||||
for (int i = 0; i < args.length() - 1; i++) {
|
||||
if (args[i] == "--config-url" &&
|
||||
|
@ -136,11 +137,18 @@ QString MultitrackVideoAutoConfigURL(obs_service_t *service)
|
|||
return args[i + 1];
|
||||
}
|
||||
}
|
||||
OBSDataAutoRelease settings = obs_service_get_settings(service);
|
||||
return obs_data_get_string(
|
||||
settings, "multitrack_video_configuration_url");
|
||||
return std::nullopt;
|
||||
}();
|
||||
|
||||
QString url;
|
||||
if (cli_url.has_value()) {
|
||||
url = *cli_url;
|
||||
} else {
|
||||
OBSDataAutoRelease settings = obs_service_get_settings(service);
|
||||
url = obs_data_get_string(settings,
|
||||
"multitrack_video_configuration_url");
|
||||
}
|
||||
|
||||
blog(LOG_INFO, "Go live URL: %s", url.toUtf8().constData());
|
||||
return url;
|
||||
}
|
||||
|
|
|
@ -14,29 +14,58 @@ constructGoLivePost(QString streamKey,
|
|||
{
|
||||
GoLiveApi::PostData post_data{};
|
||||
post_data.service = "IVS";
|
||||
post_data.schema_version = "2023-05-10";
|
||||
post_data.schema_version = "2024-06-04";
|
||||
post_data.authentication = streamKey.toStdString();
|
||||
|
||||
system_info(post_data.capabilities);
|
||||
|
||||
auto &client = post_data.capabilities.client;
|
||||
auto &client = post_data.client;
|
||||
|
||||
client.name = "obs-studio";
|
||||
client.version = obs_get_version_string();
|
||||
client.vod_track_audio = vod_track_enabled;
|
||||
|
||||
obs_video_info ovi;
|
||||
if (obs_get_video_info(&ovi)) {
|
||||
client.width = ovi.output_width;
|
||||
client.height = ovi.output_height;
|
||||
client.fps_numerator = ovi.fps_num;
|
||||
client.fps_denominator = ovi.fps_den;
|
||||
auto add_codec = [&](const char *codec) {
|
||||
auto it = std::find(std::begin(client.supported_codecs),
|
||||
std::end(client.supported_codecs), codec);
|
||||
if (it != std::end(client.supported_codecs))
|
||||
return;
|
||||
|
||||
client.canvas_width = ovi.base_width;
|
||||
client.canvas_height = ovi.base_height;
|
||||
client.supported_codecs.push_back(codec);
|
||||
};
|
||||
|
||||
const char *encoder_id = nullptr;
|
||||
for (size_t i = 0; obs_enum_encoder_types(i, &encoder_id); i++) {
|
||||
auto codec = obs_get_encoder_codec(encoder_id);
|
||||
if (!codec)
|
||||
continue;
|
||||
|
||||
if (qstricmp(codec, "h264") == 0) {
|
||||
add_codec("h264");
|
||||
#ifdef ENABLE_HEVC
|
||||
} else if (qstricmp(codec, "hevc")) {
|
||||
add_codec("h265");
|
||||
#endif
|
||||
} else if (qstricmp(codec, "av1")) {
|
||||
add_codec("av1");
|
||||
}
|
||||
}
|
||||
|
||||
auto &preferences = post_data.preferences;
|
||||
preferences.vod_track_audio = vod_track_enabled;
|
||||
|
||||
obs_video_info ovi;
|
||||
if (obs_get_video_info(&ovi)) {
|
||||
preferences.width = ovi.output_width;
|
||||
preferences.height = ovi.output_height;
|
||||
preferences.framerate.numerator = ovi.fps_num;
|
||||
preferences.framerate.denominator = ovi.fps_den;
|
||||
|
||||
preferences.canvas_width = ovi.base_width;
|
||||
preferences.canvas_height = ovi.base_height;
|
||||
|
||||
preferences.composition_gpu_index = ovi.adapter;
|
||||
}
|
||||
|
||||
if (maximum_aggregate_bitrate.has_value())
|
||||
preferences.maximum_aggregate_bitrate =
|
||||
maximum_aggregate_bitrate.value();
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
#include <QPointer>
|
||||
#include <QStyle>
|
||||
#include <QAction>
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
#include "obs-app.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
|
||||
void OBSHotkeyEdit::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "lineedit-autoresize.hpp"
|
||||
|
||||
LineEditAutoResize::LineEditAutoResize()
|
||||
LineEditAutoResize::LineEditAutoResize() : m_maxLength(32767)
|
||||
{
|
||||
connect(this, &LineEditAutoResize::textChanged, this,
|
||||
&LineEditAutoResize::checkTextLength);
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
#include <QLayout>
|
||||
#include <QDesktopServices>
|
||||
#include <string>
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
#include "log-viewer.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
|
||||
OBSLogViewer::OBSLogViewer(QWidget *parent)
|
||||
: QDialog(parent),
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include <QTimer>
|
||||
#include <vector>
|
||||
#include <obs.hpp>
|
||||
#include "qt-wrappers.hpp"
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
class Ui_MediaControls;
|
||||
|
||||
|
|
|
@ -109,18 +109,9 @@ using json = nlohmann::json;
|
|||
struct Client {
|
||||
string name = "obs-studio";
|
||||
string version;
|
||||
bool vod_track_audio;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t fps_numerator;
|
||||
uint32_t fps_denominator;
|
||||
uint32_t canvas_width;
|
||||
uint32_t canvas_height;
|
||||
std::vector<string> supported_codecs;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(Client, name, version, vod_track_audio,
|
||||
width, height, fps_numerator,
|
||||
fps_denominator, canvas_width,
|
||||
canvas_height)
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(Client, name, version, supported_codecs)
|
||||
};
|
||||
|
||||
struct Cpu {
|
||||
|
@ -182,35 +173,44 @@ struct System {
|
|||
};
|
||||
|
||||
struct Capabilities {
|
||||
Client client;
|
||||
Cpu cpu;
|
||||
Memory memory;
|
||||
optional<GamingFeatures> gaming_features;
|
||||
System system;
|
||||
optional<std::vector<Gpu>> gpu;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(Capabilities, client, cpu, memory,
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(Capabilities, cpu, memory,
|
||||
gaming_features, system, gpu)
|
||||
};
|
||||
|
||||
struct Preferences {
|
||||
optional<uint64_t> maximum_aggregate_bitrate;
|
||||
optional<uint32_t> maximum_video_tracks;
|
||||
bool vod_track_audio;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
media_frames_per_second framerate;
|
||||
uint32_t canvas_width;
|
||||
uint32_t canvas_height;
|
||||
optional<uint32_t> composition_gpu_index;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(Preferences, maximum_aggregate_bitrate,
|
||||
maximum_video_tracks)
|
||||
maximum_video_tracks, vod_track_audio,
|
||||
width, height, framerate, canvas_width,
|
||||
canvas_height, composition_gpu_index)
|
||||
};
|
||||
|
||||
struct PostData {
|
||||
string service = "IVS";
|
||||
string schema_version = "2023-05-10";
|
||||
string service;
|
||||
string schema_version;
|
||||
string authentication;
|
||||
|
||||
Client client;
|
||||
Capabilities capabilities;
|
||||
Preferences preferences;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(PostData, service, schema_version,
|
||||
authentication, capabilities,
|
||||
authentication, client, capabilities,
|
||||
preferences)
|
||||
};
|
||||
|
||||
|
@ -259,47 +259,29 @@ struct VideoEncoderConfiguration {
|
|||
string type;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t bitrate;
|
||||
optional<media_frames_per_second> framerate;
|
||||
optional<obs_scale_type> gpu_scale_type;
|
||||
json settings;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(VideoEncoderConfiguration,
|
||||
type, width, height,
|
||||
bitrate, framerate,
|
||||
gpu_scale_type)
|
||||
framerate, gpu_scale_type,
|
||||
settings)
|
||||
};
|
||||
|
||||
struct AudioEncoderConfiguration {
|
||||
string codec;
|
||||
uint32_t track_id;
|
||||
uint32_t channels;
|
||||
uint32_t bitrate;
|
||||
json settings;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(AudioEncoderConfiguration, codec,
|
||||
track_id, channels, bitrate)
|
||||
};
|
||||
|
||||
template<typename T> struct EncoderConfiguration {
|
||||
T config;
|
||||
json data;
|
||||
|
||||
friend void to_json(nlohmann::json &nlohmann_json_j,
|
||||
const EncoderConfiguration<T> &nlohmann_json_t)
|
||||
{
|
||||
nlohmann_json_j = nlohmann_json_t.data;
|
||||
to_json(nlohmann_json_j, nlohmann_json_t.config);
|
||||
}
|
||||
friend void from_json(const nlohmann::json &nlohmann_json_j,
|
||||
EncoderConfiguration<T> &nlohmann_json_t)
|
||||
{
|
||||
nlohmann_json_t.data = nlohmann_json_j;
|
||||
nlohmann_json_j.get_to(nlohmann_json_t.config);
|
||||
}
|
||||
track_id, channels, settings)
|
||||
};
|
||||
|
||||
struct AudioConfigurations {
|
||||
std::vector<EncoderConfiguration<AudioEncoderConfiguration>> live;
|
||||
std::vector<EncoderConfiguration<AudioEncoderConfiguration>> vod;
|
||||
std::vector<AudioEncoderConfiguration> live;
|
||||
optional<std::vector<AudioEncoderConfiguration>> vod;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(AudioConfigurations, live,
|
||||
vod)
|
||||
|
@ -309,8 +291,7 @@ struct Config {
|
|||
Meta meta;
|
||||
optional<Status> status;
|
||||
std::vector<IngestEndpoint> ingest_endpoints;
|
||||
std::vector<EncoderConfiguration<VideoEncoderConfiguration>>
|
||||
encoder_configurations;
|
||||
std::vector<VideoEncoderConfiguration> encoder_configurations;
|
||||
AudioConfigurations audio_configurations;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Config, meta, status,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "multitrack-video-error.hpp"
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include "obs-app.hpp"
|
||||
|
||||
MultitrackVideoError MultitrackVideoError::critical(QString error)
|
||||
|
@ -31,9 +32,12 @@ bool MultitrackVideoError::ShowDialog(
|
|||
QTStr("FailedToStartStream.WarningRetryNonMultitrackVideo")
|
||||
.arg(multitrack_video_name));
|
||||
mb.setIcon(QMessageBox::Warning);
|
||||
mb.setStandardButtons(QMessageBox::StandardButton::Yes |
|
||||
QMessageBox::StandardButton::No);
|
||||
return mb.exec() == QMessageBox::StandardButton::Yes;
|
||||
QAbstractButton *yesButton =
|
||||
mb.addButton(QTStr("Yes"), QMessageBox::YesRole);
|
||||
mb.addButton(QTStr("No"), QMessageBox::NoRole);
|
||||
mb.exec();
|
||||
|
||||
return mb.clickedButton() == yesButton;
|
||||
} else if (type == Type::Critical) {
|
||||
mb.setText(error);
|
||||
mb.setIcon(QMessageBox::Critical);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <obs-app.hpp>
|
||||
#include <obs.hpp>
|
||||
#include <remote-text.hpp>
|
||||
#include <window-basic-main.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
|
@ -116,18 +117,20 @@ create_service(const GoLiveApi::Config &go_live_config,
|
|||
|
||||
/* The stream key itself may contain query parameters, such as
|
||||
* "bandwidthtest" that need to be carried over. */
|
||||
QUrl parsed_user_key{in_stream_key};
|
||||
QUrlQuery user_key_query{parsed_user_key};
|
||||
|
||||
QUrl parsed_key{stream_key};
|
||||
QUrlQuery key_query{parsed_key};
|
||||
|
||||
QUrl parsed_url{url};
|
||||
QUrlQuery parsed_query{parsed_url};
|
||||
|
||||
for (const auto &[key, value] : key_query.queryItems())
|
||||
for (const auto &[key, value] : user_key_query.queryItems())
|
||||
parsed_query.addQueryItem(key, value);
|
||||
|
||||
if (!go_live_config.meta.config_id.empty()) {
|
||||
parsed_query.addQueryItem(
|
||||
"obsConfigId",
|
||||
"clientConfigId",
|
||||
QString::fromStdString(go_live_config.meta.config_id));
|
||||
}
|
||||
|
||||
|
@ -259,12 +262,11 @@ static bool encoder_available(const char *type)
|
|||
return false;
|
||||
}
|
||||
|
||||
static OBSEncoderAutoRelease create_video_encoder(
|
||||
DStr &name_buffer, size_t encoder_index,
|
||||
const GoLiveApi::EncoderConfiguration<
|
||||
GoLiveApi::VideoEncoderConfiguration> &encoder_config)
|
||||
static OBSEncoderAutoRelease
|
||||
create_video_encoder(DStr &name_buffer, size_t encoder_index,
|
||||
const GoLiveApi::VideoEncoderConfiguration &encoder_config)
|
||||
{
|
||||
auto encoder_type = encoder_config.config.type.c_str();
|
||||
auto encoder_type = encoder_config.type.c_str();
|
||||
if (!encoder_available(encoder_type)) {
|
||||
blog(LOG_ERROR, "Encoder type '%s' not available",
|
||||
encoder_type);
|
||||
|
@ -276,8 +278,8 @@ static OBSEncoderAutoRelease create_video_encoder(
|
|||
dstr_printf(name_buffer, "multitrack video video encoder %zu",
|
||||
encoder_index);
|
||||
|
||||
OBSDataAutoRelease encoder_settings =
|
||||
obs_data_create_from_json(encoder_config.data.dump().c_str());
|
||||
OBSDataAutoRelease encoder_settings = obs_data_create_from_json(
|
||||
encoder_config.settings.dump().c_str());
|
||||
obs_data_set_bool(encoder_settings, "disable_scenecut", true);
|
||||
|
||||
OBSEncoderAutoRelease video_encoder = obs_video_encoder_create(
|
||||
|
@ -301,22 +303,19 @@ static OBSEncoderAutoRelease create_video_encoder(
|
|||
.arg(name_buffer->array, encoder_type));
|
||||
}
|
||||
|
||||
adjust_video_encoder_scaling(ovi, video_encoder, encoder_config.config,
|
||||
adjust_video_encoder_scaling(ovi, video_encoder, encoder_config,
|
||||
encoder_index);
|
||||
adjust_encoder_frame_rate_divisor(ovi, video_encoder,
|
||||
encoder_config.config, encoder_index);
|
||||
adjust_encoder_frame_rate_divisor(ovi, video_encoder, encoder_config,
|
||||
encoder_index);
|
||||
|
||||
return video_encoder;
|
||||
}
|
||||
|
||||
static OBSEncoderAutoRelease create_audio_encoder(const char *name,
|
||||
const char *audio_encoder_id,
|
||||
uint32_t audio_bitrate,
|
||||
obs_data_t *settings,
|
||||
size_t mixer_idx)
|
||||
{
|
||||
OBSDataAutoRelease settings = obs_data_create();
|
||||
obs_data_set_int(settings, "bitrate", audio_bitrate);
|
||||
|
||||
OBSEncoderAutoRelease audio_encoder = obs_audio_encoder_create(
|
||||
audio_encoder_id, name, settings, mixer_idx, nullptr);
|
||||
if (!audio_encoder) {
|
||||
|
@ -333,11 +332,12 @@ struct OBSOutputs {
|
|||
};
|
||||
|
||||
static OBSOutputs
|
||||
SetupOBSOutput(obs_data_t *dump_stream_to_file_config,
|
||||
SetupOBSOutput(QWidget *parent, const QString &multitrack_video_name,
|
||||
obs_data_t *dump_stream_to_file_config,
|
||||
const GoLiveApi::Config &go_live_config,
|
||||
std::vector<OBSEncoderAutoRelease> &audio_encoders,
|
||||
std::vector<OBSEncoderAutoRelease> &video_encoders,
|
||||
const char *audio_encoder_id,
|
||||
std::shared_ptr<obs_encoder_group_t> &video_encoder_group,
|
||||
const char *audio_encoder_id, size_t main_audio_mixer,
|
||||
std::optional<size_t> vod_track_mixer);
|
||||
static void SetupSignalHandlers(bool recording, MultitrackVideoOutput *self,
|
||||
obs_output_t *output, OBSSignal &start,
|
||||
|
@ -350,7 +350,7 @@ void MultitrackVideoOutput::PrepareStreaming(
|
|||
std::optional<uint32_t> maximum_aggregate_bitrate,
|
||||
std::optional<uint32_t> maximum_video_tracks,
|
||||
std::optional<std::string> custom_config,
|
||||
obs_data_t *dump_stream_to_file_config,
|
||||
obs_data_t *dump_stream_to_file_config, size_t main_audio_mixer,
|
||||
std::optional<size_t> vod_track_mixer)
|
||||
{
|
||||
{
|
||||
|
@ -465,11 +465,13 @@ void MultitrackVideoOutput::PrepareStreaming(
|
|||
const auto &output_config = custom ? *custom : *go_live_config;
|
||||
const auto &service_config = go_live_config ? *go_live_config : *custom;
|
||||
|
||||
auto audio_encoders = std::vector<OBSEncoderAutoRelease>();
|
||||
auto video_encoders = std::vector<OBSEncoderAutoRelease>();
|
||||
auto outputs = SetupOBSOutput(dump_stream_to_file_config, output_config,
|
||||
audio_encoders, video_encoders,
|
||||
audio_encoder_id, vod_track_mixer);
|
||||
std::vector<OBSEncoderAutoRelease> audio_encoders;
|
||||
std::shared_ptr<obs_encoder_group_t> video_encoder_group;
|
||||
auto outputs = SetupOBSOutput(parent, multitrack_video_name,
|
||||
dump_stream_to_file_config, output_config,
|
||||
audio_encoders, video_encoder_group,
|
||||
audio_encoder_id, main_audio_mixer,
|
||||
vod_track_mixer);
|
||||
auto output = std::move(outputs.output);
|
||||
auto recording_output = std::move(outputs.recording_output);
|
||||
if (!output)
|
||||
|
@ -500,13 +502,6 @@ void MultitrackVideoOutput::PrepareStreaming(
|
|||
start_recording, stop_recording,
|
||||
deactivate_recording);
|
||||
|
||||
decltype(video_encoders) recording_video_encoders;
|
||||
recording_video_encoders.reserve(video_encoders.size());
|
||||
for (auto &encoder : video_encoders) {
|
||||
recording_video_encoders.emplace_back(
|
||||
obs_encoder_get_ref(encoder));
|
||||
}
|
||||
|
||||
decltype(audio_encoders) recording_audio_encoders;
|
||||
recording_audio_encoders.reserve(audio_encoders.size());
|
||||
for (auto &encoder : audio_encoders) {
|
||||
|
@ -519,7 +514,7 @@ void MultitrackVideoOutput::PrepareStreaming(
|
|||
current_stream_dump_mutex};
|
||||
current_stream_dump.emplace(OBSOutputObjects{
|
||||
std::move(recording_output),
|
||||
std::move(recording_video_encoders),
|
||||
video_encoder_group,
|
||||
std::move(recording_audio_encoders),
|
||||
nullptr,
|
||||
std::move(start_recording),
|
||||
|
@ -532,7 +527,7 @@ void MultitrackVideoOutput::PrepareStreaming(
|
|||
const std::lock_guard current_lock{current_mutex};
|
||||
current.emplace(OBSOutputObjects{
|
||||
std::move(output),
|
||||
std::move(video_encoders),
|
||||
video_encoder_group,
|
||||
std::move(audio_encoders),
|
||||
std::move(multitrack_video_service),
|
||||
std::move(start_streaming),
|
||||
|
@ -688,16 +683,18 @@ bool MultitrackVideoOutput::HandleIncompatibleSettings(
|
|||
return true;
|
||||
}
|
||||
|
||||
MultitrackVideoOutput::ReleaseOnMainThread(take_current());
|
||||
MultitrackVideoOutput::ReleaseOnMainThread(take_current_stream_dump());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
create_video_encoders(const GoLiveApi::Config &go_live_config,
|
||||
std::vector<OBSEncoderAutoRelease> &video_encoders,
|
||||
std::shared_ptr<obs_encoder_group_t> &video_encoder_group,
|
||||
obs_output_t *output, obs_output_t *recording_output)
|
||||
{
|
||||
DStr video_encoder_name_buffer;
|
||||
obs_encoder_t *first_encoder = nullptr;
|
||||
if (go_live_config.encoder_configurations.empty()) {
|
||||
blog(LOG_WARNING,
|
||||
"MultitrackVideoOutput: Missing video encoder configurations");
|
||||
|
@ -705,6 +702,11 @@ create_video_encoders(const GoLiveApi::Config &go_live_config,
|
|||
QTStr("FailedToStartStream.MissingEncoderConfigs"));
|
||||
}
|
||||
|
||||
std::shared_ptr<obs_encoder_group_t> encoder_group(
|
||||
obs_encoder_group_create(), obs_encoder_group_destroy);
|
||||
if (!encoder_group)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < go_live_config.encoder_configurations.size();
|
||||
i++) {
|
||||
auto encoder = create_video_encoder(
|
||||
|
@ -713,19 +715,16 @@ create_video_encoders(const GoLiveApi::Config &go_live_config,
|
|||
if (!encoder)
|
||||
return false;
|
||||
|
||||
if (!first_encoder)
|
||||
first_encoder = encoder;
|
||||
else
|
||||
obs_encoder_group_keyframe_aligned_encoders(
|
||||
first_encoder, encoder);
|
||||
if (!obs_encoder_set_group(encoder, encoder_group.get()))
|
||||
return false;
|
||||
|
||||
obs_output_set_video_encoder2(output, encoder, i);
|
||||
if (recording_output)
|
||||
obs_output_set_video_encoder2(recording_output, encoder,
|
||||
i);
|
||||
video_encoders.emplace_back(std::move(encoder));
|
||||
}
|
||||
|
||||
video_encoder_group = encoder_group;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -733,9 +732,47 @@ static void
|
|||
create_audio_encoders(const GoLiveApi::Config &go_live_config,
|
||||
std::vector<OBSEncoderAutoRelease> &audio_encoders,
|
||||
obs_output_t *output, obs_output_t *recording_output,
|
||||
const char *audio_encoder_id,
|
||||
std::optional<size_t> vod_track_mixer)
|
||||
const char *audio_encoder_id, size_t main_audio_mixer,
|
||||
std::optional<size_t> vod_track_mixer,
|
||||
std::vector<speaker_layout> &speaker_layouts,
|
||||
speaker_layout ¤t_layout)
|
||||
{
|
||||
speaker_layout speakers = SPEAKERS_UNKNOWN;
|
||||
obs_audio_info oai = {};
|
||||
if (obs_get_audio_info(&oai))
|
||||
speakers = oai.speakers;
|
||||
|
||||
current_layout = speakers;
|
||||
|
||||
auto sanitize_audio_channels = [&](obs_encoder_t *encoder,
|
||||
uint32_t channels) {
|
||||
speaker_layout target_speakers = SPEAKERS_UNKNOWN;
|
||||
for (size_t i = 0; i <= (size_t)SPEAKERS_7POINT1; i++) {
|
||||
if (get_audio_channels((speaker_layout)i) != channels)
|
||||
continue;
|
||||
|
||||
target_speakers = (speaker_layout)i;
|
||||
break;
|
||||
}
|
||||
if (target_speakers == SPEAKERS_UNKNOWN) {
|
||||
blog(LOG_WARNING,
|
||||
"MultitrackVideoOutput: Could not find "
|
||||
"speaker layout for %" PRIu32 "channels "
|
||||
"while configuring encoder '%s'",
|
||||
channels, obs_encoder_get_name(encoder));
|
||||
return;
|
||||
}
|
||||
if (speakers != SPEAKERS_UNKNOWN &&
|
||||
(channels > get_audio_channels(speakers) ||
|
||||
speakers == target_speakers))
|
||||
return;
|
||||
|
||||
auto it = std::find(std::begin(speaker_layouts),
|
||||
std::end(speaker_layouts), target_speakers);
|
||||
if (it == std::end(speaker_layouts))
|
||||
speaker_layouts.push_back(target_speakers);
|
||||
};
|
||||
|
||||
using encoder_configs_type =
|
||||
decltype(go_live_config.audio_configurations.live);
|
||||
DStr encoder_name_buffer;
|
||||
|
@ -755,11 +792,16 @@ create_audio_encoders(const GoLiveApi::Config &go_live_config,
|
|||
for (size_t i = 0; i < configs.size(); i++) {
|
||||
dstr_printf(encoder_name_buffer, "%s %zu", name_prefix,
|
||||
i);
|
||||
OBSDataAutoRelease settings = obs_data_create_from_json(
|
||||
configs[i].settings.dump().c_str());
|
||||
OBSEncoderAutoRelease audio_encoder =
|
||||
create_audio_encoder(encoder_name_buffer->array,
|
||||
audio_encoder_id,
|
||||
configs[i].config.bitrate,
|
||||
audio_encoder_id, settings,
|
||||
mixer_idx);
|
||||
|
||||
sanitize_audio_channels(audio_encoder,
|
||||
configs[i].channels);
|
||||
|
||||
obs_output_set_audio_encoder(output, audio_encoder,
|
||||
output_encoder_index);
|
||||
if (recording_output)
|
||||
|
@ -772,24 +814,99 @@ create_audio_encoders(const GoLiveApi::Config &go_live_config,
|
|||
};
|
||||
|
||||
create_encoders("multitrack video live audio",
|
||||
go_live_config.audio_configurations.live, 0);
|
||||
go_live_config.audio_configurations.live,
|
||||
main_audio_mixer);
|
||||
|
||||
if (!vod_track_mixer.has_value())
|
||||
return;
|
||||
|
||||
// we already check for empty inside of `create_encoders`
|
||||
encoder_configs_type empty = {};
|
||||
create_encoders("multitrack video vod audio",
|
||||
go_live_config.audio_configurations.vod,
|
||||
go_live_config.audio_configurations.vod.value_or(empty),
|
||||
*vod_track_mixer);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static const char *speaker_layout_to_string(speaker_layout layout)
|
||||
{
|
||||
switch (layout) {
|
||||
case SPEAKERS_MONO:
|
||||
return "Mono";
|
||||
case SPEAKERS_2POINT1:
|
||||
return "2.1";
|
||||
case SPEAKERS_4POINT0:
|
||||
return "4.0";
|
||||
case SPEAKERS_4POINT1:
|
||||
return "4.1";
|
||||
case SPEAKERS_5POINT1:
|
||||
return "5.1";
|
||||
case SPEAKERS_7POINT1:
|
||||
return "7.1";
|
||||
case SPEAKERS_UNKNOWN:
|
||||
case SPEAKERS_STEREO:
|
||||
return "Stereo";
|
||||
}
|
||||
|
||||
return "Stereo";
|
||||
}
|
||||
|
||||
static void handle_speaker_layout_issues(
|
||||
QWidget *parent, const QString &multitrack_video_name,
|
||||
const std::vector<speaker_layout> &requested_layouts,
|
||||
speaker_layout layout)
|
||||
{
|
||||
if (requested_layouts.empty())
|
||||
return;
|
||||
|
||||
QString message;
|
||||
if (requested_layouts.size() == 1) {
|
||||
message =
|
||||
QTStr("MultitrackVideo.IncompatibleSettings.AudioChannelsSingle")
|
||||
.arg(QTStr(speaker_layout_to_string(
|
||||
requested_layouts.front())));
|
||||
} else {
|
||||
message =
|
||||
QTStr("MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple")
|
||||
.arg(multitrack_video_name);
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(
|
||||
parent,
|
||||
[&] {
|
||||
QMessageBox mb(parent);
|
||||
mb.setIcon(QMessageBox::Critical);
|
||||
mb.setWindowTitle(QTStr(
|
||||
"MultitrackVideo.IncompatibleSettings.Title"));
|
||||
mb.setText(
|
||||
QTStr("MultitrackVideo.IncompatibleSettings.AudioChannels")
|
||||
.arg(multitrack_video_name)
|
||||
.arg(QTStr(speaker_layout_to_string(
|
||||
layout)))
|
||||
.arg(message));
|
||||
|
||||
mb.setStandardButtons(
|
||||
QMessageBox::StandardButton::Cancel);
|
||||
|
||||
mb.exec();
|
||||
},
|
||||
BlockingConnectionTypeFor(parent));
|
||||
|
||||
blog(LOG_INFO,
|
||||
"MultitrackVideoOutput: Attempted to start stream with incompatible "
|
||||
"audio channel setting. Action taken: cancel");
|
||||
|
||||
throw MultitrackVideoError::cancel();
|
||||
}
|
||||
|
||||
static OBSOutputs
|
||||
SetupOBSOutput(obs_data_t *dump_stream_to_file_config,
|
||||
SetupOBSOutput(QWidget *parent, const QString &multitrack_video_name,
|
||||
obs_data_t *dump_stream_to_file_config,
|
||||
const GoLiveApi::Config &go_live_config,
|
||||
std::vector<OBSEncoderAutoRelease> &audio_encoders,
|
||||
std::vector<OBSEncoderAutoRelease> &video_encoders,
|
||||
const char *audio_encoder_id,
|
||||
std::shared_ptr<obs_encoder_group_t> &video_encoder_group,
|
||||
const char *audio_encoder_id, size_t main_audio_mixer,
|
||||
std::optional<size_t> vod_track_mixer)
|
||||
{
|
||||
|
||||
|
@ -799,13 +916,19 @@ SetupOBSOutput(obs_data_t *dump_stream_to_file_config,
|
|||
recording_output =
|
||||
create_recording_output(dump_stream_to_file_config);
|
||||
|
||||
if (!create_video_encoders(go_live_config, video_encoders, output,
|
||||
if (!create_video_encoders(go_live_config, video_encoder_group, output,
|
||||
recording_output))
|
||||
return {nullptr, nullptr};
|
||||
|
||||
std::vector<speaker_layout> requested_speaker_layouts;
|
||||
speaker_layout current_layout = SPEAKERS_UNKNOWN;
|
||||
create_audio_encoders(go_live_config, audio_encoders, output,
|
||||
recording_output, audio_encoder_id,
|
||||
vod_track_mixer);
|
||||
main_audio_mixer, vod_track_mixer,
|
||||
requested_speaker_layouts, current_layout);
|
||||
|
||||
handle_speaker_layout_issues(parent, multitrack_video_name,
|
||||
requested_speaker_layouts, current_layout);
|
||||
|
||||
return {std::move(output), std::move(recording_output)};
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ public:
|
|||
std::optional<uint32_t> maximum_video_tracks,
|
||||
std::optional<std::string> custom_config,
|
||||
obs_data_t *dump_stream_to_file_config,
|
||||
size_t main_audio_mixer,
|
||||
std::optional<size_t> vod_track_mixer);
|
||||
signal_handler_t *StreamingSignalHandler();
|
||||
void StartedStreaming();
|
||||
|
@ -53,7 +54,7 @@ public:
|
|||
private:
|
||||
struct OBSOutputObjects {
|
||||
OBSOutputAutoRelease output_;
|
||||
std::vector<OBSEncoderAutoRelease> video_encoders_;
|
||||
std::shared_ptr<obs_encoder_group_t> video_encoder_group_;
|
||||
std::vector<OBSEncoderAutoRelease> audio_encoders_;
|
||||
OBSServiceAutoRelease multitrack_video_service_;
|
||||
OBSSignal start_signal, stop_signal, deactivate_signal;
|
||||
|
|
|
@ -417,7 +417,7 @@ static vector<OBSThemeVariable> ParseThemeVariables(const char *themeData)
|
|||
void OBSApp::FindThemes()
|
||||
{
|
||||
string themeDir;
|
||||
themeDir.resize(512);
|
||||
unique_ptr<OBSTheme> theme;
|
||||
|
||||
QStringList filters;
|
||||
filters << "*.obt" // OBS Base Theme
|
||||
|
@ -425,30 +425,27 @@ void OBSApp::FindThemes()
|
|||
<< "*.oha" // OBS High-contrast Adjustment layer
|
||||
;
|
||||
|
||||
GetDataFilePath("themes/", themeDir);
|
||||
QDirIterator it(QString::fromStdString(themeDir), filters, QDir::Files);
|
||||
while (it.hasNext()) {
|
||||
theme.reset(ParseThemeMeta(it.next()));
|
||||
if (theme && !themes.contains(theme->id))
|
||||
themes[theme->id] = std::move(*theme);
|
||||
}
|
||||
|
||||
themeDir.resize(1024);
|
||||
if (GetConfigPath(themeDir.data(), themeDir.capacity(),
|
||||
"obs-studio/themes/") > 0) {
|
||||
QDirIterator it(QT_UTF8(themeDir.c_str()), filters,
|
||||
QDir::Files);
|
||||
|
||||
while (it.hasNext()) {
|
||||
OBSTheme *theme = ParseThemeMeta(it.next());
|
||||
theme.reset(ParseThemeMeta(it.next()));
|
||||
if (theme && !themes.contains(theme->id))
|
||||
themes[theme->id] = std::move(*theme);
|
||||
else
|
||||
delete theme;
|
||||
}
|
||||
}
|
||||
|
||||
GetDataFilePath("themes/", themeDir);
|
||||
QDirIterator it(QString::fromStdString(themeDir), filters, QDir::Files);
|
||||
while (it.hasNext()) {
|
||||
OBSTheme *theme = ParseThemeMeta(it.next());
|
||||
if (theme && !themes.contains(theme->id))
|
||||
themes[theme->id] = std::move(*theme);
|
||||
else
|
||||
delete theme;
|
||||
}
|
||||
|
||||
/* Build dependency tree for all themes, removing ones that have items missing. */
|
||||
QSet<QString> invalid;
|
||||
|
||||
|
@ -485,6 +482,16 @@ void OBSApp::FindThemes()
|
|||
break;
|
||||
}
|
||||
|
||||
if (parent->id == theme.id ||
|
||||
theme.dependencies.contains(parent->id)) {
|
||||
blog(LOG_ERROR,
|
||||
R"(Dependency chain of "%s" ("%s") contains recursion!)",
|
||||
QT_TO_UTF8(theme.id),
|
||||
QT_TO_UTF8(parent->id));
|
||||
invalid.insert(theme.id);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Mark this theme as a variant of first parent that is a base theme. */
|
||||
if (!theme.isBaseTheme && parent->isBaseTheme &&
|
||||
theme.parent.isEmpty())
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include <util/cf-parser.h>
|
||||
#include <obs-config.h>
|
||||
#include <obs.hpp>
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <slider-ignorewheel.hpp>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
|
@ -38,7 +40,6 @@
|
|||
#include <QProcess>
|
||||
#include <QAccessible>
|
||||
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "obs-app.hpp"
|
||||
#include "obs-proxy-style.hpp"
|
||||
#include "log-viewer.hpp"
|
||||
|
@ -57,6 +58,7 @@
|
|||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <filesystem>
|
||||
#include <util/windows/win-version.h>
|
||||
#else
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
|
@ -350,7 +352,6 @@ static inline bool too_many_repeated_entries(fstream &logFile, const char *msg,
|
|||
static mutex log_mutex;
|
||||
static const char *last_msg_ptr = nullptr;
|
||||
static int last_char_sum = 0;
|
||||
static char cmp_str[8192];
|
||||
static int rep_count = 0;
|
||||
|
||||
int new_sum = sum_chars(output_str);
|
||||
|
@ -376,8 +377,6 @@ static inline bool too_many_repeated_entries(fstream &logFile, const char *msg,
|
|||
}
|
||||
|
||||
last_msg_ptr = msg;
|
||||
strncpy(cmp_str, output_str, sizeof(cmp_str));
|
||||
cmp_str[sizeof(cmp_str) - 1] = 0;
|
||||
last_char_sum = new_sum;
|
||||
rep_count = 0;
|
||||
|
||||
|
@ -2904,6 +2903,35 @@ void OBSApp::commitData(QSessionManager &manager)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
static constexpr char vcRunErrorTitle[] = "Outdated Visual C++ Runtime";
|
||||
static constexpr char vcRunErrorMsg[] =
|
||||
"OBS Studio requires a newer version of the Microsoft Visual C++ "
|
||||
"Redistributables.\n\nYou will now be directed to the download page.";
|
||||
static constexpr char vcRunInstallerUrl[] =
|
||||
"https://obsproject.com/visual-studio-2022-runtimes";
|
||||
|
||||
static bool vc_runtime_outdated()
|
||||
{
|
||||
win_version_info ver;
|
||||
if (!get_dll_ver(L"msvcp140.dll", &ver))
|
||||
return true;
|
||||
/* Major is always 14 (hence 140.dll), so we only care about minor. */
|
||||
if (ver.minor >= 40)
|
||||
return false;
|
||||
|
||||
int choice = MessageBoxA(NULL, vcRunErrorMsg, vcRunErrorTitle,
|
||||
MB_OKCANCEL | MB_ICONERROR | MB_TASKMODAL);
|
||||
if (choice == IDOK) {
|
||||
/* Open the URL in the default browser. */
|
||||
ShellExecuteA(NULL, "open", vcRunInstallerUrl, NULL, NULL,
|
||||
SW_SHOWNORMAL);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#ifndef _WIN32
|
||||
|
@ -2930,6 +2958,9 @@ int main(int argc, char *argv[])
|
|||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
// Abort as early as possible if MSVC runtime is outdated
|
||||
if (vc_runtime_outdated())
|
||||
return 1;
|
||||
// Try to keep this as early as possible
|
||||
install_dll_blocklist_hook();
|
||||
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
#include <sstream>
|
||||
#include "obs-config.h"
|
||||
#include "obs-app.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "platform.hpp"
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <util/windows/win-version.h>
|
||||
#include <util/platform.h>
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
#include "qt-display.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "display-helpers.hpp"
|
||||
#include <QWindow>
|
||||
#include <QScreen>
|
||||
#include <QResizeEvent>
|
||||
#include <QShowEvent>
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <obs-config.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -13,6 +13,14 @@
|
|||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
#include <obs-nix-platform.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_WAYLAND
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#endif
|
||||
|
||||
class SurfaceEventFilter : public QObject {
|
||||
OBSQTDisplay *display;
|
||||
|
||||
|
@ -62,6 +70,38 @@ static inline QColor rgba_to_color(uint32_t rgba)
|
|||
(rgba >> 16) & 0xFF, (rgba >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
static bool QTToGSWindow(QWindow *window, gs_window &gswindow)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
#ifdef _WIN32
|
||||
gswindow.hwnd = (HWND)window->winId();
|
||||
#elif __APPLE__
|
||||
gswindow.view = (id)window->winId();
|
||||
#else
|
||||
switch (obs_get_nix_platform()) {
|
||||
case OBS_NIX_PLATFORM_X11_EGL:
|
||||
gswindow.id = window->winId();
|
||||
gswindow.display = obs_get_nix_platform_display();
|
||||
break;
|
||||
#ifdef ENABLE_WAYLAND
|
||||
case OBS_NIX_PLATFORM_WAYLAND: {
|
||||
QPlatformNativeInterface *native =
|
||||
QGuiApplication::platformNativeInterface();
|
||||
gswindow.display =
|
||||
native->nativeResourceForWindow("surface", window);
|
||||
success = gswindow.display != nullptr;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return success;
|
||||
}
|
||||
|
||||
OBSQTDisplay::OBSQTDisplay(QWidget *parent, Qt::WindowFlags flags)
|
||||
: QWidget(parent, flags)
|
||||
{
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
******************************************************************************/
|
||||
|
||||
#include <util/curl/curl-helper.h>
|
||||
#include <qt-wrappers.hpp>
|
||||
#include "obs-app.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "remote-text.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#include "window-basic-main.hpp"
|
||||
#include "obs-app.hpp"
|
||||
#include "source-tree.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "platform.hpp"
|
||||
#include "source-label.hpp"
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <obs-frontend-api.h>
|
||||
#include <obs.h>
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
#include "update-helpers.hpp"
|
||||
#include "shared-update.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "mac-update.hpp"
|
||||
#include "obs-app.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <QMessageBox>
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
#include "shared-update.hpp"
|
||||
#include "update-window.hpp"
|
||||
#include "remote-text.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "win-update.hpp"
|
||||
#include "obs-app.hpp"
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <string>
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#include "visibility-item-widget.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "obs-app.hpp"
|
||||
#include "source-label.hpp"
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <QListWidget>
|
||||
#include <QLineEdit>
|
||||
#include <QHBoxLayout>
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
#include "window-basic-main.hpp"
|
||||
#include "volume-control.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "obs-app.hpp"
|
||||
#include "mute-checkbox.hpp"
|
||||
#include "absolute-slider.hpp"
|
||||
#include "source-label.hpp"
|
||||
|
||||
#include <slider-ignorewheel.hpp>
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <QFontDatabase>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
|
@ -249,6 +251,10 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical)
|
|||
volLabel->setObjectName("volLabel");
|
||||
volLabel->setAlignment(Qt::AlignCenter);
|
||||
|
||||
#ifdef __APPLE__
|
||||
mute->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||
#endif
|
||||
|
||||
QString sourceName = obs_source_get_name(source);
|
||||
setObjectName(sourceName);
|
||||
|
||||
|
@ -257,6 +263,8 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical)
|
|||
config->setProperty("themeID", "menuIconSmall");
|
||||
config->setAutoDefault(false);
|
||||
|
||||
config->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
|
||||
config->setAccessibleName(
|
||||
QTStr("VolControl.Properties").arg(sourceName));
|
||||
|
||||
|
@ -294,24 +302,16 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical)
|
|||
controlLayout->setContentsMargins(0, 0, 0, 0);
|
||||
controlLayout->setSpacing(0);
|
||||
|
||||
controlLayout->setAlignment(mute, Qt::AlignVCenter);
|
||||
// Add Headphone (audio monitoring) widget here
|
||||
controlLayout->addWidget(mute);
|
||||
controlLayout->addItem(
|
||||
new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding,
|
||||
QSizePolicy::Minimum));
|
||||
|
||||
if (showConfig) {
|
||||
controlLayout->addWidget(config);
|
||||
controlLayout->setAlignment(config, Qt::AlignVCenter);
|
||||
}
|
||||
|
||||
meterLayout->setContentsMargins(0, 0, 0, 0);
|
||||
meterLayout->setSpacing(0);
|
||||
meterLayout->addWidget(slider);
|
||||
meterLayout->addItem(
|
||||
new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding,
|
||||
QSizePolicy::Minimum));
|
||||
meterLayout->addWidget(volMeter);
|
||||
|
||||
meterFrame->setLayout(meterLayout);
|
||||
|
@ -338,9 +338,10 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical)
|
|||
setMaximumWidth(110);
|
||||
} else {
|
||||
QHBoxLayout *textLayout = new QHBoxLayout;
|
||||
QHBoxLayout *controlLayout = new QHBoxLayout;
|
||||
QFrame *meterFrame = new QFrame;
|
||||
QHBoxLayout *meterLayout = new QHBoxLayout;
|
||||
QHBoxLayout *botLayout = new QHBoxLayout;
|
||||
QVBoxLayout *meterLayout = new QVBoxLayout;
|
||||
QVBoxLayout *buttonLayout = new QVBoxLayout;
|
||||
|
||||
volMeter = new VolumeMeter(nullptr, obs_volmeter, false);
|
||||
volMeter->setSizePolicy(QSizePolicy::MinimumExpanding,
|
||||
|
@ -362,23 +363,25 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical)
|
|||
meterLayout->setContentsMargins(0, 0, 0, 0);
|
||||
meterLayout->setSpacing(0);
|
||||
|
||||
if (showConfig) {
|
||||
meterLayout->addWidget(config);
|
||||
meterLayout->setAlignment(config, Qt::AlignVCenter);
|
||||
}
|
||||
meterLayout->addWidget(volMeter);
|
||||
meterLayout->addWidget(slider);
|
||||
|
||||
botLayout->setContentsMargins(0, 0, 0, 0);
|
||||
botLayout->setSpacing(0);
|
||||
botLayout->addWidget(mute);
|
||||
botLayout->addWidget(slider);
|
||||
buttonLayout->setContentsMargins(0, 0, 0, 0);
|
||||
buttonLayout->setSpacing(0);
|
||||
|
||||
botLayout->setAlignment(slider, Qt::AlignVCenter);
|
||||
botLayout->setAlignment(mute, Qt::AlignVCenter);
|
||||
if (showConfig) {
|
||||
buttonLayout->addWidget(config);
|
||||
}
|
||||
buttonLayout->addItem(
|
||||
new QSpacerItem(0, 0, QSizePolicy::Minimum,
|
||||
QSizePolicy::MinimumExpanding));
|
||||
buttonLayout->addWidget(mute);
|
||||
|
||||
controlLayout->addItem(buttonLayout);
|
||||
controlLayout->addWidget(meterFrame);
|
||||
|
||||
mainLayout->addItem(textLayout);
|
||||
mainLayout->addWidget(meterFrame);
|
||||
mainLayout->addItem(botLayout);
|
||||
mainLayout->addItem(controlLayout);
|
||||
|
||||
volMeter->setFocusProxy(slider);
|
||||
}
|
||||
|
|
|
@ -26,10 +26,22 @@ target_compile_definitions(updater PRIVATE NOMINMAX "PSAPI_VERSION=2")
|
|||
|
||||
target_include_directories(updater PRIVATE "${CMAKE_SOURCE_DIR}/libobs" "${CMAKE_SOURCE_DIR}/UI/win-update")
|
||||
|
||||
target_link_libraries(updater PRIVATE OBS::blake2 nlohmann_json::nlohmann_json zstd::libzstd_static comctl32 shell32
|
||||
winhttp)
|
||||
target_link_libraries(
|
||||
updater
|
||||
PRIVATE OBS::blake2_static
|
||||
nlohmann_json::nlohmann_json
|
||||
zstd::libzstd_static
|
||||
comctl32
|
||||
shell32
|
||||
version
|
||||
winhttp
|
||||
wintrust)
|
||||
|
||||
# zstd is hardcoded with /DEFAULTLIB:LIBCMT
|
||||
target_link_options(updater PRIVATE /NODEFAULTLIB:LIBCMT)
|
||||
target_link_options(updater PRIVATE $<$<CONFIG:DEBUG>:/NODEFAULTLIB:LIBCMT>)
|
||||
|
||||
set_target_properties(updater PROPERTIES FOLDER frontend OUTPUT_NAME updater)
|
||||
set_target_properties(
|
||||
updater
|
||||
PROPERTIES FOLDER frontend
|
||||
OUTPUT_NAME updater
|
||||
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
|
|
|
@ -36,7 +36,15 @@ if(MSVC)
|
|||
target_link_options(updater PRIVATE "LINKER:/IGNORE:4098")
|
||||
endif()
|
||||
|
||||
target_link_libraries(updater PRIVATE OBS::blake2 nlohmann_json::nlohmann_json zstd::libzstd_static comctl32 shell32
|
||||
winhttp)
|
||||
target_link_libraries(
|
||||
updater
|
||||
PRIVATE OBS::blake2
|
||||
nlohmann_json::nlohmann_json
|
||||
zstd::libzstd_static
|
||||
comctl32
|
||||
shell32
|
||||
version
|
||||
winhttp
|
||||
wintrust)
|
||||
|
||||
set_target_properties(updater PROPERTIES FOLDER "frontend")
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include "manifest.hpp"
|
||||
|
||||
#include <psapi.h>
|
||||
#include <WinTrust.h>
|
||||
#include <SoftPub.h>
|
||||
|
||||
#include <util/windows/CoTaskMemPtr.hpp>
|
||||
|
||||
|
@ -39,6 +41,9 @@ constexpr const wchar_t *kCDNUpdateBaseUrl =
|
|||
L"https://cdn-fastly.obsproject.com/update_studio";
|
||||
constexpr const wchar_t *kPatchManifestURL =
|
||||
L"https://obsproject.com/update_studio/getpatchmanifest";
|
||||
constexpr const wchar_t *kVSRedistURL =
|
||||
L"https://aka.ms/vs/17/release/vc_redist.x64.exe";
|
||||
constexpr const wchar_t *kMSHostname = L"aka.ms";
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
|
@ -72,44 +77,28 @@ void FreeWinHttpHandle(HINTERNET handle)
|
|||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
static inline bool HasVS2019Redist2()
|
||||
static bool IsVSRedistOutdated()
|
||||
{
|
||||
wchar_t base[MAX_PATH];
|
||||
wchar_t path[MAX_PATH];
|
||||
WIN32_FIND_DATAW wfd;
|
||||
HANDLE handle;
|
||||
VS_FIXEDFILEINFO *info = nullptr;
|
||||
UINT len = 0;
|
||||
vector<std::byte> buf;
|
||||
|
||||
SHGetFolderPathW(NULL, CSIDL_SYSTEM, NULL, SHGFP_TYPE_CURRENT, base);
|
||||
const wchar_t vc_dll[] = L"msvcp140";
|
||||
|
||||
#define check_dll_installed(dll) \
|
||||
do { \
|
||||
StringCbCopyW(path, sizeof(path), base); \
|
||||
StringCbCatW(path, sizeof(path), L"\\" dll ".dll"); \
|
||||
handle = FindFirstFileW(path, &wfd); \
|
||||
if (handle == INVALID_HANDLE_VALUE) { \
|
||||
return false; \
|
||||
} else { \
|
||||
FindClose(handle); \
|
||||
} \
|
||||
} while (false)
|
||||
auto size = GetFileVersionInfoSize(vc_dll, nullptr);
|
||||
if (!size)
|
||||
return true;
|
||||
|
||||
check_dll_installed(L"msvcp140");
|
||||
check_dll_installed(L"vcruntime140");
|
||||
check_dll_installed(L"vcruntime140_1");
|
||||
buf.resize(size);
|
||||
if (!GetFileVersionInfo(vc_dll, 0, size, buf.data()))
|
||||
return true;
|
||||
|
||||
#undef check_dll_installed
|
||||
bool success = VerQueryValue(buf.data(), L"\\",
|
||||
reinterpret_cast<LPVOID *>(&info), &len);
|
||||
if (!success || !info || !len)
|
||||
return true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HasVS2019Redist()
|
||||
{
|
||||
PVOID old = nullptr;
|
||||
bool redirect = !!Wow64DisableWow64FsRedirection(&old);
|
||||
bool success = HasVS2019Redist2();
|
||||
if (redirect)
|
||||
Wow64RevertWow64FsRedirection(old);
|
||||
return success;
|
||||
return LOWORD(info->dwFileVersionMS) < 40;
|
||||
}
|
||||
|
||||
static void Status(const wchar_t *fmt, ...)
|
||||
|
@ -1139,7 +1128,7 @@ try {
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool UpdateVS2019Redists(const string &vc_redist_hash)
|
||||
static bool UpdateVSRedists()
|
||||
{
|
||||
/* ------------------------------------------ *
|
||||
* Initialize session */
|
||||
|
@ -1153,7 +1142,7 @@ static bool UpdateVS2019Redists(const string &vc_redist_hash)
|
|||
WINHTTP_NO_PROXY_NAME,
|
||||
WINHTTP_NO_PROXY_BYPASS, 0);
|
||||
if (!hSession) {
|
||||
Status(L"Update failed: Couldn't open obsproject.com");
|
||||
Status(L"VC Redist Update failed: Couldn't create session");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1163,10 +1152,10 @@ static bool UpdateVS2019Redists(const string &vc_redist_hash)
|
|||
WinHttpSetOption(hSession, WINHTTP_OPTION_DECOMPRESSION,
|
||||
(LPVOID)&compressionFlags, sizeof(compressionFlags));
|
||||
|
||||
HttpHandle hConnect = WinHttpConnect(hSession, kCDNHostname,
|
||||
HttpHandle hConnect = WinHttpConnect(hSession, kMSHostname,
|
||||
INTERNET_DEFAULT_HTTPS_PORT, 0);
|
||||
if (!hConnect) {
|
||||
Status(L"Update failed: Couldn't connect to %S", kCDNHostname);
|
||||
Status(L"Update failed: Couldn't connect to %S", kMSHostname);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1180,54 +1169,50 @@ static bool UpdateVS2019Redists(const string &vc_redist_hash)
|
|||
/* ------------------------------------------ *
|
||||
* Download redist */
|
||||
|
||||
Status(L"Downloading Visual C++ 2019 Redistributable");
|
||||
|
||||
wstring sourceURL =
|
||||
L"https://cdn-fastly.obsproject.com/downloads/VC_redist.x64.exe";
|
||||
Status(L"Downloading Visual C++ Redistributable");
|
||||
|
||||
wstring destPath;
|
||||
destPath += tempPath;
|
||||
destPath += L"\\VC_redist.x64.exe";
|
||||
|
||||
if (!HTTPGetFile(hConnect, sourceURL.c_str(), destPath.c_str(),
|
||||
if (!HTTPGetFile(hConnect, kVSRedistURL, destPath.c_str(),
|
||||
L"Accept-Encoding: gzip", &responseCode)) {
|
||||
|
||||
DeleteFile(destPath.c_str());
|
||||
Status(L"Update failed: Could not download "
|
||||
L"%s (error code %d)",
|
||||
L"Visual C++ 2019 Redistributable", responseCode);
|
||||
L"Visual C++ Redistributable", responseCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ------------------------------------------ *
|
||||
* Get expected hash */
|
||||
* Verify file signature */
|
||||
|
||||
B2Hash expectedHash;
|
||||
StringToHash(vc_redist_hash, expectedHash);
|
||||
GUID action = WINTRUST_ACTION_GENERIC_VERIFY_V2;
|
||||
|
||||
/* ------------------------------------------ *
|
||||
* Get download hash */
|
||||
WINTRUST_FILE_INFO fileInfo = {};
|
||||
fileInfo.cbStruct = sizeof(fileInfo);
|
||||
fileInfo.pcwszFilePath = destPath.c_str();
|
||||
|
||||
B2Hash downloadHash;
|
||||
if (!CalculateFileHash(destPath.c_str(), downloadHash)) {
|
||||
WINTRUST_DATA data = {};
|
||||
data.cbStruct = sizeof(data);
|
||||
data.dwUIChoice = WTD_UI_NONE;
|
||||
data.dwUnionChoice = WTD_CHOICE_FILE;
|
||||
data.dwStateAction = WTD_STATEACTION_VERIFY;
|
||||
data.pFile = &fileInfo;
|
||||
|
||||
LONG result = WinVerifyTrust(nullptr, &action, &data);
|
||||
|
||||
if (result != ERROR_SUCCESS) {
|
||||
Status(L"Update failed: Signature verification failed for "
|
||||
L"%s (error code %d / %d)",
|
||||
L"Visual C++ Redistributable", result, GetLastError());
|
||||
DeleteFile(destPath.c_str());
|
||||
Status(L"Update failed: Couldn't verify integrity of %s",
|
||||
L"Visual C++ 2019 Redistributable");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ------------------------------------------ *
|
||||
* If hashes do not match, integrity failed */
|
||||
|
||||
if (downloadHash == expectedHash) {
|
||||
DeleteFile(destPath.c_str());
|
||||
Status(L"Update failed: Couldn't verify integrity of %s",
|
||||
L"Visual C++ 2019 Redistributable");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ------------------------------------------ *
|
||||
* If hashes match, install redist */
|
||||
* If verification succeeded, install redist */
|
||||
|
||||
wchar_t commandline[MAX_PATH + MAX_PATH];
|
||||
StringCbPrintf(commandline, sizeof(commandline),
|
||||
|
@ -1241,7 +1226,7 @@ static bool UpdateVS2019Redists(const string &vc_redist_hash)
|
|||
nullptr, false, CREATE_NO_WINDOW,
|
||||
nullptr, nullptr, &si, &pi);
|
||||
if (success) {
|
||||
Status(L"Installing %s...", L"Visual C++ 2019 Redistributable");
|
||||
Status(L"Installing %s...", L"Visual C++ Redistributable");
|
||||
|
||||
CloseHandle(pi.hThread);
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
|
@ -1249,7 +1234,7 @@ static bool UpdateVS2019Redists(const string &vc_redist_hash)
|
|||
} else {
|
||||
Status(L"Update failed: Could not execute "
|
||||
L"%s (error code %d)",
|
||||
L"Visual C++ 2019 Redistributable", (int)GetLastError());
|
||||
L"Visual C++ Redistributable", (int)GetLastError());
|
||||
}
|
||||
|
||||
DeleteFile(destPath.c_str());
|
||||
|
@ -1514,10 +1499,10 @@ static bool Update(wchar_t *cmdLine)
|
|||
}
|
||||
|
||||
/* ------------------------------------- *
|
||||
* Check for VS2019 redistributables */
|
||||
* Check VS redistributables version */
|
||||
|
||||
if (!HasVS2019Redist()) {
|
||||
if (!UpdateVS2019Redists(manifest.vc2019_redist_x64)) {
|
||||
if (IsVSRedistOutdated()) {
|
||||
if (!UpdateVSRedists()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "window-basic-about.hpp"
|
||||
#include "window-basic-main.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "remote-text.hpp"
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <util/util.hpp>
|
||||
#include <util/platform.h>
|
||||
#include <platform.hpp>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include "item-widget-helpers.hpp"
|
||||
#include "adv-audio-control.hpp"
|
||||
#include "obs-app.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
#include "ui_OBSAdvAudio.h"
|
||||
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
#include <graphics/vec4.h>
|
||||
#include <graphics/graphics.h>
|
||||
#include <graphics/math-extra.h>
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
#include "window-basic-auto-config.hpp"
|
||||
#include "window-basic-main.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "obs-app.hpp"
|
||||
|
||||
#include "ui_AutoConfigTestPage.h"
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
#include <QScreen>
|
||||
|
||||
#include <obs.hpp>
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "window-basic-auto-config.hpp"
|
||||
#include "window-basic-main.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "obs-app.hpp"
|
||||
#include "url-push-button.hpp"
|
||||
|
||||
|
@ -456,8 +456,17 @@ bool AutoConfigStreamPage::validatePage()
|
|||
int multitrackVideoBitrate = 0;
|
||||
for (auto &encoder_config :
|
||||
config.encoder_configurations) {
|
||||
multitrackVideoBitrate +=
|
||||
encoder_config.config.bitrate;
|
||||
auto it =
|
||||
encoder_config.settings.find("bitrate");
|
||||
if (it == encoder_config.settings.end())
|
||||
continue;
|
||||
|
||||
if (!it->is_number_integer())
|
||||
continue;
|
||||
|
||||
int bitrate = 0;
|
||||
it->get_to(bitrate);
|
||||
multitrackVideoBitrate += bitrate;
|
||||
}
|
||||
|
||||
// grab a streamkey from the go live config if we can
|
||||
|
|
|
@ -20,12 +20,12 @@
|
|||
#include "window-basic-main.hpp"
|
||||
#include "window-basic-filters.hpp"
|
||||
#include "display-helpers.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "visibility-item-widget.hpp"
|
||||
#include "item-widget-helpers.hpp"
|
||||
#include "obs-app.hpp"
|
||||
#include "undo-stack-obs.hpp"
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <QMessageBox>
|
||||
#include <QCloseEvent>
|
||||
#include <obs-data.h>
|
||||
|
|
|
@ -21,8 +21,7 @@
|
|||
#include <QDialogButtonBox>
|
||||
#include <memory>
|
||||
#include <obs.hpp>
|
||||
|
||||
#include "properties-view.hpp"
|
||||
#include <properties-view.hpp>
|
||||
|
||||
class OBSBasic;
|
||||
class QMenu;
|
||||
|
|
|
@ -18,9 +18,9 @@
|
|||
#include "obs-app.hpp"
|
||||
#include "window-basic-interaction.hpp"
|
||||
#include "window-basic-main.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "display-helpers.hpp"
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <QKeyEvent>
|
||||
#include <QCloseEvent>
|
||||
#include <QScreen>
|
||||
|
|
|
@ -22,8 +22,7 @@
|
|||
#include <functional>
|
||||
|
||||
#include <obs.hpp>
|
||||
|
||||
#include "properties-view.hpp"
|
||||
#include <properties-view.hpp>
|
||||
|
||||
class OBSBasic;
|
||||
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
#include <QDir>
|
||||
#include <QThread>
|
||||
#include <QMessageBox>
|
||||
#include <qt-wrappers.hpp>
|
||||
#include "window-basic-main.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
|
||||
#include <random>
|
||||
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
#include <QSettings>
|
||||
#endif
|
||||
#include <string>
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
#include "window-basic-main.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include <cinttypes>
|
||||
#include <QMessageBox>
|
||||
#include <QPromise>
|
||||
#include "qt-wrappers.hpp"
|
||||
#include <qt-wrappers.hpp>
|
||||
#include "audio-encoders.hpp"
|
||||
#include "multitrack-video-error.hpp"
|
||||
#include "window-basic-main.hpp"
|
||||
|
@ -1138,7 +1138,7 @@ FutureHolder<bool> SimpleOutput::SetupStreaming(obs_service_t *service)
|
|||
: std::nullopt;
|
||||
|
||||
auto holder = SetupMultitrackVideo(
|
||||
service, GetSimpleAACEncoderForBitrate(audio_bitrate),
|
||||
service, GetSimpleAACEncoderForBitrate(audio_bitrate), 0,
|
||||
vod_track_mixer);
|
||||
auto future =
|
||||
PreventFutureDeadlock(holder.future)
|
||||
|
@ -2286,9 +2286,13 @@ FutureHolder<bool> AdvancedOutput::SetupStreaming(obs_service_t *service)
|
|||
|
||||
const char *audio_encoder_id =
|
||||
config_get_string(main->Config(), "AdvOut", "AudioEncoder");
|
||||
int streamTrackIndex =
|
||||
config_get_int(main->Config(), "AdvOut", "TrackIndex") - 1;
|
||||
|
||||
auto holder = SetupMultitrackVideo(service, audio_encoder_id,
|
||||
VodTrackMixerIdx(service));
|
||||
auto holder =
|
||||
SetupMultitrackVideo(service, audio_encoder_id,
|
||||
static_cast<size_t>(streamTrackIndex),
|
||||
VodTrackMixerIdx(service));
|
||||
auto future =
|
||||
PreventFutureDeadlock(holder.future)
|
||||
.then(main, [&](std::optional<bool>
|
||||
|
@ -2701,10 +2705,9 @@ std::string BasicOutputHandler::GetRecordingFilename(
|
|||
|
||||
extern std::string DeserializeConfigText(const char *text);
|
||||
|
||||
FutureHolder<std::optional<bool>>
|
||||
BasicOutputHandler::SetupMultitrackVideo(obs_service_t *service,
|
||||
std::string audio_encoder_id,
|
||||
std::optional<size_t> vod_track_mixer)
|
||||
FutureHolder<std::optional<bool>> BasicOutputHandler::SetupMultitrackVideo(
|
||||
obs_service_t *service, std::string audio_encoder_id,
|
||||
size_t main_audio_mixer, std::optional<size_t> vod_track_mixer)
|
||||
{
|
||||
if (!multitrackVideo)
|
||||
return {[] {}, CreateFuture().then([] {
|
||||
|
@ -2784,7 +2787,8 @@ BasicOutputHandler::SetupMultitrackVideo(obs_service_t *service,
|
|||
audio_encoder_id.c_str(),
|
||||
maximum_aggregate_bitrate,
|
||||
maximum_video_tracks, custom_config,
|
||||
stream_dump_config, vod_track_mixer);
|
||||
stream_dump_config, main_audio_mixer,
|
||||
vod_track_mixer);
|
||||
} catch (const MultitrackVideoError &error) {
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -98,10 +98,9 @@ protected:
|
|||
bool overwrite, const char *format,
|
||||
bool ffmpeg);
|
||||
|
||||
FutureHolder<std::optional<bool>>
|
||||
SetupMultitrackVideo(obs_service_t *service,
|
||||
std::string audio_encoder_id,
|
||||
std::optional<size_t> vod_track_mixer);
|
||||
FutureHolder<std::optional<bool>> SetupMultitrackVideo(
|
||||
obs_service_t *service, std::string audio_encoder_id,
|
||||
size_t main_audio_mixer, std::optional<size_t> vod_track_mixer);
|
||||
OBSDataAutoRelease GenerateMultitrackVideoStreamDumpConfig();
|
||||
};
|
||||
|
||||
|
|
|
@ -21,10 +21,10 @@
|
|||
#include <QMessageBox>
|
||||
#include <QVariant>
|
||||
#include <QFileDialog>
|
||||
#include <qt-wrappers.hpp>
|
||||
#include "window-basic-main.hpp"
|
||||
#include "window-basic-auto-config.hpp"
|
||||
#include "window-namedialog.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
|
||||
extern void DestroyPanelCookieManager();
|
||||
extern void DuplicateCurrentCookieProfile(ConfigFile &config);
|
||||
|
|
|
@ -21,11 +21,11 @@
|
|||
#include <QVariant>
|
||||
#include <QFileDialog>
|
||||
#include <QStandardPaths>
|
||||
#include <qt-wrappers.hpp>
|
||||
#include "item-widget-helpers.hpp"
|
||||
#include "window-basic-main.hpp"
|
||||
#include "window-importer.hpp"
|
||||
#include "window-namedialog.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
|
||||
#include "window-basic-main.hpp"
|
||||
#include "screenshot-obj.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <wincodec.h>
|
||||
|
|
|
@ -20,14 +20,14 @@
|
|||
#include <QToolTip>
|
||||
#include <QMessageBox>
|
||||
#include <util/dstr.hpp>
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <slider-ignorewheel.hpp>
|
||||
#include "window-basic-main.hpp"
|
||||
#include "window-basic-main-outputs.hpp"
|
||||
#include "window-basic-vcam-config.hpp"
|
||||
#include "display-helpers.hpp"
|
||||
#include "window-namedialog.hpp"
|
||||
#include "menu-button.hpp"
|
||||
#include "slider-ignorewheel.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
|
||||
#include "obs-hotkey.h"
|
||||
|
||||
|
@ -445,7 +445,8 @@ void OBSBasic::SetTransition(OBSSource transition)
|
|||
ui->transitionDurationLabel->setVisible(!fixed);
|
||||
ui->transitionDuration->setVisible(!fixed);
|
||||
|
||||
bool configurable = obs_source_configurable(transition);
|
||||
bool configurable = transition ? obs_source_configurable(transition)
|
||||
: false;
|
||||
ui->transitionRemove->setEnabled(configurable);
|
||||
ui->transitionProps->setEnabled(configurable);
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <QScrollBar>
|
||||
#include <QTextStream>
|
||||
#include <QActionGroup>
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
#include <util/dstr.h>
|
||||
#include <util/util.hpp>
|
||||
|
@ -67,7 +68,6 @@
|
|||
#include "window-youtube-actions.hpp"
|
||||
#include "youtube-api-wrappers.hpp"
|
||||
#endif
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "context-bar-controls.hpp"
|
||||
#include "obs-proxy-style.hpp"
|
||||
#include "display-helpers.hpp"
|
||||
|
@ -1549,7 +1549,7 @@ extern void CheckExistingCookieId();
|
|||
#elif OBS_RELEASE_CANDIDATE == 0 && OBS_BETA == 0
|
||||
#define DEFAULT_CONTAINER "mkv"
|
||||
#else
|
||||
#define DEFAULT_CONTAINER "fragmented_mp4"
|
||||
#define DEFAULT_CONTAINER "hybrid_mp4"
|
||||
#endif
|
||||
|
||||
bool OBSBasic::InitBasicConfigDefaults()
|
||||
|
@ -2889,22 +2889,14 @@ void OBSBasic::CreateHotkeys()
|
|||
this);
|
||||
LoadHotkey(splitFileHotkey, "OBSBasic.SplitFile");
|
||||
|
||||
/* Adding chapters is only supported by the native MP4 output */
|
||||
const string_view output_id =
|
||||
obs_output_get_id(outputHandler->fileOutput);
|
||||
if (output_id == "mp4_output") {
|
||||
addChapterHotkey = obs_hotkey_register_frontend(
|
||||
"OBSBasic.AddChapterMarker",
|
||||
Str("Basic.Main.AddChapterMarker"),
|
||||
[](void *, obs_hotkey_id, obs_hotkey_t *,
|
||||
bool pressed) {
|
||||
if (pressed)
|
||||
obs_frontend_recording_add_chapter(
|
||||
nullptr);
|
||||
},
|
||||
this);
|
||||
LoadHotkey(addChapterHotkey, "OBSBasic.AddChapterMarker");
|
||||
}
|
||||
addChapterHotkey = obs_hotkey_register_frontend(
|
||||
"OBSBasic.AddChapterMarker", Str("Basic.Main.AddChapterMarker"),
|
||||
[](void *, obs_hotkey_id, obs_hotkey_t *, bool pressed) {
|
||||
if (pressed)
|
||||
obs_frontend_recording_add_chapter(nullptr);
|
||||
},
|
||||
this);
|
||||
LoadHotkey(addChapterHotkey, "OBSBasic.AddChapterMarker");
|
||||
|
||||
replayBufHotkeys = obs_hotkey_pair_register_frontend(
|
||||
"OBSBasic.StartReplayBuffer",
|
||||
|
@ -8415,13 +8407,8 @@ void OBSBasic::ProgramViewContextMenuRequested()
|
|||
&OBSBasic::OpenStudioProgramProjector);
|
||||
|
||||
popup.addMenu(studioProgramProjector);
|
||||
|
||||
QAction *studioProgramWindow =
|
||||
popup.addAction(QTStr("StudioProgramWindow"), this,
|
||||
&OBSBasic::OpenStudioProgramWindow);
|
||||
|
||||
popup.addAction(studioProgramWindow);
|
||||
|
||||
popup.addAction(QTStr("StudioProgramWindow"), this,
|
||||
&OBSBasic::OpenStudioProgramWindow);
|
||||
popup.addAction(QTStr("Screenshot.StudioProgram"), this,
|
||||
&OBSBasic::ScreenshotProgram);
|
||||
|
||||
|
@ -8639,7 +8626,7 @@ void OBSBasic::UpdateEditMenu()
|
|||
const bool canTransformSingle = videoCount == 1 && totalCount == 1;
|
||||
|
||||
OBSSceneItem curItem = GetCurrentSceneItem();
|
||||
bool locked = obs_sceneitem_locked(curItem);
|
||||
bool locked = curItem && obs_sceneitem_locked(curItem);
|
||||
|
||||
ui->actionCopySource->setEnabled(totalCount > 0);
|
||||
ui->actionEditTransform->setEnabled(canTransformSingle && !locked);
|
||||
|
|
|
@ -654,7 +654,7 @@ void OBSBasicPreview::mousePressEvent(QMouseEvent *event)
|
|||
|
||||
void OBSBasicPreview::UpdateCursor(uint32_t &flags)
|
||||
{
|
||||
if (obs_sceneitem_locked(stretchItem)) {
|
||||
if (!stretchItem || obs_sceneitem_locked(stretchItem)) {
|
||||
unsetCursor();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -18,10 +18,10 @@
|
|||
#include "obs-app.hpp"
|
||||
#include "window-basic-properties.hpp"
|
||||
#include "window-basic-main.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "display-helpers.hpp"
|
||||
#include "properties-view.hpp"
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <properties-view.hpp>
|
||||
#include <QCloseEvent>
|
||||
#include <QScreen>
|
||||
#include <QWindow>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#include "window-basic-main.hpp"
|
||||
#include "obs-frontend-api.h"
|
||||
#include "obs-app.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <QColorDialog>
|
||||
|
||||
enum ColorPreset {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
#include <QMessageBox>
|
||||
#include <QUrl>
|
||||
#include <QUuid>
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
#include "window-basic-settings.hpp"
|
||||
#include "obs-frontend-api.h"
|
||||
#include "obs-app.hpp"
|
||||
#include "window-basic-main.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "url-push-button.hpp"
|
||||
|
||||
#ifdef BROWSER_AVAILABLE
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <QScreen>
|
||||
#include <QStandardItemModel>
|
||||
#include <QSpacerItem>
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
#include "audio-encoders.hpp"
|
||||
#include "hotkey-edit.hpp"
|
||||
|
@ -42,7 +43,6 @@
|
|||
#include "obs-app.hpp"
|
||||
#include "platform.hpp"
|
||||
#include "properties-view.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "window-basic-main.hpp"
|
||||
#include "window-basic-settings.hpp"
|
||||
#include "window-basic-main-outputs.hpp"
|
||||
|
@ -1021,6 +1021,10 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
|
|||
|
||||
UpdateAudioWarnings();
|
||||
UpdateAdvNetworkGroup();
|
||||
|
||||
ui->audioMsg->setVisible(false);
|
||||
ui->advancedMsg->setVisible(false);
|
||||
ui->advancedMsg2->setVisible(false);
|
||||
}
|
||||
|
||||
OBSBasicSettings::~OBSBasicSettings()
|
||||
|
@ -2836,24 +2840,30 @@ void OBSBasicSettings::UpdateColorFormatSpaceWarning()
|
|||
if ((format == "P010") || (format == "P216") ||
|
||||
(format == "P416")) {
|
||||
ui->advancedMsg2->clear();
|
||||
ui->advancedMsg2->setVisible(false);
|
||||
} else if (format == "I010") {
|
||||
ui->advancedMsg2->setText(
|
||||
QTStr("Basic.Settings.Advanced.FormatWarning"));
|
||||
ui->advancedMsg2->setVisible(true);
|
||||
} else {
|
||||
ui->advancedMsg2->setText(QTStr(
|
||||
"Basic.Settings.Advanced.FormatWarning2100"));
|
||||
ui->advancedMsg2->setVisible(true);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (format == "NV12") {
|
||||
ui->advancedMsg2->clear();
|
||||
ui->advancedMsg2->setVisible(false);
|
||||
} else if ((format == "I010") || (format == "P010") ||
|
||||
(format == "P216") || (format == "P416")) {
|
||||
ui->advancedMsg2->setText(QTStr(
|
||||
"Basic.Settings.Advanced.FormatWarningPreciseSdr"));
|
||||
ui->advancedMsg2->setVisible(true);
|
||||
} else {
|
||||
ui->advancedMsg2->setText(
|
||||
QTStr("Basic.Settings.Advanced.FormatWarning"));
|
||||
ui->advancedMsg2->setVisible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4680,6 +4690,8 @@ void OBSBasicSettings::AudioChanged()
|
|||
|
||||
void OBSBasicSettings::AudioChangedRestart()
|
||||
{
|
||||
ui->audioMsg->setVisible(false);
|
||||
|
||||
if (!loading) {
|
||||
int currentChannelIndex = ui->channelSetup->currentIndex();
|
||||
int currentSampleRateIndex = ui->sampleRate->currentIndex();
|
||||
|
@ -4691,6 +4703,7 @@ void OBSBasicSettings::AudioChangedRestart()
|
|||
currentLLAudioBufVal != llBufferingEnabled) {
|
||||
ui->audioMsg->setText(
|
||||
QTStr("Basic.Settings.ProgramRestart"));
|
||||
ui->audioMsg->setVisible(true);
|
||||
} else {
|
||||
ui->audioMsg->setText("");
|
||||
}
|
||||
|
@ -4816,10 +4829,13 @@ void RestrictResetBitrates(initializer_list<QComboBox *> boxes, int maxbitrate)
|
|||
|
||||
void OBSBasicSettings::AdvancedChangedRestart()
|
||||
{
|
||||
ui->advancedMsg->setVisible(false);
|
||||
|
||||
if (!loading) {
|
||||
advancedChanged = true;
|
||||
ui->advancedMsg->setText(
|
||||
QTStr("Basic.Settings.ProgramRestart"));
|
||||
ui->advancedMsg->setVisible(true);
|
||||
sender()->setProperty("changed", QVariant(true));
|
||||
EnableApplyButton(true);
|
||||
}
|
||||
|
@ -6022,6 +6038,7 @@ void OBSBasicSettings::UpdateAudioWarnings()
|
|||
}
|
||||
|
||||
ui->audioMsg_2->setText(text);
|
||||
ui->audioMsg_2->setVisible(!text.isEmpty());
|
||||
}
|
||||
|
||||
void OBSBasicSettings::LowLatencyBufferingChanged(bool checked)
|
||||
|
@ -6308,6 +6325,13 @@ void OBSBasicSettings::UpdateMultitrackVideo()
|
|||
ui->enableMultitrackVideo->setChecked(false);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
available = available && MultitrackVideoDeveloperModeEnabled();
|
||||
#endif
|
||||
|
||||
if (IsCustomService())
|
||||
available = available && MultitrackVideoDeveloperModeEnabled();
|
||||
|
||||
ui->multitrackVideoGroupBox->setVisible(available);
|
||||
|
||||
ui->enableMultitrackVideo->setEnabled(toggle_available);
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
******************************************************************************/
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <qt-wrappers.hpp>
|
||||
#include "window-basic-main.hpp"
|
||||
#include "window-basic-source-select.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "obs-app.hpp"
|
||||
|
||||
struct AddSourceData {
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
#include "window-basic-main.hpp"
|
||||
#include "platform.hpp"
|
||||
#include "obs-app.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <QPushButton>
|
||||
#include <QScrollArea>
|
||||
#include <QVBoxLayout>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "window-basic-vcam-config.hpp"
|
||||
#include "window-basic-main.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <util/util.hpp>
|
||||
#include <util/platform.h>
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#include "window-extra-browsers.hpp"
|
||||
#include "window-dock-browser.hpp"
|
||||
#include "window-basic-main.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <QLineEdit>
|
||||
#include <QHBoxLayout>
|
||||
#include <QUuid>
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
#include <QStyledItemDelegate>
|
||||
#include <QDirIterator>
|
||||
#include <QDropEvent>
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "importers/importers.hpp"
|
||||
|
||||
extern bool SceneCollectionExists(const char *findName);
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#include <QToolButton>
|
||||
#include <QFileDialog>
|
||||
|
||||
#include "qt-wrappers.hpp"
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
enum MissingFilesColumn {
|
||||
Source,
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
******************************************************************************/
|
||||
|
||||
#include "window-namedialog.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "obs-app.hpp"
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
NameDialog::NameDialog(QWidget *parent) : QDialog(parent)
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
#include <QMouseEvent>
|
||||
#include <QMenu>
|
||||
#include <QScreen>
|
||||
#include <qt-wrappers.hpp>
|
||||
#include "obs-app.hpp"
|
||||
#include "window-basic-main.hpp"
|
||||
#include "display-helpers.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "platform.hpp"
|
||||
#include "multiview.hpp"
|
||||
|
||||
|
|
|
@ -31,8 +31,8 @@
|
|||
#include <QStyledItemDelegate>
|
||||
#include <QToolButton>
|
||||
#include <QTimer>
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "window-basic-main.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
#include "window-youtube-actions.hpp"
|
||||
|
||||
#include "obs-app.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "youtube-api-wrappers.hpp"
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <QToolTip>
|
||||
#include <QDateTime>
|
||||
#include <QDesktopServices>
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
#include "auth-youtube.hpp"
|
||||
#include "obs-app.hpp"
|
||||
#include "window-basic-main.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "remote-text.hpp"
|
||||
#include "ui-config.h"
|
||||
#include "obf.h"
|
||||
|
|
|
@ -54,7 +54,7 @@ invoke_formatter() {
|
|||
exit 2
|
||||
fi
|
||||
|
||||
if (( ! #source_files )) source_files=((libobs|libobs-*|UI|plugins|deps)/**/*.(c|cpp|h|hpp|m|mm)(.N))
|
||||
if (( ! #source_files )) source_files=((libobs|libobs-*|UI|plugins|deps|shared)/**/*.(c|cpp|h|hpp|m|mm)(.N))
|
||||
|
||||
source_files=(${source_files:#*/(obs-websocket/deps|decklink/*/decklink-sdk|mac-syphon/syphon-framework|obs-outputs/ftl-sdk|win-dshow/libdshowcapture)/*})
|
||||
|
||||
|
@ -75,7 +75,7 @@ invoke_formatter() {
|
|||
exit 2
|
||||
}
|
||||
|
||||
if (( ! #source_files )) source_files=((libobs|libobs-*|UI|plugins|deps|cmake)/**/(CMakeLists.txt|*.cmake)(.N))
|
||||
if (( ! #source_files )) source_files=((libobs|libobs-*|UI|plugins|deps|shared|cmake)/**/(CMakeLists.txt|*.cmake)(.N))
|
||||
|
||||
source_files=(${source_files:#*/(obs-outputs/ftl-sdk|jansson|decklink/*/decklink-sdk|obs-websocket|obs-browser|win-dshow/libdshowcapture)/*})
|
||||
|
||||
|
|
|
@ -277,7 +277,7 @@ endif()
|
|||
|
||||
if(EXISTS "${FFmpeg_avutil_INCLUDE_DIR}/libavutil/ffversion.h")
|
||||
file(STRINGS "${FFmpeg_avutil_INCLUDE_DIR}/libavutil/ffversion.h" _version_string
|
||||
REGEX "^.*FFMPEG_VERSION[ \t]+\"n?[0-9a-z\\.-]+\"[ \t]*$")
|
||||
REGEX "^.*FFMPEG_VERSION[ \t]+\"n?[0-9a-z\\~.-]+\"[ \t]*$")
|
||||
string(REGEX REPLACE ".*FFMPEG_VERSION[ \t]+\"n?([0-9]+\\.[0-9]).*\".*" "\\1" FFmpeg_VERSION "${_version_string}")
|
||||
endif()
|
||||
|
||||
|
|
|
@ -74,6 +74,8 @@ function(set_target_properties_obs target)
|
|||
COMMAND "${CMAKE_COMMAND}" -E make_directory "${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_LIBRARY_DESTINATION}"
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$<TARGET_FILE:${target}>"
|
||||
"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_LIBRARY_DESTINATION}/"
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$<TARGET_SONAME_FILE:${target}>"
|
||||
"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_LIBRARY_DESTINATION}/"
|
||||
COMMENT "Copy ${target} to library directory (${OBS_LIBRARY_DESTINATION})"
|
||||
VERBATIM)
|
||||
|
||||
|
|
6
deps/CMakeLists.txt
vendored
6
deps/CMakeLists.txt
vendored
|
@ -1,13 +1,7 @@
|
|||
if(OS_WINDOWS)
|
||||
add_subdirectory(ipc-util)
|
||||
add_subdirectory(w32-pthreads)
|
||||
endif()
|
||||
|
||||
add_subdirectory(blake2)
|
||||
add_subdirectory(file-updater)
|
||||
add_subdirectory(glad)
|
||||
add_subdirectory(happy-eyeballs)
|
||||
add_subdirectory(libcaption)
|
||||
add_subdirectory(media-playback)
|
||||
add_subdirectory(obs-scripting)
|
||||
add_subdirectory(opts-parser)
|
||||
|
|
13
deps/blake2/CMakeLists.txt
vendored
13
deps/blake2/CMakeLists.txt
vendored
|
@ -11,3 +11,16 @@ target_sources(
|
|||
target_include_directories(blake2 PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
|
||||
|
||||
set_target_properties(blake2 PROPERTIES FOLDER deps)
|
||||
|
||||
if(OS_WINDOWS)
|
||||
add_library(blake2_static OBJECT)
|
||||
add_library(OBS::blake2_static ALIAS blake2_static)
|
||||
|
||||
target_sources(
|
||||
blake2_static
|
||||
PRIVATE src/blake2-impl.h src/blake2b-ref.c
|
||||
PUBLIC src/blake2.h)
|
||||
|
||||
target_include_directories(blake2_static PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
|
||||
set_target_properties(blake2_static PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
endif()
|
||||
|
|
|
@ -49,6 +49,7 @@ target_sources(
|
|||
obs-av1.h
|
||||
obs-avc.c
|
||||
obs-avc.h
|
||||
obs-config.h
|
||||
obs-data.c
|
||||
obs-data.h
|
||||
obs-defs.h
|
||||
|
|
|
@ -325,11 +325,10 @@ static void add_connection(struct obs_encoder *encoder)
|
|||
}
|
||||
|
||||
if (encoder->encoder_group) {
|
||||
bool ready = false;
|
||||
pthread_mutex_lock(&encoder->encoder_group->mutex);
|
||||
encoder->encoder_group->encoders_started += 1;
|
||||
ready = encoder->encoder_group->encoders_started ==
|
||||
encoder->encoder_group->encoders_added;
|
||||
encoder->encoder_group->num_encoders_started += 1;
|
||||
bool ready = encoder->encoder_group->num_encoders_started >=
|
||||
encoder->encoder_group->encoders.num;
|
||||
pthread_mutex_unlock(&encoder->encoder_group->mutex);
|
||||
if (ready)
|
||||
add_ready_encoder_group(encoder);
|
||||
|
@ -338,6 +337,7 @@ static void add_connection(struct obs_encoder *encoder)
|
|||
set_encoder_active(encoder, true);
|
||||
}
|
||||
|
||||
void obs_encoder_group_actually_destroy(obs_encoder_group_t *group);
|
||||
static void remove_connection(struct obs_encoder *encoder, bool shutdown)
|
||||
{
|
||||
if (encoder->info.type == OBS_ENCODER_AUDIO) {
|
||||
|
@ -353,10 +353,8 @@ static void remove_connection(struct obs_encoder *encoder, bool shutdown)
|
|||
|
||||
if (encoder->encoder_group) {
|
||||
pthread_mutex_lock(&encoder->encoder_group->mutex);
|
||||
encoder->encoder_group->encoders_started -= 1;
|
||||
if (encoder->encoder_group->encoders_started == 0)
|
||||
if (--encoder->encoder_group->num_encoders_started == 0)
|
||||
encoder->encoder_group->start_timestamp = 0;
|
||||
|
||||
pthread_mutex_unlock(&encoder->encoder_group->mutex);
|
||||
}
|
||||
|
||||
|
@ -367,6 +365,8 @@ static void remove_connection(struct obs_encoder *encoder, bool shutdown)
|
|||
* up again */
|
||||
if (shutdown)
|
||||
obs_encoder_shutdown(encoder);
|
||||
encoder->initialized = false;
|
||||
|
||||
set_encoder_active(encoder, false);
|
||||
}
|
||||
|
||||
|
@ -395,22 +395,7 @@ static void obs_encoder_actually_destroy(obs_encoder_t *encoder)
|
|||
blog(LOG_DEBUG, "encoder '%s' destroyed",
|
||||
encoder->context.name);
|
||||
|
||||
if (encoder->encoder_group) {
|
||||
struct encoder_group *group = encoder->encoder_group;
|
||||
bool release = false;
|
||||
|
||||
encoder->encoder_group = NULL;
|
||||
|
||||
pthread_mutex_lock(&group->mutex);
|
||||
group->encoders_added -= 1;
|
||||
release = group->encoders_added == 0;
|
||||
pthread_mutex_unlock(&group->mutex);
|
||||
|
||||
if (release) {
|
||||
pthread_mutex_destroy(&group->mutex);
|
||||
bfree(group);
|
||||
}
|
||||
}
|
||||
obs_encoder_set_group(encoder, NULL);
|
||||
|
||||
free_audio_buffers(encoder);
|
||||
|
||||
|
@ -801,14 +786,20 @@ void obs_encoder_start(obs_encoder_t *encoder,
|
|||
pthread_mutex_unlock(&encoder->init_mutex);
|
||||
}
|
||||
|
||||
static inline bool obs_encoder_stop_internal(
|
||||
obs_encoder_t *encoder,
|
||||
void (*new_packet)(void *param, struct encoder_packet *packet),
|
||||
void *param)
|
||||
void obs_encoder_stop(obs_encoder_t *encoder,
|
||||
void (*new_packet)(void *param,
|
||||
struct encoder_packet *packet),
|
||||
void *param)
|
||||
{
|
||||
bool last = false;
|
||||
size_t idx;
|
||||
|
||||
if (!obs_encoder_valid(encoder, "obs_encoder_stop"))
|
||||
return;
|
||||
if (!obs_ptr_valid(new_packet, "obs_encoder_stop"))
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&encoder->init_mutex);
|
||||
pthread_mutex_lock(&encoder->callbacks_mutex);
|
||||
|
||||
idx = get_callback_idx(encoder, new_packet, param);
|
||||
|
@ -821,34 +812,32 @@ static inline bool obs_encoder_stop_internal(
|
|||
|
||||
if (last) {
|
||||
remove_connection(encoder, true);
|
||||
encoder->initialized = false;
|
||||
pthread_mutex_unlock(&encoder->init_mutex);
|
||||
|
||||
if (encoder->destroy_on_stop) {
|
||||
pthread_mutex_unlock(&encoder->init_mutex);
|
||||
struct obs_encoder_group *group = encoder->encoder_group;
|
||||
|
||||
if (encoder->destroy_on_stop)
|
||||
obs_encoder_actually_destroy(encoder);
|
||||
return true;
|
||||
|
||||
/* Destroying the group all the way back here prevents a race
|
||||
* where destruction of the group can prematurely destroy the
|
||||
* encoder within internal functions. This is the point where it
|
||||
* is safe to destroy the group, even if the encoder is then
|
||||
* also destroyed. */
|
||||
if (group) {
|
||||
pthread_mutex_lock(&group->mutex);
|
||||
if (group->destroy_on_stop &&
|
||||
group->num_encoders_started == 0)
|
||||
obs_encoder_group_actually_destroy(group);
|
||||
else
|
||||
pthread_mutex_unlock(&group->mutex);
|
||||
}
|
||||
|
||||
/* init_mutex already unlocked */
|
||||
return;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void obs_encoder_stop(obs_encoder_t *encoder,
|
||||
void (*new_packet)(void *param,
|
||||
struct encoder_packet *packet),
|
||||
void *param)
|
||||
{
|
||||
bool destroyed;
|
||||
|
||||
if (!obs_encoder_valid(encoder, "obs_encoder_stop"))
|
||||
return;
|
||||
if (!obs_ptr_valid(new_packet, "obs_encoder_stop"))
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&encoder->init_mutex);
|
||||
destroyed = obs_encoder_stop_internal(encoder, new_packet, param);
|
||||
if (!destroyed)
|
||||
pthread_mutex_unlock(&encoder->init_mutex);
|
||||
pthread_mutex_unlock(&encoder->init_mutex);
|
||||
}
|
||||
|
||||
const char *obs_encoder_get_codec(const obs_encoder_t *encoder)
|
||||
|
@ -1345,7 +1334,6 @@ void full_stop(struct obs_encoder *encoder)
|
|||
pthread_mutex_unlock(&encoder->callbacks_mutex);
|
||||
|
||||
remove_connection(encoder, false);
|
||||
encoder->initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1461,7 +1449,7 @@ static void receive_video(void *param, struct video_data *frame)
|
|||
struct encoder_frame enc_frame;
|
||||
|
||||
if (encoder->encoder_group && !encoder->start_ts) {
|
||||
struct encoder_group *group = encoder->encoder_group;
|
||||
struct obs_encoder_group *group = encoder->encoder_group;
|
||||
bool ready = false;
|
||||
pthread_mutex_lock(&group->mutex);
|
||||
ready = group->start_timestamp == frame->timestamp;
|
||||
|
@ -2058,132 +2046,99 @@ uint32_t obs_encoder_get_roi_increment(const obs_encoder_t *encoder)
|
|||
return encoder->roi_increment;
|
||||
}
|
||||
|
||||
bool obs_encoder_group_keyframe_aligned_encoders(
|
||||
obs_encoder_t *encoder, obs_encoder_t *encoder_to_be_grouped)
|
||||
bool obs_encoder_set_group(obs_encoder_t *encoder, obs_encoder_group_t *group)
|
||||
{
|
||||
if (!obs_encoder_valid(encoder,
|
||||
"obs_encoder_group_keyframe_aligned_encoders") ||
|
||||
!obs_encoder_valid(encoder_to_be_grouped,
|
||||
"obs_encoder_group_keyframe_aligned_encoders"))
|
||||
if (!obs_encoder_valid(encoder, "obs_encoder_set_group"))
|
||||
return false;
|
||||
|
||||
if (obs_encoder_active(encoder) ||
|
||||
obs_encoder_active(encoder_to_be_grouped)) {
|
||||
obs_encoder_t *active = obs_encoder_active(encoder)
|
||||
? encoder
|
||||
: encoder_to_be_grouped;
|
||||
obs_encoder_t *other = active == encoder ? encoder_to_be_grouped
|
||||
: encoder;
|
||||
if (obs_encoder_active(encoder)) {
|
||||
blog(LOG_ERROR,
|
||||
"obs_encoder_group_keyframe_aligned_encoders: encoder '%s' "
|
||||
"is already active, could not group with '%s'",
|
||||
obs_encoder_get_name(active), obs_encoder_get_name(other));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (encoder_to_be_grouped->encoder_group) {
|
||||
blog(LOG_ERROR,
|
||||
"obs_encoder_group_keyframe_aligned_encoders: encoder '%s' "
|
||||
"is already part of a keyframe aligned group while trying "
|
||||
"to group with encoder '%s'",
|
||||
obs_encoder_get_name(encoder_to_be_grouped),
|
||||
"obs_encoder_set_group: encoder '%s' is already active",
|
||||
obs_encoder_get_name(encoder));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool unlock = false;
|
||||
if (!encoder->encoder_group) {
|
||||
encoder->encoder_group = bzalloc(sizeof(struct encoder_group));
|
||||
if (pthread_mutex_init(&encoder->encoder_group->mutex, NULL) <
|
||||
0) {
|
||||
bfree(encoder->encoder_group);
|
||||
encoder->encoder_group = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
encoder->encoder_group->encoders_added = 1;
|
||||
} else {
|
||||
pthread_mutex_lock(&encoder->encoder_group->mutex);
|
||||
unlock = true;
|
||||
if (encoder->encoder_group->encoders_started != 0) {
|
||||
if (encoder->encoder_group) {
|
||||
struct obs_encoder_group *old_group = encoder->encoder_group;
|
||||
pthread_mutex_lock(&old_group->mutex);
|
||||
if (old_group->num_encoders_started) {
|
||||
pthread_mutex_unlock(&old_group->mutex);
|
||||
blog(LOG_ERROR,
|
||||
"obs_encoder_group_keyframe_aligned_encoders: "
|
||||
"Can't add encoder '%s' to active group "
|
||||
"from encoder '%s'",
|
||||
obs_encoder_get_name(encoder_to_be_grouped),
|
||||
"obs_encoder_set_group: encoder '%s' existing group has started encoders",
|
||||
obs_encoder_get_name(encoder));
|
||||
pthread_mutex_unlock(&encoder->encoder_group->mutex);
|
||||
return false;
|
||||
}
|
||||
da_erase_item(old_group->encoders, &encoder);
|
||||
obs_encoder_release(encoder);
|
||||
pthread_mutex_unlock(&old_group->mutex);
|
||||
}
|
||||
|
||||
encoder->encoder_group->encoders_added += 1;
|
||||
encoder_to_be_grouped->encoder_group = encoder->encoder_group;
|
||||
if (!group)
|
||||
return true;
|
||||
|
||||
if (unlock)
|
||||
pthread_mutex_unlock(&encoder->encoder_group->mutex);
|
||||
pthread_mutex_lock(&group->mutex);
|
||||
|
||||
if (group->num_encoders_started) {
|
||||
pthread_mutex_unlock(&group->mutex);
|
||||
blog(LOG_ERROR,
|
||||
"obs_encoder_set_group: specified group has started encoders");
|
||||
return false;
|
||||
}
|
||||
|
||||
obs_encoder_t *ref = obs_encoder_get_ref(encoder);
|
||||
if (!ref) {
|
||||
pthread_mutex_unlock(&group->mutex);
|
||||
return false;
|
||||
}
|
||||
da_push_back(group->encoders, &ref);
|
||||
encoder->encoder_group = group;
|
||||
|
||||
pthread_mutex_unlock(&group->mutex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool obs_encoder_group_remove_keyframe_aligned_encoder(
|
||||
obs_encoder_t *encoder, obs_encoder_t *encoder_to_be_ungrouped)
|
||||
obs_encoder_group_t *obs_encoder_group_create()
|
||||
{
|
||||
if (!obs_encoder_valid(
|
||||
encoder,
|
||||
"obs_encoder_group_remove_keyframe_aligned_encoder") ||
|
||||
!obs_encoder_valid(
|
||||
encoder_to_be_ungrouped,
|
||||
"obs_encoder_group_remove_keyframe_aligned_encoder"))
|
||||
return false;
|
||||
struct obs_encoder_group *group =
|
||||
bzalloc(sizeof(struct obs_encoder_group));
|
||||
|
||||
if (obs_encoder_active(encoder) ||
|
||||
obs_encoder_active(encoder_to_be_ungrouped)) {
|
||||
blog(LOG_ERROR,
|
||||
"obs_encoder_group_remove_keyframe_aligned_encoder: encoders are active, "
|
||||
"could not ungroup encoder '%s' from '%s'",
|
||||
obs_encoder_get_name(encoder_to_be_ungrouped),
|
||||
obs_encoder_get_name(encoder));
|
||||
return false;
|
||||
pthread_mutex_init_value(&group->mutex);
|
||||
if (pthread_mutex_init(&group->mutex, NULL) != 0) {
|
||||
bfree(group);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (encoder->encoder_group != encoder_to_be_ungrouped->encoder_group) {
|
||||
blog(LOG_ERROR,
|
||||
"obs_encoder_group_remove_keyframe_aligned_encoder: "
|
||||
"encoder '%s' does not belong to the same group as encoder '%s'",
|
||||
obs_encoder_get_name(encoder_to_be_ungrouped),
|
||||
obs_encoder_get_name(encoder));
|
||||
return false;
|
||||
}
|
||||
|
||||
struct encoder_group *current_group = encoder->encoder_group;
|
||||
struct encoder_group *free_group = NULL;
|
||||
|
||||
pthread_mutex_lock(¤t_group->mutex);
|
||||
|
||||
if (current_group->encoders_started != 0) {
|
||||
blog(LOG_ERROR,
|
||||
"obs_encoder_group_remove_keyframe_aligned_encoder: "
|
||||
"could not ungroup encoder '%s' from '%s' while "
|
||||
"the group contains active encoders",
|
||||
obs_encoder_get_name(encoder_to_be_ungrouped),
|
||||
obs_encoder_get_name(encoder));
|
||||
pthread_mutex_unlock(¤t_group->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
current_group->encoders_added -= 1;
|
||||
encoder_to_be_ungrouped->encoder_group = NULL;
|
||||
if (current_group->encoders_added == 1) {
|
||||
free_group = current_group;
|
||||
encoder->encoder_group = NULL;
|
||||
}
|
||||
pthread_mutex_unlock(¤t_group->mutex);
|
||||
|
||||
if (free_group) {
|
||||
pthread_mutex_destroy(&free_group->mutex);
|
||||
bfree(free_group);
|
||||
}
|
||||
|
||||
return true;
|
||||
return group;
|
||||
}
|
||||
|
||||
void obs_encoder_group_actually_destroy(obs_encoder_group_t *group)
|
||||
{
|
||||
for (size_t i = 0; i < group->encoders.num; i++) {
|
||||
struct obs_encoder *encoder = group->encoders.array[i];
|
||||
encoder->encoder_group = NULL;
|
||||
obs_encoder_release(encoder);
|
||||
}
|
||||
|
||||
da_free(group->encoders);
|
||||
pthread_mutex_unlock(&group->mutex);
|
||||
pthread_mutex_destroy(&group->mutex);
|
||||
|
||||
bfree(group);
|
||||
}
|
||||
|
||||
void obs_encoder_group_destroy(obs_encoder_group_t *group)
|
||||
{
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&group->mutex);
|
||||
|
||||
if (group->num_encoders_started) {
|
||||
group->destroy_on_stop = true;
|
||||
pthread_mutex_unlock(&group->mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
obs_encoder_group_actually_destroy(group);
|
||||
}
|
||||
|
|
|
@ -1242,10 +1242,15 @@ struct encoder_callback {
|
|||
void *param;
|
||||
};
|
||||
|
||||
struct encoder_group {
|
||||
struct obs_encoder_group {
|
||||
pthread_mutex_t mutex;
|
||||
uint32_t encoders_added;
|
||||
uint32_t encoders_started;
|
||||
/* allows group to be destroyed even if some encoders are active */
|
||||
bool destroy_on_stop;
|
||||
|
||||
/* holds strong references to all encoders */
|
||||
DARRAY(struct obs_encoder *) encoders;
|
||||
|
||||
uint32_t num_encoders_started;
|
||||
uint64_t start_timestamp;
|
||||
};
|
||||
|
||||
|
@ -1314,7 +1319,7 @@ struct obs_encoder {
|
|||
uint64_t start_ts;
|
||||
|
||||
/* track encoders that are part of a gop-aligned multi track group */
|
||||
struct encoder_group *encoder_group;
|
||||
struct obs_encoder_group *encoder_group;
|
||||
|
||||
pthread_mutex_t outputs_mutex;
|
||||
DARRAY(obs_output_t *) outputs;
|
||||
|
|
|
@ -2226,7 +2226,7 @@ check_encoder_group_keyframe_alignment(obs_output_t *output,
|
|||
|
||||
pthread_mutex_lock(&packet->encoder->encoder_group->mutex);
|
||||
insert_data.required_tracks =
|
||||
packet->encoder->encoder_group->encoders_started;
|
||||
packet->encoder->encoder_group->num_encoders_started;
|
||||
pthread_mutex_unlock(&packet->encoder->encoder_group->mutex);
|
||||
|
||||
da_insert(output->keyframe_group_tracking, idx, &insert_data);
|
||||
|
|
|
@ -92,7 +92,7 @@ static void *gpu_encode_thread(void *data)
|
|||
pkt.encoder = encoder;
|
||||
|
||||
if (encoder->encoder_group && !encoder->start_ts) {
|
||||
struct encoder_group *group =
|
||||
struct obs_encoder_group *group =
|
||||
encoder->encoder_group;
|
||||
bool ready = false;
|
||||
pthread_mutex_lock(&group->mutex);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue