UI: Add visibility transitions
This also modifies libobs. This adds the ability for scene items to have transitions when their visibility is toggled.master
parent
011adc4e29
commit
32e7ba1abe
|
@ -80,6 +80,8 @@ Back="Back"
|
|||
Defaults="Defaults"
|
||||
HideMixer="Hide in Mixer"
|
||||
TransitionOverride="Transition Override"
|
||||
ShowTransition="Show Transition"
|
||||
HideTransition="Hide Transition"
|
||||
None="None"
|
||||
StudioMode.Preview="Preview"
|
||||
StudioMode.Program="Program"
|
||||
|
|
|
@ -1102,6 +1102,168 @@ QMenu *OBSBasic::CreatePerSceneTransitionMenu()
|
|||
return menu;
|
||||
}
|
||||
|
||||
void OBSBasic::on_actionShowTransitionProperties_triggered()
|
||||
{
|
||||
OBSSceneItem item = GetCurrentSceneItem();
|
||||
OBSSource source = obs_sceneitem_get_show_transition(item);
|
||||
|
||||
if (source)
|
||||
CreatePropertiesWindow(source);
|
||||
}
|
||||
|
||||
void OBSBasic::on_actionHideTransitionProperties_triggered()
|
||||
{
|
||||
OBSSceneItem item = GetCurrentSceneItem();
|
||||
OBSSource source = obs_sceneitem_get_hide_transition(item);
|
||||
|
||||
if (source)
|
||||
CreatePropertiesWindow(source);
|
||||
}
|
||||
|
||||
QMenu *OBSBasic::CreateVisibilityTransitionMenu(bool visible)
|
||||
{
|
||||
OBSSceneItem si = GetCurrentSceneItem();
|
||||
|
||||
QMenu *menu =
|
||||
new QMenu(QTStr(visible ? "ShowTransition" : "HideTransition"));
|
||||
QAction *action;
|
||||
|
||||
const char *curId = obs_source_get_id(
|
||||
visible ? obs_sceneitem_get_show_transition(si)
|
||||
: obs_sceneitem_get_hide_transition(si));
|
||||
int curDuration =
|
||||
(int)(visible ? obs_sceneitem_get_show_transition_duration(si)
|
||||
: obs_sceneitem_get_hide_transition_duration(si));
|
||||
|
||||
if (curDuration <= 0)
|
||||
curDuration = obs_frontend_get_transition_duration();
|
||||
|
||||
QSpinBox *duration = new QSpinBox(menu);
|
||||
duration->setMinimum(50);
|
||||
duration->setSuffix("ms");
|
||||
duration->setMaximum(20000);
|
||||
duration->setSingleStep(50);
|
||||
duration->setValue(curDuration);
|
||||
|
||||
auto setTransition = [this](QAction *action, bool visible) {
|
||||
OBSBasic *main =
|
||||
reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
|
||||
|
||||
QString id = action->property("transition_id").toString();
|
||||
OBSSceneItem sceneItem = main->GetCurrentSceneItem();
|
||||
|
||||
if (id.isNull() || id.isEmpty()) {
|
||||
if (visible)
|
||||
obs_sceneitem_set_show_transition(sceneItem,
|
||||
nullptr);
|
||||
else
|
||||
obs_sceneitem_set_hide_transition(sceneItem,
|
||||
nullptr);
|
||||
} else {
|
||||
OBSSource tr =
|
||||
visible ? obs_sceneitem_get_show_transition(
|
||||
sceneItem)
|
||||
: obs_sceneitem_get_hide_transition(
|
||||
sceneItem);
|
||||
|
||||
if (!tr || strcmp(QT_TO_UTF8(id),
|
||||
obs_source_get_id(tr)) != 0) {
|
||||
QString name = QT_UTF8(obs_source_get_name(
|
||||
obs_sceneitem_get_source(sceneItem)));
|
||||
name += " ";
|
||||
name += QTStr(visible ? "ShowTransition"
|
||||
: "HideTransition");
|
||||
tr = obs_source_create_private(QT_TO_UTF8(id),
|
||||
QT_TO_UTF8(name),
|
||||
nullptr);
|
||||
if (visible)
|
||||
obs_sceneitem_set_show_transition(
|
||||
sceneItem, tr);
|
||||
else
|
||||
obs_sceneitem_set_hide_transition(
|
||||
sceneItem, tr);
|
||||
obs_source_release(tr);
|
||||
|
||||
int duration =
|
||||
(int)(visible ? obs_sceneitem_get_show_transition_duration(
|
||||
sceneItem)
|
||||
: obs_sceneitem_get_hide_transition_duration(
|
||||
sceneItem));
|
||||
if (duration <= 0) {
|
||||
duration =
|
||||
obs_frontend_get_transition_duration();
|
||||
if (visible)
|
||||
obs_sceneitem_set_show_transition_duration(
|
||||
sceneItem, duration);
|
||||
else
|
||||
obs_sceneitem_set_hide_transition_duration(
|
||||
sceneItem, duration);
|
||||
}
|
||||
}
|
||||
if (obs_source_configurable(tr))
|
||||
CreatePropertiesWindow(tr);
|
||||
}
|
||||
};
|
||||
if (visible) {
|
||||
auto setDuration = [this](int duration) {
|
||||
OBSBasic *main = reinterpret_cast<OBSBasic *>(
|
||||
App()->GetMainWindow());
|
||||
|
||||
OBSSceneItem item = main->GetCurrentSceneItem();
|
||||
obs_sceneitem_set_show_transition_duration(item,
|
||||
duration);
|
||||
};
|
||||
connect(duration,
|
||||
(void (QSpinBox::*)(int)) & QSpinBox::valueChanged,
|
||||
setDuration);
|
||||
} else {
|
||||
auto setDuration = [this](int duration) {
|
||||
OBSBasic *main = reinterpret_cast<OBSBasic *>(
|
||||
App()->GetMainWindow());
|
||||
|
||||
OBSSceneItem item = main->GetCurrentSceneItem();
|
||||
obs_sceneitem_set_hide_transition_duration(item,
|
||||
duration);
|
||||
};
|
||||
connect(duration,
|
||||
(void (QSpinBox::*)(int)) & QSpinBox::valueChanged,
|
||||
setDuration);
|
||||
}
|
||||
|
||||
action = menu->addAction(QT_UTF8(Str("None")));
|
||||
action->setProperty("transition_id", QT_UTF8(""));
|
||||
action->setCheckable(true);
|
||||
action->setChecked(!curId);
|
||||
connect(action, &QAction::triggered,
|
||||
std::bind(setTransition, action, visible));
|
||||
size_t idx = 0;
|
||||
const char *id;
|
||||
while (obs_enum_transition_types(idx++, &id)) {
|
||||
const char *name = obs_source_get_display_name(id);
|
||||
const bool match = id && curId && strcmp(id, curId) == 0;
|
||||
action = menu->addAction(QT_UTF8(name));
|
||||
action->setProperty("transition_id", QT_UTF8(id));
|
||||
action->setCheckable(true);
|
||||
action->setChecked(match);
|
||||
connect(action, &QAction::triggered,
|
||||
std::bind(setTransition, action, visible));
|
||||
}
|
||||
|
||||
QWidgetAction *durationAction = new QWidgetAction(menu);
|
||||
durationAction->setDefaultWidget(duration);
|
||||
|
||||
menu->addSeparator();
|
||||
menu->addAction(durationAction);
|
||||
if (curId && obs_is_source_configurable(curId)) {
|
||||
menu->addSeparator();
|
||||
menu->addAction(
|
||||
QTStr("Properties"), this,
|
||||
visible ? SLOT(on_actionShowTransitionProperties_triggered())
|
||||
: SLOT(on_actionHideTransitionProperties_triggered()));
|
||||
}
|
||||
return menu;
|
||||
}
|
||||
|
||||
QMenu *OBSBasic::CreateTransitionMenu(QWidget *parent, QuickTransition *qt)
|
||||
{
|
||||
QMenu *menu = new QMenu(parent);
|
||||
|
|
|
@ -5065,6 +5065,10 @@ void OBSBasic::CreateSourcePopupMenu(int idx, bool preview)
|
|||
SLOT(ScreenshotSelectedSource()));
|
||||
popup.addSeparator();
|
||||
|
||||
popup.addMenu(CreateVisibilityTransitionMenu(true));
|
||||
popup.addMenu(CreateVisibilityTransitionMenu(false));
|
||||
popup.addSeparator();
|
||||
|
||||
action = popup.addAction(QTStr("Interact"), this,
|
||||
SLOT(on_actionInteract_triggered()));
|
||||
|
||||
|
|
|
@ -417,6 +417,7 @@ private:
|
|||
void CreateDefaultQuickTransitions();
|
||||
|
||||
QMenu *CreatePerSceneTransitionMenu();
|
||||
QMenu *CreateVisibilityTransitionMenu(bool visible);
|
||||
|
||||
QuickTransition *GetQuickTransition(int id);
|
||||
int GetQuickTransitionIdx(int id);
|
||||
|
@ -958,6 +959,9 @@ private slots:
|
|||
void on_transitionDuration_valueChanged(int value);
|
||||
void on_tbar_position_valueChanged(int value);
|
||||
|
||||
void on_actionShowTransitionProperties_triggered();
|
||||
void on_actionHideTransitionProperties_triggered();
|
||||
|
||||
void on_modeSwitch_clicked();
|
||||
|
||||
// Source Context Buttons
|
||||
|
|
|
@ -408,11 +408,11 @@ void OBSBasicProperties::DrawTransitionPreview(void *data, uint32_t cx,
|
|||
{
|
||||
OBSBasicProperties *window = static_cast<OBSBasicProperties *>(data);
|
||||
|
||||
if (!window->source)
|
||||
if (!window->sourceClone)
|
||||
return;
|
||||
|
||||
uint32_t sourceCX = max(obs_source_get_width(window->source), 1u);
|
||||
uint32_t sourceCY = max(obs_source_get_height(window->source), 1u);
|
||||
uint32_t sourceCX = max(obs_source_get_width(window->sourceClone), 1u);
|
||||
uint32_t sourceCY = max(obs_source_get_height(window->sourceClone), 1u);
|
||||
|
||||
int x, y;
|
||||
int newCX, newCY;
|
||||
|
|
|
@ -470,6 +470,40 @@ Scene Item Functions
|
|||
|
||||
---------------------
|
||||
|
||||
.. function:: void obs_sceneitem_set_show_transition(obs_sceneitem_t *item, obs_source_t *transition)
|
||||
void obs_sceneitem_set_hide_transition(obs_sceneitem_t *item, obs_source_t *transition)
|
||||
|
||||
Set a transition for showing or hiding a scene item. Set *NULL* to remove the transition.
|
||||
|
||||
---------------------
|
||||
|
||||
.. function:: obs_source_t *obs_sceneitem_get_show_transition(obs_sceneitem_t *item)
|
||||
obs_source_t *obs_sceneitem_get_hide_transition(obs_sceneitem_t *item)
|
||||
|
||||
:return: The transition for showing or hiding a scene item. *NULL* if no transition is set.
|
||||
|
||||
---------------------
|
||||
|
||||
.. function:: void obs_sceneitem_set_show_transition_duration(obs_sceneitem_t *item, uint32_t duration_ms)
|
||||
void obs_sceneitem_set_hide_transition_duration(obs_sceneitem_t *item, uint32_t duration_ms)
|
||||
|
||||
Set transition duration for showing or hiding a scene item.
|
||||
|
||||
---------------------
|
||||
|
||||
.. function:: uint32_t obs_sceneitem_get_show_transition_duration(obs_sceneitem_t *item)
|
||||
uint32_t obs_sceneitem_get_hide_transition_duration(obs_sceneitem_t *item)
|
||||
|
||||
:return: The transition duration in ms for showing or hiding a scene item.
|
||||
|
||||
---------------------
|
||||
|
||||
.. function:: void obs_sceneitem_do_transition(obs_sceneitem_t *item, bool visible)
|
||||
|
||||
Start the transition for showing or hiding a scene item.
|
||||
|
||||
---------------------
|
||||
|
||||
|
||||
.. _scene_item_group_reference:
|
||||
|
||||
|
|
|
@ -187,6 +187,12 @@ static void scene_destroy(void *data)
|
|||
bfree(scene);
|
||||
}
|
||||
|
||||
static inline bool transition_active(obs_source_t *transition)
|
||||
{
|
||||
return transition && (transition->transitioning_audio ||
|
||||
transition->transitioning_video);
|
||||
}
|
||||
|
||||
static void scene_enum_sources(void *data, obs_source_enum_proc_t enum_callback,
|
||||
void *param, bool active)
|
||||
{
|
||||
|
@ -201,8 +207,28 @@ static void scene_enum_sources(void *data, obs_source_enum_proc_t enum_callback,
|
|||
next = item->next;
|
||||
|
||||
obs_sceneitem_addref(item);
|
||||
if (!active || os_atomic_load_long(&item->active_refs) > 0)
|
||||
if (active) {
|
||||
if (item->visible &&
|
||||
transition_active(item->show_transition))
|
||||
enum_callback(scene->source,
|
||||
item->show_transition, param);
|
||||
else if (!item->visible &&
|
||||
transition_active(item->hide_transition))
|
||||
enum_callback(scene->source,
|
||||
item->hide_transition, param);
|
||||
else if (os_atomic_load_long(&item->active_refs) > 0)
|
||||
enum_callback(scene->source, item->source,
|
||||
param);
|
||||
} else {
|
||||
if (item->show_transition)
|
||||
enum_callback(scene->source,
|
||||
item->show_transition, param);
|
||||
if (item->hide_transition)
|
||||
enum_callback(scene->source,
|
||||
item->hide_transition, param);
|
||||
enum_callback(scene->source, item->source, param);
|
||||
}
|
||||
|
||||
obs_sceneitem_release(item);
|
||||
|
||||
item = next;
|
||||
|
@ -565,7 +591,27 @@ static inline void render_item(struct obs_scene_item *item)
|
|||
gs_matrix_translate3f(-(float)item->crop.left,
|
||||
-(float)item->crop.top, 0.0f);
|
||||
|
||||
obs_source_video_render(item->source);
|
||||
if (item->user_visible &&
|
||||
transition_active(item->show_transition)) {
|
||||
const int cx =
|
||||
obs_source_get_width(item->source);
|
||||
const int cy =
|
||||
obs_source_get_height(item->source);
|
||||
obs_transition_set_size(item->show_transition,
|
||||
cx, cy);
|
||||
obs_source_video_render(item->show_transition);
|
||||
} else if (!item->user_visible &&
|
||||
transition_active(item->hide_transition)) {
|
||||
const int cx =
|
||||
obs_source_get_width(item->source);
|
||||
const int cy =
|
||||
obs_source_get_height(item->source);
|
||||
obs_transition_set_size(item->hide_transition,
|
||||
cx, cy);
|
||||
obs_source_video_render(item->hide_transition);
|
||||
} else {
|
||||
obs_source_video_render(item->source);
|
||||
}
|
||||
|
||||
gs_texrender_end(item->item_render);
|
||||
}
|
||||
|
@ -576,6 +622,18 @@ static inline void render_item(struct obs_scene_item *item)
|
|||
gs_matrix_mul(&item->draw_transform);
|
||||
if (item->item_render) {
|
||||
render_item_texture(item);
|
||||
} else if (item->user_visible &&
|
||||
transition_active(item->show_transition)) {
|
||||
const int cx = obs_source_get_width(item->source);
|
||||
const int cy = obs_source_get_height(item->source);
|
||||
obs_transition_set_size(item->show_transition, cx, cy);
|
||||
obs_source_video_render(item->show_transition);
|
||||
} else if (!item->user_visible &&
|
||||
transition_active(item->hide_transition)) {
|
||||
const int cx = obs_source_get_width(item->source);
|
||||
const int cy = obs_source_get_height(item->source);
|
||||
obs_transition_set_size(item->hide_transition, cx, cy);
|
||||
obs_source_video_render(item->hide_transition);
|
||||
} else {
|
||||
obs_source_video_render(item->source);
|
||||
}
|
||||
|
@ -669,7 +727,8 @@ static void scene_video_render(void *data, gs_effect_t *effect)
|
|||
|
||||
item = scene->first_item;
|
||||
while (item) {
|
||||
if (item->user_visible)
|
||||
if (item->user_visible ||
|
||||
transition_active(item->hide_transition))
|
||||
render_item(item);
|
||||
|
||||
item = item->next;
|
||||
|
@ -793,6 +852,40 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
|
|||
item->scale_filter = OBS_SCALE_AREA;
|
||||
}
|
||||
|
||||
obs_data_t *show_data = obs_data_get_obj(item_data, "show_transition");
|
||||
if (show_data) {
|
||||
const char *id = obs_data_get_string(show_data, "id");
|
||||
if (id && strlen(id)) {
|
||||
const char *tn = obs_data_get_string(show_data, "name");
|
||||
obs_data_t *s =
|
||||
obs_data_get_obj(show_data, "transition");
|
||||
obs_source_t *t = obs_source_create_private(id, tn, s);
|
||||
obs_sceneitem_set_show_transition(item, t);
|
||||
obs_source_release(t);
|
||||
obs_data_release(s);
|
||||
}
|
||||
item->show_transition_duration =
|
||||
obs_data_get_int(show_data, "duration");
|
||||
obs_data_release(show_data);
|
||||
}
|
||||
|
||||
obs_data_t *hide_data = obs_data_get_obj(item_data, "hide_transition");
|
||||
if (hide_data) {
|
||||
const char *id = obs_data_get_string(hide_data, "id");
|
||||
if (id && strlen(id)) {
|
||||
const char *tn = obs_data_get_string(hide_data, "name");
|
||||
obs_data_t *s =
|
||||
obs_data_get_obj(hide_data, "transition");
|
||||
obs_source_t *t = obs_source_create_private(id, tn, s);
|
||||
obs_sceneitem_set_hide_transition(item, t);
|
||||
obs_source_release(t);
|
||||
obs_data_release(s);
|
||||
}
|
||||
item->hide_transition_duration =
|
||||
obs_data_get_int(hide_data, "duration");
|
||||
obs_data_release(hide_data);
|
||||
}
|
||||
|
||||
if (item->item_render && !item_texture_enabled(item)) {
|
||||
obs_enter_graphics();
|
||||
gs_texrender_destroy(item->item_render);
|
||||
|
@ -908,6 +1001,40 @@ static void scene_save_item(obs_data_array_t *array,
|
|||
|
||||
obs_data_set_string(item_data, "scale_filter", scale_filter);
|
||||
|
||||
obs_data_t *show_data = obs_data_create();
|
||||
if (item->show_transition) {
|
||||
obs_data_set_string(
|
||||
show_data, "id",
|
||||
obs_source_get_unversioned_id(item->show_transition));
|
||||
obs_data_set_string(show_data, "versioned_id",
|
||||
obs_source_get_id(item->show_transition));
|
||||
obs_data_set_string(show_data, "name",
|
||||
obs_source_get_name(item->show_transition));
|
||||
obs_data_t *s = obs_source_get_settings(item->show_transition);
|
||||
obs_data_set_obj(show_data, "transition", s);
|
||||
obs_data_release(s);
|
||||
}
|
||||
obs_data_set_int(show_data, "duration", item->show_transition_duration);
|
||||
obs_data_set_obj(item_data, "show_transition", show_data);
|
||||
obs_data_release(show_data);
|
||||
|
||||
obs_data_t *hide_data = obs_data_create();
|
||||
if (item->hide_transition) {
|
||||
obs_data_set_string(
|
||||
hide_data, "id",
|
||||
obs_source_get_unversioned_id(item->hide_transition));
|
||||
obs_data_set_string(hide_data, "versioned_id",
|
||||
obs_source_get_id(item->hide_transition));
|
||||
obs_data_set_string(hide_data, "name",
|
||||
obs_source_get_name(item->hide_transition));
|
||||
obs_data_t *s = obs_source_get_settings(item->hide_transition);
|
||||
obs_data_set_obj(hide_data, "transition", s);
|
||||
obs_data_release(s);
|
||||
}
|
||||
obs_data_set_int(hide_data, "duration", item->hide_transition_duration);
|
||||
obs_data_set_obj(item_data, "hide_transition", hide_data);
|
||||
obs_data_release(hide_data);
|
||||
|
||||
obs_data_set_obj(item_data, "private_settings", item->private_settings);
|
||||
|
||||
obs_data_array_push_back(array, item_data);
|
||||
|
@ -1079,9 +1206,19 @@ static bool scene_audio_render(void *data, uint64_t *ts_out,
|
|||
|
||||
item = scene->first_item;
|
||||
while (item) {
|
||||
if (!obs_source_audio_pending(item->source) && item->visible) {
|
||||
struct obs_source *source;
|
||||
if (item->visible && transition_active(item->show_transition))
|
||||
source = item->show_transition;
|
||||
else if (!item->visible &&
|
||||
transition_active(item->hide_transition))
|
||||
source = item->hide_transition;
|
||||
else
|
||||
source = item->source;
|
||||
if (!obs_source_audio_pending(source) &&
|
||||
(item->visible ||
|
||||
transition_active(item->hide_transition))) {
|
||||
uint64_t source_ts =
|
||||
obs_source_get_audio_timestamp(item->source);
|
||||
obs_source_get_audio_timestamp(source);
|
||||
|
||||
if (source_ts && (!timestamp || source_ts < timestamp))
|
||||
timestamp = source_ts;
|
||||
|
@ -1108,16 +1245,24 @@ static bool scene_audio_render(void *data, uint64_t *ts_out,
|
|||
uint64_t source_ts;
|
||||
size_t pos, count;
|
||||
bool apply_buf;
|
||||
struct obs_source *source;
|
||||
if (item->visible && transition_active(item->show_transition))
|
||||
source = item->show_transition;
|
||||
else if (!item->visible &&
|
||||
transition_active(item->hide_transition))
|
||||
source = item->hide_transition;
|
||||
else
|
||||
source = item->source;
|
||||
|
||||
apply_buf = apply_scene_item_volume(item, buf, timestamp,
|
||||
sample_rate);
|
||||
|
||||
if (obs_source_audio_pending(item->source)) {
|
||||
if (obs_source_audio_pending(source)) {
|
||||
item = item->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
source_ts = obs_source_get_audio_timestamp(item->source);
|
||||
source_ts = obs_source_get_audio_timestamp(source);
|
||||
if (!source_ts) {
|
||||
item = item->next;
|
||||
continue;
|
||||
|
@ -1127,12 +1272,14 @@ static bool scene_audio_render(void *data, uint64_t *ts_out,
|
|||
source_ts - timestamp);
|
||||
count = AUDIO_OUTPUT_FRAMES - pos;
|
||||
|
||||
if (!apply_buf && !item->visible) {
|
||||
if (!apply_buf && !item->visible &&
|
||||
!transition_active(item->hide_transition)) {
|
||||
item = item->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
obs_source_get_audio_mix(item->source, &child_audio);
|
||||
obs_source_get_audio_mix(source, &child_audio);
|
||||
|
||||
for (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) {
|
||||
if ((mixers & (1 << mix)) == 0)
|
||||
continue;
|
||||
|
@ -1284,6 +1431,23 @@ static inline void duplicate_item_data(struct obs_scene_item *dst,
|
|||
dst->bounds_align = src->bounds_align;
|
||||
dst->bounds = src->bounds;
|
||||
|
||||
if (src->show_transition) {
|
||||
obs_source_t *transition = obs_source_duplicate(
|
||||
src->show_transition,
|
||||
obs_source_get_name(src->show_transition), true);
|
||||
obs_sceneitem_set_show_transition(dst, transition);
|
||||
obs_source_release(transition);
|
||||
}
|
||||
if (src->hide_transition) {
|
||||
obs_source_t *transition = obs_source_duplicate(
|
||||
src->hide_transition,
|
||||
obs_source_get_name(src->hide_transition), true);
|
||||
obs_sceneitem_set_hide_transition(dst, transition);
|
||||
obs_source_release(transition);
|
||||
}
|
||||
dst->show_transition_duration = src->show_transition_duration;
|
||||
dst->hide_transition_duration = src->hide_transition_duration;
|
||||
|
||||
if (duplicate_hotkeys && !dst_scene->source->context.private) {
|
||||
obs_data_array_t *data0 = NULL;
|
||||
obs_data_array_t *data1 = NULL;
|
||||
|
@ -1776,6 +1940,10 @@ static void obs_sceneitem_destroy(obs_sceneitem_t *item)
|
|||
signal_handler_disconnect(
|
||||
obs_source_get_signal_handler(item->source), "rename",
|
||||
sceneitem_renamed, item);
|
||||
if (item->show_transition)
|
||||
obs_source_release(item->show_transition);
|
||||
if (item->hide_transition)
|
||||
obs_source_release(item->hide_transition);
|
||||
if (item->source)
|
||||
obs_source_release(item->source);
|
||||
da_free(item->audio_actions);
|
||||
|
@ -1822,6 +1990,9 @@ void obs_sceneitem_remove(obs_sceneitem_t *item)
|
|||
|
||||
set_visibility(item, false);
|
||||
|
||||
obs_sceneitem_set_show_transition(item, NULL);
|
||||
obs_sceneitem_set_hide_transition(item, NULL);
|
||||
|
||||
signal_item_remove(item);
|
||||
detach_sceneitem(item);
|
||||
|
||||
|
@ -2128,6 +2299,18 @@ bool obs_sceneitem_visible(const obs_sceneitem_t *item)
|
|||
return item ? item->user_visible : false;
|
||||
}
|
||||
|
||||
static bool group_item_transition(obs_scene_t *scene, obs_sceneitem_t *item,
|
||||
void *param)
|
||||
{
|
||||
if (!param || !item)
|
||||
return true;
|
||||
const bool visible = *(bool *)param;
|
||||
if (obs_sceneitem_visible(item))
|
||||
obs_sceneitem_do_transition(item, visible);
|
||||
UNUSED_PARAMETER(scene);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool obs_sceneitem_set_visible(obs_sceneitem_t *item, bool visible)
|
||||
{
|
||||
struct calldata cd;
|
||||
|
@ -2144,6 +2327,13 @@ bool obs_sceneitem_set_visible(obs_sceneitem_t *item, bool visible)
|
|||
if (!item->parent)
|
||||
return false;
|
||||
|
||||
obs_sceneitem_do_transition(item, visible);
|
||||
if (obs_sceneitem_is_group(item))
|
||||
obs_sceneitem_group_enum_items(item, group_item_transition,
|
||||
&visible);
|
||||
|
||||
item->user_visible = visible;
|
||||
|
||||
if (visible) {
|
||||
if (os_atomic_inc_long(&item->active_refs) == 1) {
|
||||
if (!obs_source_add_active_child(item->parent->source,
|
||||
|
@ -2154,8 +2344,6 @@ bool obs_sceneitem_set_visible(obs_sceneitem_t *item, bool visible)
|
|||
}
|
||||
}
|
||||
|
||||
item->user_visible = visible;
|
||||
|
||||
calldata_init_fixed(&cd, stack, sizeof(stack));
|
||||
calldata_set_ptr(&cd, "item", item);
|
||||
calldata_set_bool(&cd, "visible", visible);
|
||||
|
@ -3013,3 +3201,137 @@ void obs_sceneitem_force_update_transform(obs_sceneitem_t *item)
|
|||
if (os_atomic_set_bool(&item->update_transform, false))
|
||||
update_item_transform(item, false);
|
||||
}
|
||||
|
||||
void obs_sceneitem_set_show_transition(obs_sceneitem_t *item,
|
||||
obs_source_t *transition)
|
||||
{
|
||||
if (!item)
|
||||
return;
|
||||
if (item->show_transition)
|
||||
obs_source_release(item->show_transition);
|
||||
|
||||
item->show_transition = transition;
|
||||
if (item->show_transition)
|
||||
obs_source_addref(item->show_transition);
|
||||
}
|
||||
|
||||
void obs_sceneitem_set_show_transition_duration(obs_sceneitem_t *item,
|
||||
uint32_t duration_ms)
|
||||
{
|
||||
if (!item)
|
||||
return;
|
||||
item->show_transition_duration = duration_ms;
|
||||
}
|
||||
|
||||
obs_source_t *obs_sceneitem_get_show_transition(obs_sceneitem_t *item)
|
||||
{
|
||||
if (!item)
|
||||
return NULL;
|
||||
return item->show_transition;
|
||||
}
|
||||
|
||||
uint32_t obs_sceneitem_get_show_transition_duration(obs_sceneitem_t *item)
|
||||
{
|
||||
if (!item)
|
||||
return 0;
|
||||
return item->show_transition_duration;
|
||||
}
|
||||
|
||||
void obs_sceneitem_set_hide_transition(obs_sceneitem_t *item,
|
||||
obs_source_t *transition)
|
||||
{
|
||||
if (!item)
|
||||
return;
|
||||
if (item->hide_transition)
|
||||
obs_source_release(item->hide_transition);
|
||||
|
||||
item->hide_transition = transition;
|
||||
if (item->hide_transition)
|
||||
obs_source_addref(item->hide_transition);
|
||||
}
|
||||
|
||||
void obs_sceneitem_set_hide_transition_duration(obs_sceneitem_t *item,
|
||||
uint32_t duration_ms)
|
||||
{
|
||||
if (!item)
|
||||
return;
|
||||
item->hide_transition_duration = duration_ms;
|
||||
}
|
||||
|
||||
obs_source_t *obs_sceneitem_get_hide_transition(obs_sceneitem_t *item)
|
||||
{
|
||||
if (!item)
|
||||
return NULL;
|
||||
return item->hide_transition;
|
||||
}
|
||||
|
||||
uint32_t obs_sceneitem_get_hide_transition_duration(obs_sceneitem_t *item)
|
||||
{
|
||||
if (!item)
|
||||
return 0;
|
||||
return item->hide_transition_duration;
|
||||
}
|
||||
|
||||
void obs_sceneitem_transition_stop(void *data, calldata_t *calldata)
|
||||
{
|
||||
obs_source_t *parent = data;
|
||||
obs_source_t *transition;
|
||||
calldata_get_ptr(calldata, "source", &transition);
|
||||
obs_source_remove_active_child(parent, transition);
|
||||
signal_handler_t *sh = obs_source_get_signal_handler(transition);
|
||||
if (sh)
|
||||
signal_handler_disconnect(sh, "transition_stop",
|
||||
obs_sceneitem_transition_stop,
|
||||
parent);
|
||||
}
|
||||
|
||||
void obs_sceneitem_do_transition(obs_sceneitem_t *item, bool visible)
|
||||
{
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
if (transition_active(item->show_transition))
|
||||
obs_transition_force_stop(item->show_transition);
|
||||
|
||||
if (transition_active(item->hide_transition))
|
||||
obs_transition_force_stop(item->hide_transition);
|
||||
|
||||
obs_source_t *transition =
|
||||
visible ? obs_sceneitem_get_show_transition(item)
|
||||
: obs_sceneitem_get_hide_transition(item);
|
||||
if (!transition)
|
||||
return;
|
||||
|
||||
int duration =
|
||||
(int)(visible ? obs_sceneitem_get_show_transition_duration(item)
|
||||
: obs_sceneitem_get_hide_transition_duration(
|
||||
item));
|
||||
|
||||
const int cx = obs_source_get_width(item->source);
|
||||
const int cy = obs_source_get_height(item->source);
|
||||
obs_transition_set_size(transition, cx, cy);
|
||||
obs_transition_set_alignment(transition, OBS_ALIGN_CENTER);
|
||||
obs_transition_set_scale_type(transition, OBS_TRANSITION_SCALE_ASPECT);
|
||||
|
||||
if (duration == 0)
|
||||
duration = 300;
|
||||
|
||||
obs_scene_t *scene = obs_sceneitem_get_scene(item);
|
||||
obs_source_t *parent = obs_scene_get_source(scene);
|
||||
obs_source_add_active_child(parent, transition);
|
||||
|
||||
signal_handler_t *sh = obs_source_get_signal_handler(transition);
|
||||
if (sh)
|
||||
signal_handler_connect(sh, "transition_stop",
|
||||
obs_sceneitem_transition_stop, parent);
|
||||
|
||||
if (!visible) {
|
||||
obs_transition_set(transition, item->source);
|
||||
obs_transition_start(transition, OBS_TRANSITION_MODE_AUTO,
|
||||
duration, NULL);
|
||||
} else {
|
||||
obs_transition_set(transition, NULL);
|
||||
obs_transition_start(transition, OBS_TRANSITION_MODE_AUTO,
|
||||
duration, item->source);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,6 +78,11 @@ struct obs_scene_item {
|
|||
pthread_mutex_t actions_mutex;
|
||||
DARRAY(struct item_action) audio_actions;
|
||||
|
||||
struct obs_source *show_transition;
|
||||
struct obs_source *hide_transition;
|
||||
uint32_t show_transition_duration;
|
||||
uint32_t hide_transition_duration;
|
||||
|
||||
/* would do **prev_next, but not really great for reordering */
|
||||
struct obs_scene_item *prev;
|
||||
struct obs_scene_item *next;
|
||||
|
|
16
libobs/obs.h
16
libobs/obs.h
|
@ -1716,6 +1716,22 @@ EXPORT obs_scene_t *obs_group_from_source(const obs_source_t *source);
|
|||
EXPORT void obs_sceneitem_defer_group_resize_begin(obs_sceneitem_t *item);
|
||||
EXPORT void obs_sceneitem_defer_group_resize_end(obs_sceneitem_t *item);
|
||||
|
||||
EXPORT void obs_sceneitem_set_show_transition(obs_sceneitem_t *item,
|
||||
obs_source_t *transition);
|
||||
EXPORT void obs_sceneitem_set_show_transition_duration(obs_sceneitem_t *item,
|
||||
uint32_t duration_ms);
|
||||
EXPORT obs_source_t *obs_sceneitem_get_show_transition(obs_sceneitem_t *item);
|
||||
EXPORT uint32_t
|
||||
obs_sceneitem_get_show_transition_duration(obs_sceneitem_t *item);
|
||||
EXPORT void obs_sceneitem_set_hide_transition(obs_sceneitem_t *item,
|
||||
obs_source_t *transition);
|
||||
EXPORT void obs_sceneitem_set_hide_transition_duration(obs_sceneitem_t *item,
|
||||
uint32_t duration_ms);
|
||||
EXPORT obs_source_t *obs_sceneitem_get_hide_transition(obs_sceneitem_t *item);
|
||||
EXPORT uint32_t
|
||||
obs_sceneitem_get_hide_transition_duration(obs_sceneitem_t *item);
|
||||
EXPORT void obs_sceneitem_do_transition(obs_sceneitem_t *item, bool visible);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Outputs */
|
||||
|
||||
|
|
Loading…
Reference in New Issue