Compare commits

...

98 commits

Author SHA1 Message Date
tytan652 46e47c07c9 UI: Remove obs-ui-support
The remaining files are only used by the UI, so those are restored in
the obs-studio target
2024-06-27 14:09:55 +02:00
tytan652 3572842641 UI,shared: Move OBSPropertiesView to its own directory 2024-06-27 14:09:55 +02:00
tytan652 1f477724ef frontend-tools: Remove unused source files in legacy path 2024-06-27 14:09:55 +02:00
tytan652 0020df98f4 UI,shared: Move SliderIgnoreScroll to its own directory 2024-06-27 14:09:55 +02:00
tytan652 be48558755 UI,shared: Move VScrollArea to its own directory 2024-06-27 14:09:55 +02:00
tytan652 0bd6e0ba8a UI,shared: Move OBSPlainTextEdit to its own directory 2024-06-27 14:09:55 +02:00
tytan652 85651a7d4b UI,shared: Move Qt Wrappers to its own directory 2024-06-27 14:09:55 +02:00
tytan652 1407fb315c UI: Move QTToGSWindow outside of Qt wrappers 2024-06-27 14:09:55 +02:00
tytan652 6cca6f88d9 deps,shared,plugins: Move opts-parser to shared folder 2024-06-27 14:09:55 +02:00
tytan652 21b47c4f91 deps,shared,frontend-tools: Move obs-scripting to shared folder 2024-06-27 14:09:55 +02:00
tytan652 4b1e16203f deps,shared,obs-ffmpeg: Move media-playback to shared folder 2024-06-27 14:09:55 +02:00
tytan652 326d5f4e31 deps,shared,win-capture: Move ipc-util to shared folder 2024-06-27 14:09:55 +02:00
tytan652 ab05714919 deps,shared,obs-outputs: Move happy-eyeballs to shared folder 2024-06-27 14:09:55 +02:00
tytan652 bef7de616e deps,shared,plugins: Move file-updater to shared folder 2024-06-27 14:09:55 +02:00
tytan652 b0aac7e794 Add shared folder in gitignore and format scripts 2024-06-27 14:09:55 +02:00
Ryan Foster 9d67bf2662 Revert "plugins/win-dshow: Add CUDA decoder"
This reverts commit ce4c99be4e.

This was causing infinitely looping log errors in systems with no
CUDA-capable hardware when hardware decoding was enabled on video
capture devices with custom config enabled.
2024-06-26 18:43:09 -04:00
tt2468 8892edde05 libobs: Merge obs_encoder_stop() and ..._stop_internal()
There is no longer any need for them to be separate functions. This is
just code cleanup.
2024-06-26 16:42:05 -04:00
tt2468 06291c7201 libobs: Fix race when to-be-destroyed encoder group finishes stopping
Fixes a crash when the following steps occur:
- Encoder group created and then started with encoders, only the group
owning encoder refs
- Encoder group destroy called: group has `destroy_on_stop` set
without actual destroy
- Outputs holding encoders from stopping are stopped
- `remove_connection()` function destroys encoder group, releasing
encoders and causing the currently processed encoder to be destroyed
early
- parent scopes try to access destroyed encoder pointer and crash

This change moves some logic around to improve the release/destruct
order of `obs_encoder_stop()` to fix the race above.

