Files
obs-studio/UI/media-controls.cpp
Clayton Groeneveld ecb71e32d1 UI: Set restart state when there is no media
When there is no media, set the media to the restart state.
Currently, this only affects the VLC source, as the media
source doesn't use the OBS_MEDIA_STATE_NONE, at this time.
2020-09-03 17:09:31 -05:00

430 lines
9.6 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");
}
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");
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->setShortcut({Qt::Key_R});
connect(restartAction, SIGNAL(triggered()), this, SLOT(RestartMedia()));
addAction(restartAction);
}
MediaControls::~MediaControls()
{
delete ui;
}
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();
mediaTimer.stop();
}
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();
mediaTimer.start(1000);
}
}
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;
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);
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->timerLabel->setVisible(!isSlideshow);
ui->label->setVisible(!isSlideshow);
ui->durationLabel->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;
}
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);
} 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 = (time / duration) * (float)ui->slider->maximum();
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();
}