The source toolbar allows quick and easy access to properties and filers, and shows common properties/features of a source type. For example, when you select a media source, VLC source, or the slideshow source, you'll get media controls to control playback of the media. If you select a text source you can edit the font, color, or text if applicable. Or if you select a capture source, you can select the display/window/etc to capture for that source. If the source toolbar is not desired and is viewed as taking up valuable space in the window, it can be disabled via the view menu. Co-authored-by: Clayton Groeneveld <claytong1214@gmail.com> Co-authored-by: Jim <obs.jim@gmail.com>
429 lines
9.6 KiB
C++
429 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:
|
|
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();
|
|
}
|