Note: Cases of encoder errors will not destruct the group if it has
`destroy_on_stop` set, as the encoder is also not destroyed if it also
is set to destroy on stop. This part of the code should be revisited
at a later date and fixed up to prevent memory leaks.
2024-06-26 16:42:05 -04:00
tt2468 fb5bbc8575 libobs: Set encoder initialized call closer to shutdown
This is mainly code cleanup.
2024-06-26 16:42:05 -04:00
Ruwen Hahn 7d19add10b UI: Display dialog for multitrack video output audio channels mismatch 2024-06-26 16:10:15 -04:00
Vainock 4830d6903e UI: Fix capitalization of 'OBS' and 'RTMP' 2024-06-26 15:01:01 -04:00
Warchamp7 47fb194223 UI: Adjust Yami (Classic) styling 2024-06-26 14:58:13 -04:00
derrod edcda5a825 obs-x264: Ignore stats/qp file and multipass options 2024-06-26 13:58:08 -04:00
derrod 2e6e79b4f5 obs-outputs: Skip trak box if track has no data 2024-06-26 13:57:59 -04:00
derrod b34fbb116e obs-ffmpeg: Check if current NVENC configuration supports 4:4:4 encode 2024-06-26 13:57:43 -04:00
Penwywern 4517918c7a cmake: Fix FFmpeg version regex 2024-06-26 12:26:56 -04:00
derrod 6cc0e2b803 obs-outputs: Fix file splitting ts offset using video DTS instead of PTS 2024-06-25 23:36:33 -04:00
derrod ed2478535f obs-outputs: Do not create MP4 track chunks without samples 2024-06-25 23:36:33 -04:00
Ed Maste fd34bab615 UI: Link Qt::DBus on FreeBSD
As with Linux we need to link Qt::DBus on FreeBSD now that there's a
HighContrastEnabled implementation that makes use of it.

Fixes: 41ba8bdfdd ("UI: Add HighContrastEnabled implementation fo...")
2024-06-25 22:37:14 -04:00
Alex Luccisano 5f98d34e2c UI: Fix multitrack-video audio track index
Fix a minor oversight from a recent commit. Audio
track indexing in the UI is 1-based while underlying
code uses 0-based indexing.
2024-06-20 06:44:33 +02:00
tt2468 6c389271b3 obs-ffmpeg: Close VAAPI device on vaInitialize fail
On some systems (eg. mine), VAAPI fails on vaInitialize. Valgrind was
able to spot that the device was not being closed, and it appears to
have been correct. This fixes a memory leak.
2024-06-19 19:46:05 -07:00
Fabien Lavocat 6457d7b429 rtmp-services: Add Dolby Millicast 2024-06-19 13:13:16 -04:00
gxalpha 92352f18b8 cmake: Add obs-config.h to libobs headers
Adds the header to make it findable in IDEs
2024-06-19 13:08:30 -04:00
Ed Maste a189489dd2 CI: Fix FreeBSD package installation
FreeBSD's package tool is pkg(8), and install is the command verb passed
to it.

Fixes: fb4d65875e ("CI: Update Linux build scripts to use CMake p...")
2024-06-19 13:06:10 -04:00
Ruwen Hahn aa096e2ad0 UI: Disable multitrack video settings on non-win32 platforms 2024-06-19 12:50:34 -04:00
Ruwen Hahn 9d1ac8816e UI: Add supported codecs to GetClientConfiguration request 2024-06-19 12:27:13 -04:00
Ruwen Hahn 8a8019db3f UI: Only cache multitrack config URL startup argument 2024-06-19 12:08:52 -04:00
derrod a9b5968552 CI: Add tag subject to Windows patch notes 2024-06-19 10:42:11 -04:00
derrod fc05ca601a CI: Update Windows patch creation bouf version 2024-06-19 10:42:11 -04:00
Ryan Foster 32b53ea936 CI: Fix Windows Patches action release notes generation
By not specifying a checkout ref, actions/checkout does a second
checkout when this action is invoked by the Publish workflow (release
event). When this happens, it checks out the commit object from the tag,
and git can no longer locate the annotated tag that contains the release
notes. This then causes the release notes to be just the commit message
and not the annotated tag message.

