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);