123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454 |
- /******************************************************************************
- Copyright (C) 2023 by Lain Bailey <[email protected]>
- Zachary Lund <[email protected]>
- Philippe Groarke <[email protected]>
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- ******************************************************************************/
- #include "OBSBasic.hpp"
- #include <components/UIValidation.hpp>
- #ifdef YOUTUBE_ENABLED
- #include <docks/YouTubeAppDock.hpp>
- #include <utility/YoutubeApiWrappers.hpp>
- #endif
- #include <qt-wrappers.hpp>
- #define STREAMING_START "==== Streaming Start ==============================================="
- #define STREAMING_STOP "==== Streaming Stop ================================================"
- void OBSBasic::DisplayStreamStartError()
- {
- QString message = !outputHandler->lastError.empty() ? QTStr(outputHandler->lastError.c_str())
- : QTStr("Output.StartFailedGeneric");
- emit StreamingStopped();
- if (sysTrayStream) {
- sysTrayStream->setText(QTStr("Basic.Main.StartStreaming"));
- sysTrayStream->setEnabled(true);
- }
- QMessageBox::critical(this, QTStr("Output.StartStreamFailed"), message);
- }
- void OBSBasic::StartStreaming()
- {
- if (outputHandler->StreamingActive())
- return;
- if (disableOutputsRef)
- return;
- if (auth && auth->broadcastFlow()) {
- if (!broadcastActive && !broadcastReady) {
- QMessageBox no_broadcast(this);
- no_broadcast.setText(QTStr("Output.NoBroadcast.Text"));
- QPushButton *SetupBroadcast =
- no_broadcast.addButton(QTStr("Basic.Main.SetupBroadcast"), QMessageBox::YesRole);
- no_broadcast.setDefaultButton(SetupBroadcast);
- no_broadcast.addButton(QTStr("Close"), QMessageBox::NoRole);
- no_broadcast.setIcon(QMessageBox::Information);
- no_broadcast.setWindowTitle(QTStr("Output.NoBroadcast.Title"));
- no_broadcast.exec();
- if (no_broadcast.clickedButton() == SetupBroadcast)
- QMetaObject::invokeMethod(this, "SetupBroadcast");
- return;
- }
- }
- emit StreamingPreparing();
- if (sysTrayStream) {
- sysTrayStream->setEnabled(false);
- sysTrayStream->setText("Basic.Main.PreparingStream");
- }
- auto finish_stream_setup = [&](bool setupStreamingResult) {
- if (!setupStreamingResult) {
- DisplayStreamStartError();
- return;
- }
- OnEvent(OBS_FRONTEND_EVENT_STREAMING_STARTING);
- SaveProject();
- emit StreamingStarting(autoStartBroadcast);
- if (sysTrayStream)
- sysTrayStream->setText("Basic.Main.Connecting");
- if (!outputHandler->StartStreaming(service)) {
- DisplayStreamStartError();
- return;
- }
- if (autoStartBroadcast) {
- emit BroadcastStreamStarted(autoStopBroadcast);
- broadcastActive = true;
- }
- bool recordWhenStreaming =
- config_get_bool(App()->GetUserConfig(), "BasicWindow", "RecordWhenStreaming");
- if (recordWhenStreaming)
- StartRecording();
- bool replayBufferWhileStreaming =
- config_get_bool(App()->GetUserConfig(), "BasicWindow", "ReplayBufferWhileStreaming");
- if (replayBufferWhileStreaming)
- StartReplayBuffer();
- #ifdef YOUTUBE_ENABLED
- if (!autoStartBroadcast)
- OBSBasic::ShowYouTubeAutoStartWarning();
- #endif
- };
- setupStreamingGuard = outputHandler->SetupStreaming(service, finish_stream_setup);
- }
- void OBSBasic::StopStreaming()
- {
- SaveProject();
- if (outputHandler->StreamingActive())
- outputHandler->StopStreaming(streamingStopping);
- // special case: force reset broadcast state if
- // no autostart and no autostop selected
- if (!autoStartBroadcast && !broadcastActive) {
- broadcastActive = false;
- autoStartBroadcast = true;
- autoStopBroadcast = true;
- broadcastReady = false;
- }
- if (autoStopBroadcast) {
- broadcastActive = false;
- broadcastReady = false;
- }
- emit BroadcastStreamReady(broadcastReady);
- OnDeactivate();
- bool recordWhenStreaming = config_get_bool(App()->GetUserConfig(), "BasicWindow", "RecordWhenStreaming");
- bool keepRecordingWhenStreamStops =
- config_get_bool(App()->GetUserConfig(), "BasicWindow", "KeepRecordingWhenStreamStops");
- if (recordWhenStreaming && !keepRecordingWhenStreamStops)
- StopRecording();
- bool replayBufferWhileStreaming =
- config_get_bool(App()->GetUserConfig(), "BasicWindow", "ReplayBufferWhileStreaming");
- bool keepReplayBufferStreamStops =
- config_get_bool(App()->GetUserConfig(), "BasicWindow", "KeepReplayBufferStreamStops");
- if (replayBufferWhileStreaming && !keepReplayBufferStreamStops)
- StopReplayBuffer();
- }
- void OBSBasic::ForceStopStreaming()
- {
- SaveProject();
- if (outputHandler->StreamingActive())
- outputHandler->StopStreaming(true);
- // special case: force reset broadcast state if
- // no autostart and no autostop selected
- if (!autoStartBroadcast && !broadcastActive) {
- broadcastActive = false;
- autoStartBroadcast = true;
- autoStopBroadcast = true;
- broadcastReady = false;
- }
- if (autoStopBroadcast) {
- broadcastActive = false;
- broadcastReady = false;
- }
- emit BroadcastStreamReady(broadcastReady);
- OnDeactivate();
- bool recordWhenStreaming = config_get_bool(App()->GetUserConfig(), "BasicWindow", "RecordWhenStreaming");
- bool keepRecordingWhenStreamStops =
- config_get_bool(App()->GetUserConfig(), "BasicWindow", "KeepRecordingWhenStreamStops");
- if (recordWhenStreaming && !keepRecordingWhenStreamStops)
- StopRecording();
- bool replayBufferWhileStreaming =
- config_get_bool(App()->GetUserConfig(), "BasicWindow", "ReplayBufferWhileStreaming");
- bool keepReplayBufferStreamStops =
- config_get_bool(App()->GetUserConfig(), "BasicWindow", "KeepReplayBufferStreamStops");
- if (replayBufferWhileStreaming && !keepReplayBufferStreamStops)
- StopReplayBuffer();
- }
- void OBSBasic::StreamDelayStarting(int sec)
- {
- emit StreamingStarted(true);
- if (sysTrayStream) {
- sysTrayStream->setText(QTStr("Basic.Main.StopStreaming"));
- sysTrayStream->setEnabled(true);
- }
- ui->statusbar->StreamDelayStarting(sec);
- OnActivate();
- }
- void OBSBasic::StreamDelayStopping(int sec)
- {
- emit StreamingStopped(true);
- if (sysTrayStream) {
- sysTrayStream->setText(QTStr("Basic.Main.StartStreaming"));
- sysTrayStream->setEnabled(true);
- }
- ui->statusbar->StreamDelayStopping(sec);
- OnEvent(OBS_FRONTEND_EVENT_STREAMING_STOPPING);
- }
- void OBSBasic::StreamingStart()
- {
- emit StreamingStarted();
- OBSOutputAutoRelease output = obs_frontend_get_streaming_output();
- ui->statusbar->StreamStarted(output);
- if (sysTrayStream) {
- sysTrayStream->setText(QTStr("Basic.Main.StopStreaming"));
- sysTrayStream->setEnabled(true);
- }
- #ifdef YOUTUBE_ENABLED
- if (!autoStartBroadcast) {
- // get a current stream key
- obs_service_t *service_obj = GetService();
- OBSDataAutoRelease settings = obs_service_get_settings(service_obj);
- std::string key = obs_data_get_string(settings, "stream_id");
- if (!key.empty() && !youtubeStreamCheckThread) {
- youtubeStreamCheckThread = CreateQThread([this, key] { YoutubeStreamCheck(key); });
- youtubeStreamCheckThread->setObjectName("YouTubeStreamCheckThread");
- youtubeStreamCheckThread->start();
- }
- }
- #endif
- OnEvent(OBS_FRONTEND_EVENT_STREAMING_STARTED);
- OnActivate();
- #ifdef YOUTUBE_ENABLED
- if (YouTubeAppDock::IsYTServiceSelected())
- youtubeAppDock->IngestionStarted();
- #endif
- blog(LOG_INFO, STREAMING_START);
- }
- void OBSBasic::StreamStopping()
- {
- emit StreamingStopping();
- if (sysTrayStream)
- sysTrayStream->setText(QTStr("Basic.Main.StoppingStreaming"));
- streamingStopping = true;
- OnEvent(OBS_FRONTEND_EVENT_STREAMING_STOPPING);
- }
- void OBSBasic::StreamingStop(int code, QString last_error)
- {
- const char *errorDescription = "";
- DStr errorMessage;
- bool use_last_error = false;
- bool encode_error = false;
- bool should_reconnect = false;
- /* Ignore stream key error for multitrack output if its internal reconnect handling is active. */
- if (code == OBS_OUTPUT_INVALID_STREAM && outputHandler->multitrackVideo &&
- outputHandler->multitrackVideo->RestartOnError()) {
- code = OBS_OUTPUT_SUCCESS;
- should_reconnect = true;
- }
- switch (code) {
- case OBS_OUTPUT_BAD_PATH:
- errorDescription = Str("Output.ConnectFail.BadPath");
- break;
- case OBS_OUTPUT_CONNECT_FAILED:
- use_last_error = true;
- errorDescription = Str("Output.ConnectFail.ConnectFailed");
- break;
- case OBS_OUTPUT_INVALID_STREAM:
- errorDescription = Str("Output.ConnectFail.InvalidStream");
- break;
- case OBS_OUTPUT_ENCODE_ERROR:
- encode_error = true;
- break;
- case OBS_OUTPUT_HDR_DISABLED:
- errorDescription = Str("Output.ConnectFail.HdrDisabled");
- break;
- default:
- case OBS_OUTPUT_ERROR:
- use_last_error = true;
- errorDescription = Str("Output.ConnectFail.Error");
- break;
- case OBS_OUTPUT_DISCONNECTED:
- /* doesn't happen if output is set to reconnect. note that
- * reconnects are handled in the output, not in the UI */
- use_last_error = true;
- errorDescription = Str("Output.ConnectFail.Disconnected");
- }
- if (use_last_error && !last_error.isEmpty())
- dstr_printf(errorMessage, "%s\n\n%s", errorDescription, QT_TO_UTF8(last_error));
- else
- dstr_copy(errorMessage, errorDescription);
- ui->statusbar->StreamStopped();
- emit StreamingStopped();
- if (sysTrayStream) {
- sysTrayStream->setText(QTStr("Basic.Main.StartStreaming"));
- sysTrayStream->setEnabled(true);
- }
- streamingStopping = false;
- OnEvent(OBS_FRONTEND_EVENT_STREAMING_STOPPED);
- OnDeactivate();
- #ifdef YOUTUBE_ENABLED
- if (YouTubeAppDock::IsYTServiceSelected())
- youtubeAppDock->IngestionStopped();
- #endif
- blog(LOG_INFO, STREAMING_STOP);
- if (encode_error) {
- QString msg = last_error.isEmpty() ? QTStr("Output.StreamEncodeError.Msg")
- : QTStr("Output.StreamEncodeError.Msg.LastError").arg(last_error);
- OBSMessageBox::information(this, QTStr("Output.StreamEncodeError.Title"), msg);
- } else if (code != OBS_OUTPUT_SUCCESS && isVisible()) {
- OBSMessageBox::information(this, QTStr("Output.ConnectFail.Title"), QT_UTF8(errorMessage));
- } else if (code != OBS_OUTPUT_SUCCESS && !isVisible()) {
- SysTrayNotify(QT_UTF8(errorDescription), QSystemTrayIcon::Warning);
- }
- // Reset broadcast button state/text
- if (!broadcastActive)
- SetBroadcastFlowEnabled(auth && auth->broadcastFlow());
- if (should_reconnect)
- QMetaObject::invokeMethod(this, "StartStreaming", Qt::QueuedConnection);
- }
- void OBSBasic::StreamActionTriggered()
- {
- if (outputHandler->StreamingActive()) {
- bool confirm = config_get_bool(App()->GetUserConfig(), "BasicWindow", "WarnBeforeStoppingStream");
- #ifdef YOUTUBE_ENABLED
- if (isVisible() && auth && IsYouTubeService(auth->service()) && autoStopBroadcast) {
- QMessageBox::StandardButton button = OBSMessageBox::question(
- this, QTStr("ConfirmStop.Title"), QTStr("YouTube.Actions.AutoStopStreamingWarning"),
- QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
- if (button == QMessageBox::No)
- return;
- confirm = false;
- }
- #endif
- if (confirm && isVisible()) {
- QMessageBox::StandardButton button =
- OBSMessageBox::question(this, QTStr("ConfirmStop.Title"), QTStr("ConfirmStop.Text"),
- QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
- if (button == QMessageBox::No)
- return;
- }
- StopStreaming();
- } else {
- if (!UIValidation::NoSourcesConfirmation(this))
- return;
- Auth *auth = GetAuth();
- auto action = (auth && auth->external()) ? StreamSettingsAction::ContinueStream
- : UIValidation::StreamSettingsConfirmation(this, service);
- switch (action) {
- case StreamSettingsAction::ContinueStream:
- break;
- case StreamSettingsAction::OpenSettings:
- on_action_Settings_triggered();
- return;
- case StreamSettingsAction::Cancel:
- return;
- }
- bool confirm = config_get_bool(App()->GetUserConfig(), "BasicWindow", "WarnBeforeStartingStream");
- bool bwtest = false;
- if (this->auth) {
- OBSDataAutoRelease settings = obs_service_get_settings(service);
- bwtest = obs_data_get_bool(settings, "bwtest");
- // Disable confirmation if this is going to open broadcast setup
- if (auth && auth->broadcastFlow() && !broadcastReady && !broadcastActive)
- confirm = false;
- }
- if (bwtest && isVisible()) {
- QMessageBox::StandardButton button = OBSMessageBox::question(this, QTStr("ConfirmBWTest.Title"),
- QTStr("ConfirmBWTest.Text"));
- if (button == QMessageBox::No)
- return;
- } else if (confirm && isVisible()) {
- QMessageBox::StandardButton button =
- OBSMessageBox::question(this, QTStr("ConfirmStart.Title"), QTStr("ConfirmStart.Text"),
- QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
- if (button == QMessageBox::No)
- return;
- }
- StartStreaming();
- }
- }
- bool OBSBasic::StreamingActive()
- {
- if (!outputHandler)
- return false;
- return outputHandler->StreamingActive();
- }
|