The sparkle-appcast action in general and this actioo when invoked via
the Dispatch workflow do not have this issue, and they both specify the
tag as the ref.
2024-06-19 10:37:11 -04:00
Ruwen Hahn fb3e571ce8 UI: Use advanced mode audio track in multitrack video output 2024-06-18 14:13:42 -04:00
Ryan Foster 48b1298faf UI: Fix parsing of Multitrack Video stream key query parameters
The code was checking stream_key, but stream_key could be the
user-supplied value (in_stream_key) or the server-supplied value
(endpoint.authentication). The server-supplied value may lack the query
parameters set in the user-supplied value. To ensure that user-specified
query parameters (such as bandwidthtest) are passed along, parse the
user-supplied key instead of the server-supplied key.
2024-06-18 12:39:56 -04:00
derrod 608d3bfc26 UI: Set default container for beta builds to hybrid MP4 2024-06-17 12:12:20 -04:00
tt2468 e215502b62 libobs, UI: Normalize encoder group API
Modifies the encoder group API added previously to better follow the
existing libobs API naming paradigms. This also produces much more
readable code, and allows a few small benefits like only needing to
hold a reference to the encoder group, instead of every encoder.
2024-06-17 08:20:01 -07:00
tt2468 751dbdad10 libobs: Update video encoder group struct member names
Updates the struct member names of the video encoder group to be more
like what is commonly seen in OBS elsewhere.
2024-06-17 08:20:01 -07:00
Ryan Foster 9d88b632ae UI: Add step value to Multitrack Video maximum bitrate control
All other bitrate setting fields in the UI have a single-step value of
50. Without setting this, the single-step value is 1, which makes using
the scroll wheel a bit tedious.

While this could arguably be higher, let's make this consistent first
and then consider changing the values across the application later.
2024-06-17 10:36:17 -04:00
tt2468 4eef796f80 deps/media-playback: Fix init of swscale with hw decode
Checking the format of the AVCodecContext will result in using the
format of the hardware-side frames, not the software-side frames. This
uses the software frame parameters itself to initialize the swscale
context.
2024-06-16 02:47:06 +02:00
Ryan Foster 0d7478c017 UI: Set Multitrack Video maximum bitrate limit default to 0/Auto
There were some reports that the default value of 8000 was confusing
because it implies that the automaticaly selected and used value is 8000
Kbps. Set it to 0 which should hopefully make it more obvious that OBS
is not sending 0 Kbps.
2024-06-15 16:40:53 -07:00
tt2468 d1bf6f951a obs-outputs: Add multitrack flag to null output 2024-06-15 16:35:59 -07:00
Ryan Foster 62830cd8c7 UI: Fix icon-size values in theme files
The Qt docs on icon-size say its Type is Length, which is further
defined as, "A number followed by a measurement unit."

https://doc.qt.io/qt-6/stylesheet-reference.html#icon-size
https://doc.qt.io/qt-6/stylesheet-reference.html#length

This fixes the following logged Qt warning:
QCssParser::sizeValue: Too many values provided
2024-06-15 16:11:49 -07:00
cg2121 dcd2f19c83 UI: Remove redundant addAction call
This removes a redundant addAction call in the studio mode program
context menu.
2024-06-15 16:08:24 -07:00
gxalpha 7cd5ede1e0 UI: Initialize YoutubeChatDock chat input members in constructor
Currently, the chat input elements (lineEdit, sendButton, and
chatLayout) are initialized when the QCefWidget gets set. This is
problematic behavior that only happened to work because we're a bit
lucky: The chat is only enabled after a widget is set, and it's only set
once. Without those conditions, the chat dock would crash when enabling
the chat before a widget is set, and the elements would get recreated if
the widget is set a second time, resulting in the original elements not
getting freed and leaking.
Moving the element creation to the constructor fixes both of these
problems, as now they're created immediately and only once.

Detected by PVS-Studio.
2024-06-15 16:06:38 -07:00
gxalpha f462ffb224 UI: Initialize max length of LineEditAutoResize in constructor
LineEditAutoResize didn't have its maxLength initialized in the
constructor, leaving it to be a random value until set via setMaxLength.
The one place where LineEditAutoResize was used immediately set this
after calling the constructor, but if we use this anywhere else in the
future it makes sense to have this initialized.
As it is meant to mostly behave like a QLineEdit, lets use the same
default value of 32767.

