mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-03-21 17:34:57 -05:00
Remove "Played to end" and replace it with "Playlist end" state
"Ended" can now be used to also detect the end of individual playlist items.
This commit is contained in:
parent
11fede6cc3
commit
ddc2ee4fa5
|
|
@ -128,6 +128,7 @@ AdvSceneSwitcher.condition.media="Media"
|
|||
AdvSceneSwitcher.condition.media.anyOnScene="Any media source on"
|
||||
AdvSceneSwitcher.condition.media.allOnScene="All media sources on"
|
||||
AdvSceneSwitcher.condition.media.matchOnChange="Only match on change (Note: This option will be removed in a future version - please use time constraints instead)"
|
||||
AdvSceneSwitcher.condition.media.inconsistencyInfo="Unfortunately not all media source types behave the same (e.g. Media Source vs. VLC Video Source \"Stopped\" state).\nSo please experiment what works for your setup!"
|
||||
AdvSceneSwitcher.condition.media.entry="{{mediaSources}}{{scenes}} state is {{states}} and {{timeRestrictions}} {{time}}"
|
||||
AdvSceneSwitcher.condition.video="Video"
|
||||
AdvSceneSwitcher.condition.video.condition.match="exactly matches"
|
||||
|
|
@ -527,7 +528,7 @@ AdvSceneSwitcher.mediaTab.states.Paused="Paused"
|
|||
AdvSceneSwitcher.mediaTab.states.stopped="Stopped"
|
||||
AdvSceneSwitcher.mediaTab.states.ended="Ended"
|
||||
AdvSceneSwitcher.mediaTab.states.error="Error"
|
||||
AdvSceneSwitcher.mediaTab.states.playedToEnd="Played to end"
|
||||
AdvSceneSwitcher.mediaTab.states.playlistEnd="Ended(Playlist)"
|
||||
AdvSceneSwitcher.mediaTab.states.any="Any"
|
||||
AdvSceneSwitcher.mediaTab.timeRestriction.none="None"
|
||||
AdvSceneSwitcher.mediaTab.timeRestriction.shorter="Time shorter"
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ enum class MediaState {
|
|||
// Just a marker
|
||||
LAST_OBS_MEDIA_STATE,
|
||||
// states added for use in the plugin
|
||||
PLAYED_TO_END = custom_media_states_offset,
|
||||
PLAYLIST_ENDED = custom_media_states_offset,
|
||||
ANY,
|
||||
};
|
||||
|
||||
|
|
@ -58,6 +58,7 @@ public:
|
|||
void ResetSignalHandler();
|
||||
static void MediaStopped(void *data, calldata_t *);
|
||||
static void MediaEnded(void *data, calldata_t *);
|
||||
static void MediaNext(void *data, calldata_t *);
|
||||
|
||||
MediaSourceType _sourceType = MediaSourceType::SOURCE;
|
||||
SceneSelection _scene;
|
||||
|
|
@ -70,17 +71,19 @@ public:
|
|||
bool _onlyMatchonChagne = false;
|
||||
|
||||
private:
|
||||
bool CheckTime();
|
||||
bool CheckState();
|
||||
bool CheckPlaylistEnd(const obs_media_state);
|
||||
bool CheckMediaMatch();
|
||||
|
||||
bool _stopped = false;
|
||||
bool _ended = false;
|
||||
bool _next = false;
|
||||
// TODO: Remove _alreadyMatched as it does not make much sense when
|
||||
// time restrictions for macro conditions are available.
|
||||
// Trigger scene change only once even if media state might trigger repeatedly
|
||||
bool _alreadyMatched = false;
|
||||
// Workaround to enable use of "ended" to specify end of VLC playlist
|
||||
bool _previousStateEnded = false;
|
||||
bool _playedToEnd = false;
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ static std::map<MediaState, std::string> mediaStates = {
|
|||
"AdvSceneSwitcher.mediaTab.states.ended"},
|
||||
{MediaState::OBS_MEDIA_STATE_ERROR,
|
||||
"AdvSceneSwitcher.mediaTab.states.error"},
|
||||
{MediaState::PLAYED_TO_END,
|
||||
"AdvSceneSwitcher.mediaTab.states.playedToEnd"},
|
||||
{MediaState::PLAYLIST_ENDED,
|
||||
"AdvSceneSwitcher.mediaTab.states.playlistEnd"},
|
||||
{MediaState::ANY, "AdvSceneSwitcher.mediaTab.states.any"},
|
||||
};
|
||||
|
||||
|
|
@ -51,111 +51,113 @@ MacroConditionMedia::~MacroConditionMedia()
|
|||
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
|
||||
signal_handler_disconnect(sh, "media_stopped", MediaStopped, this);
|
||||
signal_handler_disconnect(sh, "media_ended", MediaEnded, this);
|
||||
signal_handler_disconnect(sh, "media_next", MediaNext, this);
|
||||
obs_source_release(mediasource);
|
||||
}
|
||||
|
||||
bool matchTime(const int64_t currentTime, const int64_t duration,
|
||||
const MediaTimeRestriction restriction, const int64_t time)
|
||||
bool MacroConditionMedia::CheckTime()
|
||||
{
|
||||
bool matchedTimeNone =
|
||||
(restriction == MediaTimeRestriction::TIME_RESTRICTION_NONE);
|
||||
bool matchedTimeLonger =
|
||||
(restriction ==
|
||||
MediaTimeRestriction::TIME_RESTRICTION_LONGER) &&
|
||||
(currentTime > time);
|
||||
bool matchedTimeShorter =
|
||||
(restriction ==
|
||||
MediaTimeRestriction::TIME_RESTRICTION_SHORTER) &&
|
||||
(currentTime < time);
|
||||
bool matchedTimeRemainLonger =
|
||||
(restriction ==
|
||||
MediaTimeRestriction::TIME_RESTRICTION_REMAINING_LONGER) &&
|
||||
(duration > currentTime && duration - currentTime > time);
|
||||
bool matchedTimeRemainShorter =
|
||||
(restriction ==
|
||||
MediaTimeRestriction::TIME_RESTRICTION_REMAINING_SHORTER) &&
|
||||
(duration > currentTime && duration - currentTime < time);
|
||||
obs_source_t *s = obs_weak_source_get_source(_source);
|
||||
auto duration = obs_source_media_get_duration(s);
|
||||
auto currentTime = obs_source_media_get_time(s);
|
||||
obs_source_release(s);
|
||||
|
||||
return matchedTimeNone || matchedTimeLonger || matchedTimeShorter ||
|
||||
matchedTimeRemainLonger || matchedTimeRemainShorter;
|
||||
bool match = false;
|
||||
|
||||
switch (_restriction) {
|
||||
case MediaTimeRestriction::TIME_RESTRICTION_NONE:
|
||||
match = true;
|
||||
break;
|
||||
case MediaTimeRestriction::TIME_RESTRICTION_SHORTER:
|
||||
match = currentTime < _time.seconds * 1000;
|
||||
break;
|
||||
case MediaTimeRestriction::TIME_RESTRICTION_LONGER:
|
||||
match = currentTime > _time.seconds * 1000;
|
||||
break;
|
||||
case MediaTimeRestriction::TIME_RESTRICTION_REMAINING_SHORTER:
|
||||
match = duration > currentTime &&
|
||||
duration - currentTime < _time.seconds * 1000;
|
||||
break;
|
||||
case MediaTimeRestriction::TIME_RESTRICTION_REMAINING_LONGER:
|
||||
match = duration > currentTime &&
|
||||
duration - currentTime > _time.seconds * 1000;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
bool MacroConditionMedia::CheckState()
|
||||
{
|
||||
obs_source_t *s = obs_weak_source_get_source(_source);
|
||||
obs_media_state currentState = obs_source_media_get_state(s);
|
||||
obs_source_release(s);
|
||||
|
||||
bool match = false;
|
||||
// To be able to compare to obs_media_state more easily
|
||||
int expectedState = static_cast<int>(_state);
|
||||
|
||||
switch (_state) {
|
||||
case MediaState::OBS_MEDIA_STATE_STOPPED:
|
||||
match = _stopped || currentState == OBS_MEDIA_STATE_STOPPED;
|
||||
break;
|
||||
case MediaState::OBS_MEDIA_STATE_ENDED:
|
||||
match = _ended || currentState == OBS_MEDIA_STATE_ENDED;
|
||||
break;
|
||||
case MediaState::PLAYLIST_ENDED:
|
||||
match = CheckPlaylistEnd(currentState);
|
||||
break;
|
||||
case MediaState::ANY:
|
||||
match = true;
|
||||
break;
|
||||
case MediaState::OBS_MEDIA_STATE_NONE:
|
||||
case MediaState::OBS_MEDIA_STATE_PLAYING:
|
||||
case MediaState::OBS_MEDIA_STATE_OPENING:
|
||||
case MediaState::OBS_MEDIA_STATE_BUFFERING:
|
||||
case MediaState::OBS_MEDIA_STATE_PAUSED:
|
||||
case MediaState::OBS_MEDIA_STATE_ERROR:
|
||||
match = currentState == expectedState;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
bool MacroConditionMedia::CheckPlaylistEnd(const obs_media_state currentState)
|
||||
{
|
||||
bool consecutiveEndedStates = false;
|
||||
if (_next || currentState != OBS_MEDIA_STATE_ENDED) {
|
||||
_previousStateEnded = false;
|
||||
}
|
||||
if (currentState == OBS_MEDIA_STATE_ENDED && _previousStateEnded) {
|
||||
consecutiveEndedStates = true;
|
||||
}
|
||||
_previousStateEnded = _ended || currentState == OBS_MEDIA_STATE_ENDED;
|
||||
return consecutiveEndedStates;
|
||||
}
|
||||
|
||||
bool MacroConditionMedia::CheckMediaMatch()
|
||||
{
|
||||
|
||||
if (!_source) {
|
||||
return false;
|
||||
}
|
||||
bool match = false;
|
||||
|
||||
obs_source_t *s = obs_weak_source_get_source(_source);
|
||||
auto duration = obs_source_media_get_duration(s);
|
||||
auto time = obs_source_media_get_time(s);
|
||||
obs_media_state currentState = obs_source_media_get_state(s);
|
||||
obs_source_release(s);
|
||||
|
||||
// To be able to compare to obs_media_state more easily
|
||||
int expectedState = static_cast<int>(_state);
|
||||
|
||||
bool matchedStopped = expectedState == OBS_MEDIA_STATE_STOPPED &&
|
||||
_stopped;
|
||||
|
||||
// The signal for the state ended is intentionally not used here
|
||||
// so matchedEnded can be used to specify the end of a VLC playlist
|
||||
// by two consequtive matches of OBS_MEDIA_STATE_ENDED
|
||||
//
|
||||
// This was done to reduce the likelyhood of interpreting a single
|
||||
// OBS_MEDIA_STATE_ENDED caught by obs_source_media_get_state()
|
||||
// as the end of the playlist of the VLC source, while actually being
|
||||
// in the middle of switching to the next item of the playlist
|
||||
//
|
||||
// If there is a separate obs_media_sate in the future for the
|
||||
// "end of playlist reached" signal the line below can be used
|
||||
// and an additional check for this new singal can be introduced
|
||||
//
|
||||
// bool matchedEnded = _state == OBS_MEDIA_STATE_ENDED && _ended;
|
||||
|
||||
bool ended = false;
|
||||
|
||||
if (currentState == OBS_MEDIA_STATE_ENDED) {
|
||||
ended = _previousStateEnded;
|
||||
_previousStateEnded = true;
|
||||
} else {
|
||||
_previousStateEnded = false;
|
||||
}
|
||||
|
||||
bool matchedEnded = ended && (expectedState == OBS_MEDIA_STATE_ENDED);
|
||||
|
||||
// match if playedToEnd was true in last interval
|
||||
// and playback is currently ended
|
||||
bool matchedPlayedToEnd = _state == MediaState::PLAYED_TO_END &&
|
||||
_playedToEnd && ended;
|
||||
|
||||
// interval * 2 to make sure not to miss any state changes
|
||||
// which happened during check of the conditions
|
||||
_playedToEnd = _playedToEnd ||
|
||||
(duration - time <= switcher->interval * 2);
|
||||
|
||||
// reset for next check
|
||||
if (ended) {
|
||||
_playedToEnd = false;
|
||||
}
|
||||
_stopped = false;
|
||||
_ended = false;
|
||||
|
||||
bool matchedState =
|
||||
(currentState == expectedState || _state == MediaState::ANY) ||
|
||||
matchedStopped || matchedEnded || matchedPlayedToEnd;
|
||||
|
||||
bool matchedTime =
|
||||
matchTime(time, duration, _restriction, _time.seconds * 1000);
|
||||
bool matched = matchedState && matchedTime;
|
||||
bool matched = CheckState() && CheckTime();
|
||||
|
||||
if (matched && !(_onlyMatchonChagne && _alreadyMatched)) {
|
||||
match = true;
|
||||
}
|
||||
_alreadyMatched = matched;
|
||||
|
||||
// reset for next check
|
||||
_stopped = false;
|
||||
_ended = false;
|
||||
_next = false;
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
|
|
@ -195,6 +197,7 @@ bool MacroConditionMedia::Save(obs_data_t *obj)
|
|||
obs_data_set_int(obj, "restriction", static_cast<int>(_restriction));
|
||||
_time.Save(obj);
|
||||
obs_data_set_bool(obj, "matchOnChagne", _onlyMatchonChagne);
|
||||
obs_data_set_int(obj, "version", 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -250,11 +253,7 @@ bool MacroConditionMedia::Load(obs_data_t *obj)
|
|||
_restriction = static_cast<MediaTimeRestriction>(
|
||||
obs_data_get_int(obj, "restriction"));
|
||||
_time.Load(obj);
|
||||
if (!obs_data_has_user_value(obj, "matchOnChagne")) {
|
||||
_onlyMatchonChagne = true;
|
||||
} else {
|
||||
_onlyMatchonChagne = obs_data_get_bool(obj, "matchOnChagne");
|
||||
}
|
||||
_onlyMatchonChagne = obs_data_get_bool(obj, "matchOnChagne");
|
||||
|
||||
if (_sourceType == MediaSourceType::SOURCE) {
|
||||
obs_source_t *mediasource = obs_weak_source_get_source(_source);
|
||||
|
|
@ -262,11 +261,17 @@ bool MacroConditionMedia::Load(obs_data_t *obj)
|
|||
obs_source_get_signal_handler(mediasource);
|
||||
signal_handler_connect(sh, "media_stopped", MediaStopped, this);
|
||||
signal_handler_connect(sh, "media_ended", MediaEnded, this);
|
||||
signal_handler_connect(sh, "media_next", MediaNext, this);
|
||||
obs_source_release(mediasource);
|
||||
}
|
||||
|
||||
forMediaSourceOnSceneAddMediaCondition(_scene.GetScene(), this,
|
||||
_sources);
|
||||
if (!obs_data_has_user_value(obj, "version")) {
|
||||
if (_state == MediaState::OBS_MEDIA_STATE_ENDED) {
|
||||
_state = MediaState::PLAYLIST_ENDED;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -304,6 +309,7 @@ void MacroConditionMedia::ClearSignalHandler()
|
|||
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
|
||||
signal_handler_disconnect(sh, "media_stopped", MediaStopped, this);
|
||||
signal_handler_disconnect(sh, "media_ended", MediaEnded, this);
|
||||
signal_handler_disconnect(sh, "media_next", MediaNext, this);
|
||||
obs_source_release(mediasource);
|
||||
}
|
||||
|
||||
|
|
@ -313,8 +319,10 @@ void MacroConditionMedia::ResetSignalHandler()
|
|||
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
|
||||
signal_handler_disconnect(sh, "media_stopped", MediaStopped, this);
|
||||
signal_handler_disconnect(sh, "media_ended", MediaEnded, this);
|
||||
signal_handler_disconnect(sh, "media_next", MediaNext, this);
|
||||
signal_handler_connect(sh, "media_stopped", MediaStopped, this);
|
||||
signal_handler_connect(sh, "media_ended", MediaEnded, this);
|
||||
signal_handler_connect(sh, "media_next", MediaNext, this);
|
||||
obs_source_release(mediasource);
|
||||
}
|
||||
|
||||
|
|
@ -330,6 +338,12 @@ void MacroConditionMedia::MediaEnded(void *data, calldata_t *)
|
|||
media->_ended = true;
|
||||
}
|
||||
|
||||
void MacroConditionMedia::MediaNext(void *data, calldata_t *)
|
||||
{
|
||||
MacroConditionMedia *media = static_cast<MacroConditionMedia *>(data);
|
||||
media->_next = true;
|
||||
}
|
||||
|
||||
static void populateMediaTimeRestrictions(QComboBox *list)
|
||||
{
|
||||
for (auto entry : mediaTimeRestrictions) {
|
||||
|
|
@ -356,15 +370,18 @@ static void addAnyAndAllStates(QComboBox *list)
|
|||
|
||||
MacroConditionMediaEdit::MacroConditionMediaEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionMedia> entryData)
|
||||
: QWidget(parent)
|
||||
: QWidget(parent),
|
||||
_mediaSources(new QComboBox()),
|
||||
_scenes(new SceneSelectionWidget(window())),
|
||||
_states(new QComboBox()),
|
||||
_timeRestrictions(new QComboBox()),
|
||||
_time(new DurationSelection()),
|
||||
_onChange(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.media.matchOnChange")))
|
||||
|
||||
{
|
||||
_mediaSources = new QComboBox();
|
||||
_scenes = new SceneSelectionWidget(window());
|
||||
_states = new QComboBox();
|
||||
_timeRestrictions = new QComboBox();
|
||||
_time = new DurationSelection();
|
||||
_onChange = new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.media.matchOnChange"));
|
||||
_states->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.media.inconsistencyInfo"));
|
||||
|
||||
QWidget::connect(_mediaSources,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
|
|
@ -548,6 +565,9 @@ void MacroConditionMediaEdit::OnChangeChanged(int value)
|
|||
void MacroConditionMediaEdit::SetWidgetVisibility()
|
||||
{
|
||||
_scenes->setVisible(_entryData->_sourceType != MediaSourceType::SOURCE);
|
||||
if (!_onChange->isChecked()) {
|
||||
_onChange->hide();
|
||||
}
|
||||
}
|
||||
|
||||
int getIdxFromMediaState(MediaState state)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user