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:
parent
b74e7ede0e
commit
7327663112
@ -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)
|
||||
|
@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <obs.hpp>
|
||||
#include <QFuture>
|
||||
#include <QString>
|
||||
|
||||
#include "models/multitrack-video.hpp"
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -1,10 +0,0 @@
|
||||
#include "qt-helpers.hpp"
|
||||
|
||||
QFuture<void> CreateFuture()
|
||||
{
|
||||
QPromise<void> promise;
|
||||
auto future = promise.future();
|
||||
promise.start();
|
||||
promise.finish();
|
||||
return future;
|
||||
}
|
@ -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
|
||||
}
|
@ -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,
|
||||
return 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};
|
||||
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()
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user