Detected by PVS-Studio.
2024-06-15 16:06:38 -07:00
Ruwen Hahn 600a564039 UI: Add composition_gpu_index to multitrack video postdata 2024-06-14 20:08:26 -04:00
Alex Luccisano d7e2636316 UI: Change multitrack video configId
Schema has changed this field from "obsConfigId" to
"clientConfigId". Updated the name to match.
2024-06-14 20:08:26 -04:00
derrod d2a7f01295 updater: Use static blake2 and fix building with Debug runtimes 2024-06-14 18:10:04 -04:00
derrod 2d489fc54e deps/blake2: Add static blake2 library for Windows updater 2024-06-14 18:10:04 -04:00
Ruwen Hahn e4305b0a50 UI: Hide multitrack video options for custom output
Custom output doesn't currently allow specifying a config URL, so
disable relevant settings for now
2024-06-14 17:39:11 -04:00
tytan652 508f9c2e3c obs-scripting: Refactor Lua C paths 2024-06-14 17:34:14 -04:00
tytan652 b16516a3fa Revert "cmake: Fix script plugin path on Linux with CMake 3"
This reverts commit 7a4cb085ba.
2024-06-14 17:34:14 -04:00
Ruwen Hahn 40bf8b3c06 UI: Make audio_configurations.vod optional
This is only required if VOD is supported by the service and VOD
track is enabled; otherwise there's no need to supply the key
or an empty array
2024-06-14 17:33:31 -04:00
Ryan Foster 021adac2d0 UI: Fix submenu arrow indicator position in menus
Commit b11d61c89f added padding-right to
provide some minimal padding for this element. This was seemingly
accounted for in the Yami Base Theme (Yami.obt), but was missed in the
Yami Classic Variant Theme (Yami_Classic.ovt).

Re-add the padding-right to restore the padding.
2024-06-14 14:43:40 -04:00
Ryan Foster b1643c2ac9 UI: Add unit suffix to Multitrack Video bitrate limit control 2024-06-14 14:42:03 -04:00
Andrew Francis 14d2c80560 UI: Re-arrange multitrack-video schema to version 2024-06-04
Co-authored-by: Ruwen Hahn <haruwenz@twitch.tv>
2024-06-14 13:12:19 -04:00
Andrew Francis ce4171908b UI: Remove default values from multitrack-video.hpp
These are always overridden in goliveapi-postdata.cpp

Co-authored-by: Ruwen Hahn <haruwenz@twitch.tv>
2024-06-14 13:12:19 -04:00
Ryan Foster 77d31fa33f UI: Restore visibility of Advanced Settings warning
The warning message when changing Advanced settings that require a
restart was lost. Restore it.

Amends commit 7d55942601.
2024-06-13 14:42:25 -04:00
Ryan Foster 70307a5d25 CI: Add updateChannel to check-tag in Publish workflow
This was missing even though we try to set it later.
2024-06-13 13:28:04 -04:00
Ryan Foster 0ea90380b4 Revert "CI: Remove redundant checkout step in Windows Patches job"
This reverts commit 8fcdfb815f.

This checkout is not redundant. It is required so that the
windows-patches action files can actually be found.
2024-06-12 19:41:48 -04:00
Ryan Foster 8fcdfb815f CI: Remove redundant checkout step in Windows Patches job 2024-06-12 19:04:24 -04:00
derrod 718bd0b265 UI: Use unique_ptr for theme objects 2024-06-12 18:41:11 -04:00
derrod 4a46d2d722 UI: Fix themeDir buffer being resized incorrectly
c677bac875 changed the order here, but
this also resulted in the string having whatever size was necessary for
the install data path, rather than being large enough to fit a userdata
path. To fix this, move the resize operaetion *after* the buit-in
themes are searched, and also bump it to 1024 just to be sure.

