0
0
mirror of https://github.com/obsproject/obs-studio.git synced 2024-09-19 20:32:15 +02:00

UI: Remove QFuture usage

`QFuture`s are broken on older Qt versions, even with the deadlock
workaround (see <https://github.com/obsproject/obs-studio/issues/10929>)
This commit is contained in:
Ruwen Hahn 2024-07-08 14:07:10 +02:00 committed by Ryan Foster
parent b74e7ede0e
commit 7327663112
10 changed files with 105 additions and 135 deletions

View File

@ -98,8 +98,6 @@ target_sources(
multitrack-video-error.hpp
multitrack-video-output.cpp
multitrack-video-output.hpp
qt-helpers.cpp
qt-helpers.hpp
system-info.hpp)
if(OS_WINDOWS)

View File

@ -1,7 +1,6 @@
#pragma once
#include <obs.hpp>
#include <QFuture>
#include <QString>
#include "models/multitrack-video.hpp"

View File

@ -35,7 +35,6 @@
#include "goliveapi-postdata.hpp"
#include "goliveapi-network.hpp"
#include "multitrack-video-error.hpp"
#include "qt-helpers.hpp"
#include "models/multitrack-video.hpp"
Qt::ConnectionType BlockingConnectionTypeFor(QObject *object)

View File

@ -4,12 +4,11 @@
#include <util/config-file.h>
#include <atomic>
#include <mutex>
#include <optional>
#include <vector>
#include <qobject.h>
#include <QFuture>
#include <QFutureSynchronizer>
#include <QObject>
#define NOMINMAX

View File

@ -1,10 +0,0 @@
#include "qt-helpers.hpp"
QFuture<void> CreateFuture()
{
QPromise<void> promise;
auto future = promise.future();
promise.start();
promise.finish();
return future;
}

View File

@ -1,46 +0,0 @@
#pragma once
#include <functional>
#include <QFuture>
#include <QtGlobal>
template<typename T> struct FutureHolder {
std::function<void()> cancelAll;
QFuture<T> future;
};
QFuture<void> CreateFuture();
template<typename T> inline QFuture<T> PreventFutureDeadlock(QFuture<T> future)
{
/*
* QFutures deadlock if there are continuations on the same thread that
* need to wait for the previous continuation to finish, see
* https://github.com/qt/qtbase/commit/59e21a536f7f81625216dc7a621e7be59919da33
*
* related bugs:
* https://bugreports.qt.io/browse/QTBUG-119406
* https://bugreports.qt.io/browse/QTBUG-119103
* https://bugreports.qt.io/browse/QTBUG-117918
* https://bugreports.qt.io/browse/QTBUG-119579
* https://bugreports.qt.io/browse/QTBUG-119810
* @RytoEX's summary:
* QTBUG-119406 and QTBUG-119103 affect Qt 6.6.0 and are fixed in Qt 6.6.2 and 6.7.0+.
* QTBUG-119579 and QTBUG-119810 affect Qt 6.6.1 and are fixed in Qt 6.6.2 and 6.7.0+.
* QTBUG-117918 is the only strange one that seems to possibly affect all Qt 6.x versions
* until 6.6.2, but only in Debug builds.
*
* To fix this, move relevant QFutures to another thread before resuming
* on main thread for affected Qt versions
*/
#if (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)) && \
(QT_VERSION < QT_VERSION_CHECK(6, 6, 2))
if (future.isFinished()) {
return future;
}
return future.then(QtFuture::Launch::Async, [](T val) { return val; });
#else
return future;
#endif
}

View File

@ -2,7 +2,7 @@
#include <algorithm>
#include <cinttypes>
#include <QMessageBox>
#include <QPromise>
#include <QThreadPool>
#include "qt-wrappers.hpp"
#include "audio-encoders.hpp"
#include "multitrack-video-error.hpp"
@ -183,6 +183,28 @@ static void OBSDeactivateVirtualCam(void *data, calldata_t * /* params */)
/* ------------------------------------------------------------------------ */
struct StartMultitrackVideoStreamingGuard {
StartMultitrackVideoStreamingGuard()
{
future = guard.get_future().share();
};
~StartMultitrackVideoStreamingGuard() { guard.set_value(); }
std::shared_future<void> GetFuture() const { return future; }
static std::shared_future<void> MakeReadyFuture()
{
StartMultitrackVideoStreamingGuard guard;
return guard.GetFuture();
}
private:
std::promise<void> guard;
std::shared_future<void> future;
};
/* ------------------------------------------------------------------------ */
static bool CreateSimpleAACEncoder(OBSEncoder &res, int bitrate,
const char *name, size_t idx)
{
@ -520,8 +542,9 @@ struct SimpleOutput : BasicOutputHandler {
bool IsVodTrackEnabled(obs_service_t *service);
void SetupVodTrack(obs_service_t *service);
virtual FutureHolder<bool>
SetupStreaming(obs_service_t *service) override;
virtual std::shared_future<void>
SetupStreaming(obs_service_t *service,
SetupStreamingContinuation_t continuation) override;
virtual bool StartStreaming(obs_service_t *service) override;
virtual bool StartRecording() override;
virtual bool StartReplayBuffer() override;
@ -1118,7 +1141,9 @@ const char *FindAudioEncoderFromCodec(const char *type)
return nullptr;
}
FutureHolder<bool> SimpleOutput::SetupStreaming(obs_service_t *service)
std::shared_future<void>
SimpleOutput::SetupStreaming(obs_service_t *service,
SetupStreamingContinuation_t continuation)
{
if (!Active())
SetupOutputs();
@ -1130,8 +1155,10 @@ FutureHolder<bool> SimpleOutput::SetupStreaming(obs_service_t *service)
/* --------------------- */
const char *type = GetStreamOutputType(service);
if (!type)
return {[] {}, CreateFuture().then([] { return false; })};
if (!type) {
continuation(false);
return StartMultitrackVideoStreamingGuard::MakeReadyFuture();
}
auto audio_bitrate = GetAudioBitrate();
auto vod_track_mixer = IsVodTrackEnabled(service) ? std::optional{1}
@ -1182,12 +1209,11 @@ FutureHolder<bool> SimpleOutput::SetupStreaming(obs_service_t *service)
return true;
};
auto holder = SetupMultitrackVideo(
return SetupMultitrackVideo(
service, GetSimpleAACEncoderForBitrate(audio_bitrate), 0,
vod_track_mixer);
auto future = PreventFutureDeadlock(holder.future)
.then(main, handle_multitrack_video_result);
return {holder.cancelAll, future};
vod_track_mixer, [&, continuation](std::optional<bool> res) {
continuation(handle_multitrack_video_result(res));
});
}
static inline bool ServiceSupportsVodTrack(const char *service);
@ -1567,8 +1593,9 @@ struct AdvancedOutput : BasicOutputHandler {
void SetupOutputs() override;
int GetAudioBitrate(size_t i, const char *id) const;
virtual FutureHolder<bool>
SetupStreaming(obs_service_t *service) override;
virtual std::shared_future<void>
SetupStreaming(obs_service_t *service,
SetupStreamingContinuation_t continuation) override;
virtual bool StartStreaming(obs_service_t *service) override;
virtual bool StartRecording() override;
virtual bool StartReplayBuffer() override;
@ -2247,7 +2274,9 @@ inline void AdvancedOutput::SetupVodTrack(obs_service_t *service)
clear_archive_encoder(streamOutput, ADV_ARCHIVE_NAME);
}
FutureHolder<bool> AdvancedOutput::SetupStreaming(obs_service_t *service)
std::shared_future<void>
AdvancedOutput::SetupStreaming(obs_service_t *service,
SetupStreamingContinuation_t continuation)
{
int multiTrackAudioMixes = config_get_int(main->Config(), "AdvOut",
"StreamMultiTrackAudioMixes");
@ -2272,8 +2301,10 @@ FutureHolder<bool> AdvancedOutput::SetupStreaming(obs_service_t *service)
/* --------------------- */
const char *type = GetStreamOutputType(service);
if (!type)
return {[] {}, CreateFuture().then(main, [] { return false; })};
if (!type) {
continuation(false);
return StartMultitrackVideoStreamingGuard::MakeReadyFuture();
}
const char *audio_encoder_id =
config_get_string(main->Config(), "AdvOut", "AudioEncoder");
@ -2339,13 +2370,13 @@ FutureHolder<bool> AdvancedOutput::SetupStreaming(obs_service_t *service)
return true;
};
auto holder =
SetupMultitrackVideo(service, audio_encoder_id,
static_cast<size_t>(streamTrackIndex),
VodTrackMixerIdx(service));
auto future = PreventFutureDeadlock(holder.future)
.then(main, handle_multitrack_video_result);
return {holder.cancelAll, future};
return SetupMultitrackVideo(
service, audio_encoder_id,
static_cast<size_t>(streamTrackIndex),
VodTrackMixerIdx(service),
[&, continuation](std::optional<bool> res) {
continuation(handle_multitrack_video_result(res));
});
}
bool AdvancedOutput::StartStreaming(obs_service_t *service)
@ -2683,14 +2714,17 @@ std::string BasicOutputHandler::GetRecordingFilename(
extern std::string DeserializeConfigText(const char *text);
FutureHolder<std::optional<bool>> BasicOutputHandler::SetupMultitrackVideo(
std::shared_future<void> BasicOutputHandler::SetupMultitrackVideo(
obs_service_t *service, std::string audio_encoder_id,
size_t main_audio_mixer, std::optional<size_t> vod_track_mixer)
size_t main_audio_mixer, std::optional<size_t> vod_track_mixer,
std::function<void(std::optional<bool>)> continuation)
{
if (!multitrackVideo)
return {[] {}, CreateFuture().then([] {
return std::optional<bool>{std::nullopt};
})};
auto start_streaming_guard =
std::make_shared<StartMultitrackVideoStreamingGuard>();
if (!multitrackVideo) {
continuation(std::nullopt);
return start_streaming_guard->GetFuture();
}
multitrackVideoActive = false;
@ -2751,10 +2785,11 @@ FutureHolder<std::optional<bool>> BasicOutputHandler::SetupMultitrackVideo(
auto stream_dump_config = GenerateMultitrackVideoStreamDumpConfig();
auto continue_on_main_thread =
[&, service = OBSService{service}](
std::optional<MultitrackVideoError> error)
-> std::optional<bool> {
auto continue_on_main_thread = [&, start_streaming_guard,
service = OBSService{service},
continuation = std::move(continuation)](
std::optional<MultitrackVideoError>
error) {
if (error) {
OBSDataAutoRelease service_settings =
obs_service_get_settings(service);
@ -2769,8 +2804,8 @@ FutureHolder<std::optional<bool>> BasicOutputHandler::SetupMultitrackVideo(
multitrackVideoActive = false;
if (!error->ShowDialog(main, multitrack_video_name))
return false;
return std::nullopt;
return continuation(false);
return continuation(std::nullopt);
}
multitrackVideoActive = true;
@ -2786,16 +2821,16 @@ FutureHolder<std::optional<bool>> BasicOutputHandler::SetupMultitrackVideo(
OBSStartStreaming, this);
stopStreaming.Connect(signal_handler, "stop", OBSStopStreaming,
this);
return true;
return continuation(true);
};
auto firstFuture = CreateFuture().then(
QThreadPool::globalInstance(),
QThreadPool::globalInstance()->start(
[=, multitrackVideo = multitrackVideo.get(),
service_name = std::string{service_name},
service = OBSService{service},
stream_dump_config = std::move(stream_dump_config)]()
-> std::optional<MultitrackVideoError> {
stream_dump_config = OBSData{stream_dump_config},
start_streaming_guard = start_streaming_guard]() mutable {
std::optional<MultitrackVideoError> error;
try {
multitrackVideo->PrepareStreaming(
main, service_name.c_str(), service,
@ -2805,16 +2840,16 @@ FutureHolder<std::optional<bool>> BasicOutputHandler::SetupMultitrackVideo(
maximum_video_tracks, custom_config,
stream_dump_config, main_audio_mixer,
vod_track_mixer);
} catch (const MultitrackVideoError &error) {
return error;
} catch (const MultitrackVideoError &error_) {
error.emplace(error_);
}
return std::nullopt;
QMetaObject::invokeMethod(main, [=] {
continue_on_main_thread(error);
});
});
auto secondFuture = firstFuture.then(main, continue_on_main_thread);
return {[=]() mutable { firstFuture.cancel(); },
PreventFutureDeadlock(secondFuture)};
return start_streaming_guard->GetFuture();
}
OBSDataAutoRelease BasicOutputHandler::GenerateMultitrackVideoStreamDumpConfig()

View File

@ -1,15 +1,15 @@
#pragma once
#include <future>
#include <memory>
#include <string>
#include <QFuture>
#include "qt-helpers.hpp"
#include "multitrack-video-output.hpp"
class OBSBasic;
using SetupStreamingContinuation_t = std::function<void(bool)>;
struct BasicOutputHandler {
OBSOutputAutoRelease fileOutput;
OBSOutputAutoRelease streamOutput;
@ -63,7 +63,9 @@ struct BasicOutputHandler {
virtual ~BasicOutputHandler(){};
virtual FutureHolder<bool> SetupStreaming(obs_service_t *service) = 0;
virtual std::shared_future<void>
SetupStreaming(obs_service_t *service,
SetupStreamingContinuation_t continuation) = 0;
virtual bool StartStreaming(obs_service_t *service) = 0;
virtual bool StartRecording() = 0;
virtual bool StartReplayBuffer() { return false; }
@ -98,9 +100,10 @@ protected:
bool overwrite, const char *format,
bool ffmpeg);
FutureHolder<std::optional<bool>> SetupMultitrackVideo(
std::shared_future<void> SetupMultitrackVideo(
obs_service_t *service, std::string audio_encoder_id,
size_t main_audio_mixer, std::optional<size_t> vod_track_mixer);
size_t main_audio_mixer, std::optional<size_t> vod_track_mixer,
std::function<void(std::optional<bool>)> continuation);
OBSDataAutoRelease GenerateMultitrackVideoStreamDumpConfig();
};

View File

@ -2038,7 +2038,9 @@ void OBSBasic::ResetOutputs()
bool advOut = astrcmpi(mode, "Advanced") == 0;
if ((!outputHandler || !outputHandler->Active()) &&
startStreamingFuture.future.isFinished()) {
(!setupStreamingGuard.valid() ||
setupStreamingGuard.wait_for(std::chrono::seconds{0}) ==
std::future_status::ready)) {
outputHandler.reset();
outputHandler.reset(advOut ? CreateAdvancedOutputHandler(this)
: CreateSimpleOutputHandler(this));
@ -5170,18 +5172,11 @@ void OBSBasic::ClearSceneData()
void OBSBasic::closeEvent(QCloseEvent *event)
{
if (!startStreamingFuture.future.isFinished() &&
!startStreamingFuture.future.isCanceled()) {
startStreamingFuture.future.onCanceled(
this, [basic = QPointer{this}] {
if (basic)
basic->close();
});
startStreamingFuture.cancelAll();
event->ignore();
return;
} else if (startStreamingFuture.future.isCanceled() &&
!startStreamingFuture.future.isFinished()) {
/* Wait for multitrack video stream to start/finish processing in the background */
if (setupStreamingGuard.valid() &&
setupStreamingGuard.wait_for(std::chrono::seconds{0}) !=
std::future_status::ready) {
QTimer::singleShot(1000, this, &OBSBasic::close);
event->ignore();
return;
}
@ -7153,9 +7148,8 @@ void OBSBasic::StartStreaming()
#endif
};
auto holder = outputHandler->SetupStreaming(service);
auto future = holder.future.then(this, finish_stream_setup);
startStreamingFuture = {holder.cancelAll, future};
setupStreamingGuard =
outputHandler->SetupStreaming(service, finish_stream_setup);
}
void OBSBasic::BroadcastButtonClicked()

View File

@ -23,10 +23,10 @@
#include <QWidgetAction>
#include <QSystemTrayIcon>
#include <QStyledItemDelegate>
#include <QFuture>
#include <obs.hpp>
#include <vector>
#include <memory>
#include <future>
#include "window-main.hpp"
#include "window-basic-interaction.hpp"
#include "window-basic-vcam.hpp"
@ -43,7 +43,6 @@
#include "auth-base.hpp"
#include "log-viewer.hpp"
#include "undo-stack-obs.hpp"
#include "qt-helpers.hpp"
#include <obs-frontend-internal.hpp>
@ -286,7 +285,7 @@ private:
OBSService service;
std::unique_ptr<BasicOutputHandler> outputHandler;
FutureHolder<void> startStreamingFuture;
std::shared_future<void> setupStreamingGuard;
bool streamingStopping = false;
bool recordingStopping = false;
bool replayBufferStopping = false;