mirror of
https://github.com/obsproject/obs-studio.git
synced 2024-09-20 13:08:50 +02:00
b0fbf25e24
The play, pause and restart shortcuts were being called as global shortcuts, being triggered no matter what. This fixes this by calling the shortcut actions only when the media controls widget has focus.
535 lines
12 KiB
C++
535 lines
12 KiB
C++
#include "window-basic-main.hpp"
|
|
#include "media-controls.hpp"
|
|
#include "obs-app.hpp"
|
|
#include <QToolTip>
|
|
#include <QStyle>
|
|
#include <QMenu>
|
|
|
|
#include "ui_media-controls.h"
|
|
|
|
void MediaControls::OBSMediaStopped(void *data, calldata_t *)
|
|
{
|
|
MediaControls *media = static_cast<MediaControls *>(data);
|
|
QMetaObject::invokeMethod(media, "SetRestartState");
|
|
}
|
|
|
|
void MediaControls::OBSMediaPlay(void *data, calldata_t *)
|
|
{
|
|
MediaControls *media = static_cast<MediaControls *>(data);
|
|
QMetaObject::invokeMethod(media, "SetPlayingState");
|
|
}
|
|
|
|
void MediaControls::OBSMediaPause(void *data, calldata_t *)
|
|
{
|
|
MediaControls *media = static_cast<MediaControls *>(data);
|
|
QMetaObject::invokeMethod(media, "SetPausedState");
|
|
}
|
|
|
|
void MediaControls::OBSMediaStarted(void *data, calldata_t *)
|
|
{
|
|
MediaControls *media = static_cast<MediaControls *>(data);
|
|
QMetaObject::invokeMethod(media, "SetPlayingState");
|
|
}
|
|
|
|
void MediaControls::OBSMediaNext(void *data, calldata_t *)
|
|
{
|
|
MediaControls *media = static_cast<MediaControls *>(data);
|
|
QMetaObject::invokeMethod(media, "UpdateSlideCounter");
|
|
}
|
|
|
|
void MediaControls::OBSMediaPrevious(void *data, calldata_t *)
|
|
{
|
|
MediaControls *media = static_cast<MediaControls *>(data);
|
|
QMetaObject::invokeMethod(media, "UpdateSlideCounter");
|
|
}
|
|
|
|
MediaControls::MediaControls(QWidget *parent)
|
|
: QWidget(parent), ui(new Ui::MediaControls)
|
|
{
|
|
ui->setupUi(this);
|
|
ui->playPauseButton->setProperty("themeID", "playIcon");
|
|
ui->previousButton->setProperty("themeID", "previousIcon");
|
|
ui->nextButton->setProperty("themeID", "nextIcon");
|
|
ui->stopButton->setProperty("themeID", "stopIcon");
|
|
setFocusPolicy(Qt::StrongFocus);
|
|
|
|
connect(&mediaTimer, SIGNAL(timeout()), this,
|
|
SLOT(SetSliderPosition()));
|
|
connect(&seekTimer, SIGNAL(timeout()), this, SLOT(SeekTimerCallback()));
|
|
connect(ui->slider, SIGNAL(sliderPressed()), this,
|
|
SLOT(MediaSliderClicked()));
|
|
connect(ui->slider, SIGNAL(mediaSliderHovered(int)), this,
|
|
SLOT(MediaSliderHovered(int)));
|
|
connect(ui->slider, SIGNAL(sliderReleased()), this,
|
|
SLOT(MediaSliderReleased()));
|
|
connect(ui->slider, SIGNAL(sliderMoved(int)), this,
|
|
SLOT(MediaSliderMoved(int)));
|
|
|
|
countDownTimer = config_get_bool(App()->GlobalConfig(), "BasicWindow",
|
|
"MediaControlsCountdownTimer");
|
|
|
|
QAction *restartAction = new QAction(this);
|
|
restartAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
|
restartAction->setShortcut({Qt::Key_R});
|
|
connect(restartAction, SIGNAL(triggered()), this, SLOT(RestartMedia()));
|
|
addAction(restartAction);
|
|
|
|
QAction *sliderFoward = new QAction(this);
|
|
sliderFoward->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
|
connect(sliderFoward, SIGNAL(triggered()), this,
|
|
SLOT(MoveSliderFoward()));
|
|
sliderFoward->setShortcut({Qt::Key_Right});
|
|
addAction(sliderFoward);
|
|
|
|
QAction *sliderBack = new QAction(this);
|
|
sliderBack->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
|
connect(sliderBack, SIGNAL(triggered()), this,
|
|
SLOT(MoveSliderBackwards()));
|
|
sliderBack->setShortcut({Qt::Key_Left});
|
|
addAction(sliderBack);
|
|
|
|
QAction *playPause = new QAction(this);
|
|
playPause->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
|
connect(playPause, SIGNAL(triggered()), this,
|
|
SLOT(on_playPauseButton_clicked()));
|
|
playPause->setShortcut({Qt::Key_Space});
|
|
addAction(playPause);
|
|
}
|
|
|
|
MediaControls::~MediaControls() {}
|
|
|
|
bool MediaControls::MediaPaused()
|
|
{
|
|
OBSSource source = OBSGetStrongRef(weakSource);
|
|
if (!source) {
|
|
return false;
|
|
}
|
|
|
|
obs_media_state state = obs_source_media_get_state(source);
|
|
return state == OBS_MEDIA_STATE_PAUSED;
|
|
}
|
|
|
|
int64_t MediaControls::GetSliderTime(int val)
|
|
{
|
|
OBSSource source = OBSGetStrongRef(weakSource);
|
|
if (!source) {
|
|
return 0;
|
|
}
|
|
|
|
float percent = (float)val / (float)ui->slider->maximum();
|
|
float duration = (float)obs_source_media_get_duration(source);
|
|
int64_t seekTo = (int64_t)(percent * duration);
|
|
|
|
return seekTo;
|
|
}
|
|
|
|
void MediaControls::MediaSliderClicked()
|
|
{
|
|
OBSSource source = OBSGetStrongRef(weakSource);
|
|
if (!source) {
|
|
return;
|
|
}
|
|
|
|
obs_media_state state = obs_source_media_get_state(source);
|
|
|
|
if (state == OBS_MEDIA_STATE_PAUSED) {
|
|
prevPaused = true;
|
|
} else if (state == OBS_MEDIA_STATE_PLAYING) {
|
|
prevPaused = false;
|
|
PauseMedia();
|
|
StopMediaTimer();
|
|
}
|
|
|
|
seek = ui->slider->value();
|
|
seekTimer.start(100);
|
|
}
|
|
|
|
void MediaControls::MediaSliderReleased()
|
|
{
|
|
OBSSource source = OBSGetStrongRef(weakSource);
|
|
if (!source) {
|
|
return;
|
|
}
|
|
|
|
if (seekTimer.isActive()) {
|
|
seekTimer.stop();
|
|
if (lastSeek != seek) {
|
|
obs_source_media_set_time(source, GetSliderTime(seek));
|
|
}
|
|
|
|
seek = lastSeek = -1;
|
|
}
|
|
|
|
if (!prevPaused) {
|
|
PlayMedia();
|
|
StartMediaTimer();
|
|
}
|
|
}
|
|
|
|
void MediaControls::MediaSliderHovered(int val)
|
|
{
|
|
float seconds = ((float)GetSliderTime(val) / 1000.0f);
|
|
QToolTip::showText(QCursor::pos(), FormatSeconds((int)seconds), this);
|
|
}
|
|
|
|
void MediaControls::MediaSliderMoved(int val)
|
|
{
|
|
if (seekTimer.isActive()) {
|
|
seek = val;
|
|
}
|
|
}
|
|
|
|
void MediaControls::SeekTimerCallback()
|
|
{
|
|
if (lastSeek != seek) {
|
|
OBSSource source = OBSGetStrongRef(weakSource);
|
|
if (source) {
|
|
obs_source_media_set_time(source, GetSliderTime(seek));
|
|
}
|
|
lastSeek = seek;
|
|
}
|
|
}
|
|
|
|
void MediaControls::StartMediaTimer()
|
|
{
|
|
if (isSlideshow)
|
|
return;
|
|
|
|
if (!mediaTimer.isActive())
|
|
mediaTimer.start(1000);
|
|
}
|
|
|
|
void MediaControls::StopMediaTimer()
|
|
{
|
|
if (mediaTimer.isActive())
|
|
mediaTimer.stop();
|
|
}
|
|
|
|
void MediaControls::SetPlayingState()
|
|
{
|
|
ui->slider->setEnabled(true);
|
|
ui->playPauseButton->setProperty("themeID", "pauseIcon");
|
|
ui->playPauseButton->style()->unpolish(ui->playPauseButton);
|
|
ui->playPauseButton->style()->polish(ui->playPauseButton);
|
|
ui->playPauseButton->setToolTip(
|
|
QTStr("ContextBar.MediaControls.PauseMedia"));
|
|
|
|
prevPaused = false;
|
|
|
|
UpdateSlideCounter();
|
|
StartMediaTimer();
|
|
}
|
|
|
|
void MediaControls::SetPausedState()
|
|
{
|
|
ui->playPauseButton->setProperty("themeID", "playIcon");
|
|
ui->playPauseButton->style()->unpolish(ui->playPauseButton);
|
|
ui->playPauseButton->style()->polish(ui->playPauseButton);
|
|
ui->playPauseButton->setToolTip(
|
|
QTStr("ContextBar.MediaControls.PlayMedia"));
|
|
|
|
StopMediaTimer();
|
|
}
|
|
|
|
void MediaControls::SetRestartState()
|
|
{
|
|
ui->playPauseButton->setProperty("themeID", "restartIcon");
|
|
ui->playPauseButton->style()->unpolish(ui->playPauseButton);
|
|
ui->playPauseButton->style()->polish(ui->playPauseButton);
|
|
ui->playPauseButton->setToolTip(
|
|
QTStr("ContextBar.MediaControls.RestartMedia"));
|
|
|
|
ui->slider->setValue(0);
|
|
|
|
if (!isSlideshow) {
|
|
ui->timerLabel->setText("--:--:--");
|
|
ui->durationLabel->setText("--:--:--");
|
|
} else {
|
|
ui->timerLabel->setText("-");
|
|
ui->durationLabel->setText("-");
|
|
}
|
|
|
|
ui->slider->setEnabled(false);
|
|
|
|
StopMediaTimer();
|
|
}
|
|
|
|
void MediaControls::RefreshControls()
|
|
{
|
|
OBSSource source;
|
|
source = OBSGetStrongRef(weakSource);
|
|
|
|
uint32_t flags = 0;
|
|
const char *id = nullptr;
|
|
|
|
if (source) {
|
|
flags = obs_source_get_output_flags(source);
|
|
id = obs_source_get_unversioned_id(source);
|
|
}
|
|
|
|
if (!source || !(flags & OBS_SOURCE_CONTROLLABLE_MEDIA)) {
|
|
SetRestartState();
|
|
setEnabled(false);
|
|
hide();
|
|
return;
|
|
} else {
|
|
setEnabled(true);
|
|
show();
|
|
}
|
|
|
|
bool has_playlist = strcmp(id, "ffmpeg_source") != 0;
|
|
ui->previousButton->setVisible(has_playlist);
|
|
ui->nextButton->setVisible(has_playlist);
|
|
|
|
isSlideshow = strcmp(id, "slideshow") == 0;
|
|
ui->slider->setVisible(!isSlideshow);
|
|
ui->emptySpaceAgain->setVisible(isSlideshow);
|
|
|
|
obs_media_state state = obs_source_media_get_state(source);
|
|
|
|
switch (state) {
|
|
case OBS_MEDIA_STATE_STOPPED:
|
|
case OBS_MEDIA_STATE_ENDED:
|
|
case OBS_MEDIA_STATE_NONE:
|
|
SetRestartState();
|
|
break;
|
|
case OBS_MEDIA_STATE_PLAYING:
|
|
SetPlayingState();
|
|
break;
|
|
case OBS_MEDIA_STATE_PAUSED:
|
|
SetPausedState();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (isSlideshow)
|
|
UpdateSlideCounter();
|
|
else
|
|
SetSliderPosition();
|
|
}
|
|
|
|
OBSSource MediaControls::GetSource()
|
|
{
|
|
return OBSGetStrongRef(weakSource);
|
|
}
|
|
|
|
void MediaControls::SetSource(OBSSource source)
|
|
{
|
|
sigs.clear();
|
|
|
|
if (source) {
|
|
weakSource = OBSGetWeakRef(source);
|
|
signal_handler_t *sh = obs_source_get_signal_handler(source);
|
|
sigs.emplace_back(sh, "media_play", OBSMediaPlay, this);
|
|
sigs.emplace_back(sh, "media_pause", OBSMediaPause, this);
|
|
sigs.emplace_back(sh, "media_restart", OBSMediaPlay, this);
|
|
sigs.emplace_back(sh, "media_stopped", OBSMediaStopped, this);
|
|
sigs.emplace_back(sh, "media_started", OBSMediaStarted, this);
|
|
sigs.emplace_back(sh, "media_ended", OBSMediaStopped, this);
|
|
sigs.emplace_back(sh, "media_next", OBSMediaNext, this);
|
|
sigs.emplace_back(sh, "media_previous", OBSMediaPrevious, this);
|
|
} else {
|
|
weakSource = nullptr;
|
|
}
|
|
|
|
RefreshControls();
|
|
}
|
|
|
|
void MediaControls::SetSliderPosition()
|
|
{
|
|
OBSSource source = OBSGetStrongRef(weakSource);
|
|
if (!source) {
|
|
return;
|
|
}
|
|
|
|
float time = (float)obs_source_media_get_time(source);
|
|
float duration = (float)obs_source_media_get_duration(source);
|
|
|
|
float sliderPosition;
|
|
|
|
if (duration)
|
|
sliderPosition =
|
|
(time / duration) * (float)ui->slider->maximum();
|
|
else
|
|
sliderPosition = 0.0f;
|
|
|
|
ui->slider->setValue((int)sliderPosition);
|
|
|
|
ui->timerLabel->setText(FormatSeconds((int)(time / 1000.0f)));
|
|
|
|
if (!countDownTimer)
|
|
ui->durationLabel->setText(
|
|
FormatSeconds((int)(duration / 1000.0f)));
|
|
else
|
|
ui->durationLabel->setText(
|
|
QString("-") +
|
|
FormatSeconds((int)((duration - time) / 1000.0f)));
|
|
}
|
|
|
|
QString MediaControls::FormatSeconds(int totalSeconds)
|
|
{
|
|
int seconds = totalSeconds % 60;
|
|
int totalMinutes = totalSeconds / 60;
|
|
int minutes = totalMinutes % 60;
|
|
int hours = totalMinutes / 60;
|
|
|
|
return QString::asprintf("%02d:%02d:%02d", hours, minutes, seconds);
|
|
}
|
|
|
|
void MediaControls::on_playPauseButton_clicked()
|
|
{
|
|
OBSSource source = OBSGetStrongRef(weakSource);
|
|
if (!source) {
|
|
return;
|
|
}
|
|
|
|
obs_media_state state = obs_source_media_get_state(source);
|
|
|
|
switch (state) {
|
|
case OBS_MEDIA_STATE_STOPPED:
|
|
case OBS_MEDIA_STATE_ENDED:
|
|
RestartMedia();
|
|
break;
|
|
case OBS_MEDIA_STATE_PLAYING:
|
|
PauseMedia();
|
|
break;
|
|
case OBS_MEDIA_STATE_PAUSED:
|
|
PlayMedia();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MediaControls::RestartMedia()
|
|
{
|
|
OBSSource source = OBSGetStrongRef(weakSource);
|
|
if (source) {
|
|
obs_source_media_restart(source);
|
|
}
|
|
}
|
|
|
|
void MediaControls::PlayMedia()
|
|
{
|
|
OBSSource source = OBSGetStrongRef(weakSource);
|
|
if (source) {
|
|
obs_source_media_play_pause(source, false);
|
|
}
|
|
}
|
|
|
|
void MediaControls::PauseMedia()
|
|
{
|
|
OBSSource source = OBSGetStrongRef(weakSource);
|
|
if (source) {
|
|
obs_source_media_play_pause(source, true);
|
|
}
|
|
}
|
|
|
|
void MediaControls::StopMedia()
|
|
{
|
|
OBSSource source = OBSGetStrongRef(weakSource);
|
|
if (source) {
|
|
obs_source_media_stop(source);
|
|
}
|
|
}
|
|
|
|
void MediaControls::PlaylistNext()
|
|
{
|
|
OBSSource source = OBSGetStrongRef(weakSource);
|
|
if (source) {
|
|
obs_source_media_next(source);
|
|
}
|
|
}
|
|
|
|
void MediaControls::PlaylistPrevious()
|
|
{
|
|
OBSSource source = OBSGetStrongRef(weakSource);
|
|
if (source) {
|
|
obs_source_media_previous(source);
|
|
}
|
|
}
|
|
|
|
void MediaControls::on_stopButton_clicked()
|
|
{
|
|
StopMedia();
|
|
}
|
|
|
|
void MediaControls::on_nextButton_clicked()
|
|
{
|
|
PlaylistNext();
|
|
}
|
|
|
|
void MediaControls::on_previousButton_clicked()
|
|
{
|
|
PlaylistPrevious();
|
|
}
|
|
|
|
void MediaControls::on_durationLabel_clicked()
|
|
{
|
|
countDownTimer = !countDownTimer;
|
|
|
|
config_set_bool(App()->GlobalConfig(), "BasicWindow",
|
|
"MediaControlsCountdownTimer", countDownTimer);
|
|
|
|
if (MediaPaused())
|
|
SetSliderPosition();
|
|
}
|
|
|
|
void MediaControls::MoveSliderFoward(int seconds)
|
|
{
|
|
OBSSource source = OBSGetStrongRef(weakSource);
|
|
|
|
if (!source)
|
|
return;
|
|
|
|
int ms = obs_source_media_get_time(source);
|
|
ms += seconds * 1000;
|
|
|
|
obs_source_media_set_time(source, ms);
|
|
SetSliderPosition();
|
|
}
|
|
|
|
void MediaControls::MoveSliderBackwards(int seconds)
|
|
{
|
|
OBSSource source = OBSGetStrongRef(weakSource);
|
|
|
|
if (!source)
|
|
return;
|
|
|
|
int ms = obs_source_media_get_time(source);
|
|
ms -= seconds * 1000;
|
|
|
|
obs_source_media_set_time(source, ms);
|
|
SetSliderPosition();
|
|
}
|
|
|
|
void MediaControls::UpdateSlideCounter()
|
|
{
|
|
if (!isSlideshow)
|
|
return;
|
|
|
|
OBSSource source = OBSGetStrongRef(weakSource);
|
|
|
|
if (!source)
|
|
return;
|
|
|
|
proc_handler_t *ph = obs_source_get_proc_handler(source);
|
|
calldata_t cd = {};
|
|
|
|
proc_handler_call(ph, "current_index", &cd);
|
|
int slide = calldata_int(&cd, "current_index");
|
|
|
|
proc_handler_call(ph, "total_files", &cd);
|
|
int total = calldata_int(&cd, "total_files");
|
|
calldata_free(&cd);
|
|
|
|
if (total > 0) {
|
|
ui->timerLabel->setText(QString::number(slide + 1));
|
|
ui->durationLabel->setText(QString::number(total));
|
|
} else {
|
|
ui->timerLabel->setText("-");
|
|
ui->durationLabel->setText("-");
|
|
}
|
|
}
|