This resulted in a crash due to a bug in os_get_path_internal() which
will need to be fixed separately.
2024-06-12 17:57:02 -04:00
Ryan Foster e454f488aa obs-websocket: Update version to 5.5.1
Changelog:
obsproject/obs-websocket@f8bc7c4f59
2024-06-11 19:05:02 -04:00
derrod 21adf0930f UI: Prevent recursion in theme dependencies 2024-06-11 18:25:40 -04:00
Ryan Foster cfd692ca15 updater: Fix defaultlib conflict
Trying to build in Debug fails due a default lib conflict:
LNK4098: defaultlib 'MSVCRTD' conflicts with use of other libs; use
/NODEFAULTLIB:library

Instead, we can set the updater to always use /MT instead of /MTd.
2024-06-11 18:25:05 -04:00
Ruwen Hahn 0f4e33c33e UI: Discard state if stream attempt is canceled
Repro steps:
1. Enable an incompatible setting in settings
2. Start stream, hit cancel on incompatible settings dialog
3. Disable incompatible setting in settings
4. Start stream -> crash
2024-06-11 13:27:15 -04:00
derrod 0cfae862cd updater: Compile with static VC runtime 2024-06-11 13:24:07 -04:00
derrod 6779052902 updater: Check and update VC runtime version 2024-06-11 13:24:07 -04:00
Ryan Foster 051c11e7b2 UI: Translate Multitrack Video error dialog buttons
The Yes and No standard buttons are not translated unless we manually
set the translated text ourselves.
2024-06-11 13:22:31 -04:00
Ryan Foster e774bd90b8
Merge pull request #10819 from derrod/update-bouf
CI: Update bouf release
2024-06-10 17:33:50 -04:00
derrod 5bc1d31a98 CI: Update windows signing workflow commit 2024-06-10 17:25:25 -04:00
derrod d2b05a6e0c CI: Update bouf release in Windows Signing action 2024-06-10 17:21:36 -04:00
derrod 86502764b9 obs-ffmpeg: Set encoder error message for CUDA errors 2024-06-10 17:08:57 -04:00
tytan652 c677bac875 UI: Avoid allowing to override provided themes 2024-06-10 17:01:21 -04:00
derrod 00c68981ab UI: Check VC++ Runtime version on startup 2024-06-10 16:51:50 -04:00
Warchamp7 4e13cff8f1 UI: Center widgets in vertical mixer layout 2024-06-10 16:48:32 -04:00
gxalpha c7dc09e862 UI: Fix mute checkbox positioning on macOS
Likely related to QTBUG-2699, QMacStyle appears to screw up the size of
this checkbox. Ignoring the style's layout rect fixes this.
2024-06-10 16:48:32 -04:00
Warchamp7 a989fefa0b UI: Fix audio meter and slider blocking mousewheel 2024-06-10 16:48:32 -04:00
Warchamp7 86c337d4e7 UI: Adjust audio mixer slider size and spacing 2024-06-10 16:48:32 -04:00
Warchamp7 56d6fb4c62 UI: Group horizontal audio mixer buttons 2024-06-10 16:48:32 -04:00
tytan652 4b187ed38c cmake: Copy shared library soname file to rundir on Linux
Soname files are required since 1d8c377240
2024-06-10 16:32:11 -04:00
Warchamp7 7d55942601 UI: Clean up Settings widget structure and styling 2024-06-10 15:33:50 -04:00
gxalpha 14fa71f749 UI: Connect replay buffer stopping to signal instead of slot 2024-06-09 16:12:02 -04:00
derrod 0680b642e9 UI: Always show chapter marker hotkey 2024-06-08 17:03:09 -07:00
gxalpha 298e858f63 UI: Improve macOS properties tooltip spacing workaround
QMacStyle appears to have an issue where it messes up the positions of
some widgets. The previous workaround added extra spacing to force the
icon further to the right. Forcing the widget rectangle to be used
instead of the one made by the style also fixes this, arguably in a
nicer way.
See also b760b24ff0 which does this for
checkboxes in the source tree.
2024-06-08 16:59:38 -07:00
tytan652 7a4cb085ba cmake: Fix script plugin path on Linux with CMake 3 2024-06-08 17:08:25 -04:00
Richard Stanway f9f4171d56 UI: Add null checks before doing some API calls
Harmless, but generated a debug warning for null pointers passed into
the API.
2024-06-08 15:25:04 -04:00
derrod 51e3bd5e3d UI: Remove unnecessary string copy from log filter 2024-06-08 14:06:23 -04:00
derrod 30f174b8bb CI: Fix input variable name when getting Windows release notes 2024-06-08 13:25:17 -04:00
200 changed files with 2360 additions and 1713 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
View file

