From 1057d765f77cfbe7656c3350f37ae30a41ef0a09 Mon Sep 17 00:00:00 2001 From: Alex Van Camp Date: Thu, 6 Feb 2020 18:44:28 -0600 Subject: [PATCH 1/2] feat(events): add "TransitionEnd" and "TransitionVideoEnd" events Adds events for `TransitionEnd` and `TransitionVideoEnd`. `TransitionVideoEnd` is specific to Stinger transitions. It is fired when their video has actually completed playback. This is important, because `TransitionEnd` is fired at the cut point of the Stinger, which is nice to have but isn't always what the user might want. This builds on #409, and should be merged after it. --- src/Utils.cpp | 24 +++++++++++++ src/Utils.h | 1 + src/WSEvents.cpp | 93 ++++++++++++++++++++++++++++++++++-------------- src/WSEvents.h | 6 ++-- 4 files changed, 96 insertions(+), 28 deletions(-) diff --git a/src/Utils.cpp b/src/Utils.cpp index cb381d0f..6e5f98db 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -413,6 +413,30 @@ bool Utils::SetTransitionByName(QString transitionName) { } } +obs_data_t* Utils::GetTransitionData(obs_source_t* transition) { + int duration = Utils::GetTransitionDuration(transition); + if (duration < 0) { + blog(LOG_WARNING, "GetTransitionData: duration is negative !"); + } + + obs_data_t* transitionData = obs_data_create(); + obs_data_set_string(transitionData, "name", obs_source_get_name(transition)); + obs_data_set_string(transitionData, "type", obs_source_get_id(transition)); + obs_data_set_int(transitionData, "duration", duration); + + OBSSourceAutoRelease sourceScene = obs_transition_get_source(transition, OBS_TRANSITION_SOURCE_A); + if (sourceScene) { + obs_data_set_string(transitionData, "from-scene", obs_source_get_name(sourceScene)); + } + + OBSSourceAutoRelease destinationScene = obs_transition_get_active_source(transition); + if (destinationScene) { + obs_data_set_string(transitionData, "to-scene", obs_source_get_name(destinationScene)); + } + + return transitionData; +} + QString Utils::OBSVersionString() { uint32_t version = obs_get_version(); diff --git a/src/Utils.h b/src/Utils.h index 4259c2e6..4f8a8e7e 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -60,6 +60,7 @@ namespace Utils { int GetTransitionDuration(obs_source_t* transition); obs_source_t* GetTransitionFromName(QString transitionName); bool SetTransitionByName(QString transitionName); + obs_data_t* GetTransitionData(obs_source_t* transition); QString OBSVersionString(); diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index 619aea7a..d964efdc 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -122,7 +122,7 @@ void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void* private switch (event) { case OBS_FRONTEND_EVENT_FINISHED_LOADING: - owner->hookTransitionBeginEvent(); + owner->hookTransitionPlaybackEvents(); break; case OBS_FRONTEND_EVENT_SCENE_CHANGED: @@ -134,7 +134,7 @@ void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void* private break; case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED: - owner->hookTransitionBeginEvent(); + owner->hookTransitionPlaybackEvents(); owner->OnSceneCollectionChange(); break; @@ -147,7 +147,7 @@ void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void* private break; case OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED: - owner->hookTransitionBeginEvent(); + owner->hookTransitionPlaybackEvents(); owner->OnTransitionListChange(); break; @@ -231,7 +231,7 @@ void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void* private break; case OBS_FRONTEND_EVENT_EXIT: - owner->unhookTransitionBeginEvent(); + owner->unhookTransitionPlaybackEvents(); owner->OnExit(); break; } @@ -309,6 +309,8 @@ void WSEvents::disconnectSourceSignals(obs_source_t* source) { signal_handler_disconnect(sh, "item_deselect", OnSceneItemDeselected, this); signal_handler_disconnect(sh, "transition_start", OnTransitionBegin, this); + signal_handler_disconnect(sh, "transition_stop", OnTransitionEnd, this); + signal_handler_disconnect(sh, "transition_video_stop", OnTransitionVideoEnd, this); } void WSEvents::connectFilterSignals(obs_source_t* filter) { @@ -331,7 +333,7 @@ void WSEvents::disconnectFilterSignals(obs_source_t* filter) { signal_handler_disconnect(sh, "enable", OnSourceFilterVisibilityChanged, this); } -void WSEvents::hookTransitionBeginEvent() { +void WSEvents::hookTransitionPlaybackEvents() { obs_frontend_source_list transitions = {}; obs_frontend_get_transitions(&transitions); @@ -340,12 +342,16 @@ void WSEvents::hookTransitionBeginEvent() { signal_handler_t* sh = obs_source_get_signal_handler(transition); signal_handler_disconnect(sh, "transition_start", OnTransitionBegin, this); signal_handler_connect(sh, "transition_start", OnTransitionBegin, this); + signal_handler_disconnect(sh, "transition_stop", OnTransitionEnd, this); + signal_handler_connect(sh, "transition_stop", OnTransitionEnd, this); + signal_handler_disconnect(sh, "transition_video_stop", OnTransitionVideoEnd, this); + signal_handler_connect(sh, "transition_video_stop", OnTransitionVideoEnd, this); } obs_frontend_source_list_free(&transitions); } -void WSEvents::unhookTransitionBeginEvent() { +void WSEvents::unhookTransitionPlaybackEvents() { obs_frontend_source_list transitions = {}; obs_frontend_get_transitions(&transitions); @@ -353,6 +359,8 @@ void WSEvents::unhookTransitionBeginEvent() { obs_source_t* transition = transitions.sources.array[i]; signal_handler_t* sh = obs_source_get_signal_handler(transition); signal_handler_disconnect(sh, "transition_start", OnTransitionBegin, this); + signal_handler_disconnect(sh, "transition_stop", OnTransitionEnd, this); + signal_handler_disconnect(sh, "transition_video_stop", OnTransitionVideoEnd, this); } obs_frontend_source_list_free(&transitions); @@ -895,29 +903,62 @@ void WSEvents::OnTransitionBegin(void* param, calldata_t* data) { return; } - int duration = Utils::GetTransitionDuration(transition); - if (duration < 0) { - blog(LOG_WARNING, "OnTransitionBegin: duration is negative !"); - } - - OBSDataAutoRelease fields = obs_data_create(); - obs_data_set_string(fields, "name", obs_source_get_name(transition)); - obs_data_set_string(fields, "type", obs_source_get_id(transition)); - obs_data_set_int(fields, "duration", duration); - - OBSSourceAutoRelease sourceScene = obs_transition_get_source(transition, OBS_TRANSITION_SOURCE_A); - if (sourceScene) { - obs_data_set_string(fields, "from-scene", obs_source_get_name(sourceScene)); - } - - OBSSourceAutoRelease destinationScene = obs_transition_get_active_source(transition); - if (destinationScene) { - obs_data_set_string(fields, "to-scene", obs_source_get_name(destinationScene)); - } - + OBSDataAutoRelease fields = Utils::GetTransitionData(transition); instance->broadcastUpdate("TransitionBegin", fields); } +/** +* A transition (other than "cut") has ended. +* +* @return {String} `name` Transition name. +* @return {String} `type` Transition type. +* @return {int} `duration` Transition duration (in milliseconds). +* @return {String} `from-scene` Source scene of the transition +* @return {String} `to-scene` Destination scene of the transition +* +* @api events +* @name TransitionEnd +* @category transitions +* @since 4.8.0 +*/ +void WSEvents::OnTransitionEnd(void* param, calldata_t* data) { + auto instance = reinterpret_cast(param); + + OBSSource transition = calldata_get_pointer(data, "source"); + if (!transition) { + return; + } + + OBSDataAutoRelease fields = Utils::GetTransitionData(transition); + instance->broadcastUpdate("TransitionEnd", fields); +} + +/** +* A stinger transition has finished playing its video. +* +* @return {String} `name` Transition name. +* @return {String} `type` Transition type. +* @return {int} `duration` Transition duration (in milliseconds). +* @return {String} `from-scene` Source scene of the transition +* @return {String} `to-scene` Destination scene of the transition +* +* @api events +* @name TransitionVideoEnd +* @category transitions +* @since 4.8.0 +*/ +void WSEvents::OnTransitionVideoEnd(void* param, calldata_t* data) { + auto instance = reinterpret_cast(param); + + OBSSource transition = calldata_get_pointer(data, "source"); + if (!transition) { + return; + } + + OBSDataAutoRelease fields = Utils::GetTransitionData(transition); + instance->broadcastUpdate("TransitionVideoEnd", fields); +} + /** * A source has been created. A source can be an input, a scene or a transition. * diff --git a/src/WSEvents.h b/src/WSEvents.h index 684fe654..05972fd0 100644 --- a/src/WSEvents.h +++ b/src/WSEvents.h @@ -43,8 +43,8 @@ public: void connectFilterSignals(obs_source_t* filter); void disconnectFilterSignals(obs_source_t* filter); - void hookTransitionBeginEvent(); - void unhookTransitionBeginEvent(); + void hookTransitionPlaybackEvents(); + void unhookTransitionPlaybackEvents(); uint64_t getStreamingTime(); uint64_t getRecordingTime(); @@ -116,6 +116,8 @@ private: enum obs_frontend_event event, void* privateData); static void OnTransitionBegin(void* param, calldata_t* data); + static void OnTransitionEnd(void* param, calldata_t* data); + static void OnTransitionVideoEnd(void* param, calldata_t* data); static void OnSourceCreate(void* param, calldata_t* data); static void OnSourceDestroy(void* param, calldata_t* data); From 2a7aa432a9b4ab2fa2900ba91a69c39bd33d8491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 15 Mar 2020 23:20:56 +0100 Subject: [PATCH 2/2] events(TransitionEnd): remove `from-scene` because it's not available in transition_stop --- src/Utils.cpp | 21 ++++++++++++++------- src/WSEvents.cpp | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/Utils.cpp b/src/Utils.cpp index 6e5f98db..cf0bcfb5 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -419,20 +419,27 @@ obs_data_t* Utils::GetTransitionData(obs_source_t* transition) { blog(LOG_WARNING, "GetTransitionData: duration is negative !"); } + OBSSourceAutoRelease sourceScene = obs_transition_get_source(transition, OBS_TRANSITION_SOURCE_A); + OBSSourceAutoRelease destinationScene = obs_transition_get_active_source(transition); + obs_data_t* transitionData = obs_data_create(); obs_data_set_string(transitionData, "name", obs_source_get_name(transition)); obs_data_set_string(transitionData, "type", obs_source_get_id(transition)); obs_data_set_int(transitionData, "duration", duration); - OBSSourceAutoRelease sourceScene = obs_transition_get_source(transition, OBS_TRANSITION_SOURCE_A); - if (sourceScene) { + // When a transition starts and while it is running, SOURCE_A is the source scene + // and SOURCE_B is the destination scene. + // Before the transition_end event is triggered on a transition, the destination scene + // goes into SOURCE_A and SOURCE_B becomes null. This means that, in transition_stop + // we don't know what was the source scene + // TODO fix this in libobs + + bool isTransitionEndEvent = (sourceScene == destinationScene); + if (!isTransitionEndEvent) { obs_data_set_string(transitionData, "from-scene", obs_source_get_name(sourceScene)); } - - OBSSourceAutoRelease destinationScene = obs_transition_get_active_source(transition); - if (destinationScene) { - obs_data_set_string(transitionData, "to-scene", obs_source_get_name(destinationScene)); - } + + obs_data_set_string(transitionData, "to-scene", obs_source_get_name(destinationScene)); return transitionData; } diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index d964efdc..1bee974c 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -909,11 +909,11 @@ void WSEvents::OnTransitionBegin(void* param, calldata_t* data) { /** * A transition (other than "cut") has ended. +* Please note that the `from-scene` field is not available in TransitionEnd. * * @return {String} `name` Transition name. * @return {String} `type` Transition type. * @return {int} `duration` Transition duration (in milliseconds). -* @return {String} `from-scene` Source scene of the transition * @return {String} `to-scene` Destination scene of the transition * * @api events