@ -9,6 +9,7 @@
!/docs
!/libobs*
!/plugins
!/shared
!/test
!/UI
!.cirrus.xml

View file

@ -65,9 +65,6 @@ bool AbsoluteSlider::eventFilter(QObject *obj, QEvent *event)
}
}
if (event->type() == QEvent::Wheel)
return true;
return QSlider::eventFilter(obj, event);
}

View file

@ -1,7 +1,7 @@
#pragma once
#include <QMouseEvent>
#include "slider-ignorewheel.hpp"
#include <slider-ignorewheel.hpp>
class AbsoluteSlider : public SliderIgnoreScroll {
Q_OBJECT

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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_);
}

View file

@ -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);

View file

@ -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);

View file

@ -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")

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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>

View file

@ -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]"

View file

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

View file

@ -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 {

View file

@ -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);
}

View file

@ -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

View file

@ -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;

View file

@ -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>

View file

@ -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)

View file

@ -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>

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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>

View file

@ -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;
}

View file

@ -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();

View file

@ -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)
{

View file

@ -1,6 +1,6 @@
#include "lineedit-autoresize.hpp"
LineEditAutoResize::LineEditAutoResize()
LineEditAutoResize::LineEditAutoResize() : m_maxLength(32767)
{
connect(this, &LineEditAutoResize::textChanged, this,
&LineEditAutoResize::checkTextLength);

View file

@ -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),

View file

@ -4,7 +4,7 @@
#include <QTimer>
#include <vector>
#include <obs.hpp>
#include "qt-wrappers.hpp"
#include <qt-wrappers.hpp>
class Ui_MediaControls;

View file

@ -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,

View file

@ -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);

View file

@ -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 &current_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)};
}

View file

@ -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;

View file

@ -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())

View file

@ -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();

View file

@ -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>

View file

@ -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)
{

View file

@ -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;

View file

@ -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>

View file

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

View file

@ -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>

View file

@ -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>

View file

@ -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);
}

View file

@ -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>")

View file

@ -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")

View file

@ -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;
}
}

View file

@ -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>

View file

@ -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"

View file

@ -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"

View file

@ -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

View file

@ -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>

View file

@ -21,8 +21,7 @@
#include <QDialogButtonBox>
#include <memory>
#include <obs.hpp>
#include "properties-view.hpp"
#include <properties-view.hpp>
class OBSBasic;
class QMenu;

View file

@ -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>

View file

@ -22,8 +22,7 @@
#include <functional>
#include <obs.hpp>
#include "properties-view.hpp"
#include <properties-view.hpp>
class OBSBasic;

View file

@ -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>

View file

@ -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;

View file

@ -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;
}

View file

@ -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();
};

View file

@ -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);

View file

@ -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;

View file

@ -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>

View file

@ -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);

View file

@ -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);

View file

@ -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;
}

View file

@ -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>

View file

@ -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 {

View file

@ -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

View file

@ -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);

View file

@ -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 {

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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);

View file

@ -24,7 +24,7 @@
#include <QToolButton>
#include <QFileDialog>
#include "qt-wrappers.hpp"
#include <qt-wrappers.hpp>
enum MissingFilesColumn {
Source,

View file

@ -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)

View file

@ -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"

View file

@ -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>

View file

@ -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>

View file

@ -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"

View file

@ -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)/*})

View file

@ -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()

View file

@ -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
View file

@ -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)

View file

@ -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()

View file

@ -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

View file

@ -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(&current_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(&current_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(&current_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);
}

View file

@ -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;

View file

@ -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);

View file

@ -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