libobs: Change groups to actual public types

(This commit also modifies UI)

Changes groups to their own independent type, "group".  This allows them
to be used like other regular types, and allows the ability to reference
groups in multiple scenes.  Before, a group would always be linked to
the scene it was in.  This made it cumbersome for users to modify groups
if they had a similar group in multiple scenes (they would have to
modify each group in each scene).  Making groups like other source types
makes more sense to solve this issue so they can be referenced in
multiple scenes at once.  This also removes a significant amount of
group-specific handling code required for implementing groups in the
front-end.

One limitation however: due to the way sub-items of groups are
seamlessly modifiable and sortable as part of the whole scene, the user
cannot have multiple references to the same group within one scene.
master
jp9000 2018-07-15 18:50:33 -07:00
parent f45247d5c8
commit 5993834815
6 changed files with 128 additions and 65 deletions

View File

@ -228,16 +228,10 @@ void SourceTreeItem::ExitEditMode(bool save)
/* ----------------------------------------- */
/* check for existing source */
bool exists = false;
if (obs_sceneitem_is_group(sceneitem)) {
exists = !!obs_scene_get_group(scene, newName.c_str());
} else {
obs_source_t *existingSource =
obs_get_source_by_name(newName.c_str());
obs_source_release(existingSource);
exists = !!existingSource;
}
obs_source_t *existingSource =
obs_get_source_by_name(newName.c_str());
obs_source_release(existingSource);
bool exists = !!existingSource;
if (exists) {
OBSMessageBox::information(main,
@ -550,11 +544,15 @@ void SourceTreeModel::ReorderItems()
void SourceTreeModel::Add(obs_sceneitem_t *item)
{
beginInsertRows(QModelIndex(), 0, 0);
items.insert(0, item);
endInsertRows();
if (obs_sceneitem_is_group(item)) {
SceneChanged();
} else {
beginInsertRows(QModelIndex(), 0, 0);
items.insert(0, item);
endInsertRows();
st->UpdateWidget(createIndex(0, 0, nullptr), item);
st->UpdateWidget(createIndex(0, 0, nullptr), item);
}
}
void SourceTreeModel::Remove(obs_sceneitem_t *item)
@ -652,8 +650,8 @@ QString SourceTreeModel::GetNewGroupName()
int i = 2;
for (;;) {
obs_sceneitem_t *group = obs_scene_get_group(scene,
QT_TO_UTF8(name));
obs_source_t *group = obs_get_source_by_name(QT_TO_UTF8(name));
obs_source_release(group);
if (!group)
break;
name = QTStr("Basic.Main.Group").arg(QString::number(i++));

View File

@ -315,8 +315,14 @@ static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder,
SaveAudioDevice(AUX_AUDIO_2, 4, saveData, audioSources);
SaveAudioDevice(AUX_AUDIO_3, 5, saveData, audioSources);
/* -------------------------------- */
/* save non-group sources */
auto FilterAudioSources = [&](obs_source_t *source)
{
if (obs_source_is_group(source))
return false;
return find(begin(audioSources), end(audioSources), source) ==
end(audioSources);
};
@ -328,6 +334,18 @@ static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder,
return (*static_cast<FilterAudioSources_t*>(data))(source);
}, static_cast<void*>(&FilterAudioSources));
/* -------------------------------- */
/* save group sources separately */
/* saving separately ensures they won't be loaded in older versions */
obs_data_array_t *groupsArray = obs_save_sources_filtered(
[](void*, obs_source_t *source)
{
return obs_source_is_group(source);
}, nullptr);
/* -------------------------------- */
obs_source_t *transition = obs_get_output_source(0);
obs_source_t *currentScene = obs_scene_get_source(scene);
const char *sceneName = obs_source_get_name(currentScene);
@ -341,10 +359,12 @@ static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder,
obs_data_set_array(saveData, "scene_order", sceneOrder);
obs_data_set_string(saveData, "name", sceneCollection);
obs_data_set_array(saveData, "sources", sourcesArray);
obs_data_set_array(saveData, "groups", groupsArray);
obs_data_set_array(saveData, "quick_transitions", quickTransitionData);
obs_data_set_array(saveData, "transitions", transitions);
obs_data_set_array(saveData, "saved_projectors", savedProjectorList);
obs_data_array_release(sourcesArray);
obs_data_array_release(groupsArray);
obs_data_set_string(saveData, "current_transition",
obs_source_get_name(transition));
@ -719,6 +739,7 @@ void OBSBasic::Load(const char *file)
obs_data_array_t *sceneOrder = obs_data_get_array(data, "scene_order");
obs_data_array_t *sources = obs_data_get_array(data, "sources");
obs_data_array_t *groups = obs_data_get_array(data, "groups");
obs_data_array_t *transitions= obs_data_get_array(data, "transitions");
const char *sceneName = obs_data_get_string(data,
"current_scene");
@ -759,6 +780,13 @@ void OBSBasic::Load(const char *file)
LoadAudioDevice(AUX_AUDIO_2, 4, data);
LoadAudioDevice(AUX_AUDIO_3, 5, data);
if (!sources) {
sources = groups;
groups = nullptr;
} else {
obs_data_array_push_back_array(sources, groups);
}
obs_load_sources(sources, nullptr, nullptr);
if (transitions)
@ -803,6 +831,7 @@ retryScene:
obs_source_release(curProgramScene);
obs_data_array_release(sources);
obs_data_array_release(groups);
obs_data_array_release(sceneOrder);
/* ------------------- */
@ -4043,8 +4072,9 @@ QMenu *OBSBasic::CreateAddSourcePopupMenu()
popup->addSeparator();
QAction *addGroup = new QAction(QTStr("Group"), this);
addGroup->setData(QT_UTF8("group"));
connect(addGroup, SIGNAL(triggered(bool)),
ui->sources, SLOT(AddGroup()));
this, SLOT(AddSourceFromAction()));
popup->addAction(addGroup);
if (!foundDeprecated) {

View File

@ -38,6 +38,25 @@ bool OBSBasicSourceSelect::EnumSources(void *data, obs_source_t *source)
return true;
}
bool OBSBasicSourceSelect::EnumGroups(void *data, obs_source_t *source)
{
OBSBasicSourceSelect *window = static_cast<OBSBasicSourceSelect*>(data);
const char *name = obs_source_get_name(source);
const char *id = obs_source_get_id(source);
if (strcmp(id, window->id) == 0) {
OBSBasic *main = reinterpret_cast<OBSBasic*>(
App()->GetMainWindow());
OBSScene scene = main->GetCurrentScene();
obs_sceneitem_t *existing = obs_scene_get_group(scene, name);
if (!existing)
window->ui->sourceList->addItem(QT_UTF8(name));
}
return true;
}
void OBSBasicSourceSelect::OBSSourceAdded(void *data, calldata_t *calldata)
{
OBSBasicSourceSelect *window = static_cast<OBSBasicSourceSelect*>(data);
@ -277,6 +296,8 @@ OBSBasicSourceSelect::OBSBasicSourceSelect(OBSBasic *parent, const char *id_)
const char *name = obs_source_get_name(sceneSource);
ui->sourceList->addItem(QT_UTF8(name));
}
} else if (strcmp(id_, "group") == 0) {
obs_enum_sources(EnumGroups, this);
} else {
obs_enum_sources(EnumSources, this);
}

View File

@ -32,6 +32,7 @@ private:
const char *id;
static bool EnumSources(void *data, obs_source_t *source);
static bool EnumGroups(void *data, obs_source_t *source);
static void OBSSourceRemoved(void *data, calldata_t *calldata);
static void OBSSourceAdded(void *data, calldata_t *calldata);

View File

@ -20,7 +20,10 @@
#include "graphics/math-defs.h"
#include "obs-scene.h"
const struct obs_source_info group_info;
static void resize_group(obs_sceneitem_t *group);
static void resize_scene(obs_scene_t *scene);
static void signal_parent(obs_scene_t *parent, const char *name,
calldata_t *params);
static void get_ungrouped_transform(obs_sceneitem_t *group,
@ -67,12 +70,25 @@ static const char *scene_getname(void *unused)
return "Scene";
}
static const char *group_getname(void *unused)
{
UNUSED_PARAMETER(unused);
return "Group";
}
static void *scene_create(obs_data_t *settings, struct obs_source *source)
{
pthread_mutexattr_t attr;
struct obs_scene *scene = bzalloc(sizeof(struct obs_scene));
scene->source = source;
if (source->info.id == group_info.id) {
scene->is_group = true;
scene->custom_size = true;
scene->cx = 0;
scene->cy = 0;
}
signal_handler_add_array(obs_source_get_signal_handler(source),
obs_scene_signals);
@ -637,26 +653,17 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
obs_source_t *source;
const char *scale_filter_str;
struct obs_scene_item *item;
bool is_group;
bool visible;
bool lock;
if (obs_data_get_bool(item_data, "group_item_backup"))
return;
is_group = obs_data_get_bool(item_data, "is_group");
if (is_group) {
obs_scene_t *sub_scene = obs_scene_create_private(name);
source = sub_scene->source;
} else {
source = obs_get_source_by_name(name);
if (!source) {
blog(LOG_WARNING, "[scene_load_item] Source %s not "
"found!", name);
return;
}
source = obs_get_source_by_name(name);
if (!source) {
blog(LOG_WARNING, "[scene_load_item] Source %s not "
"found!", name);
return;
}
item = obs_scene_add(scene, source);
@ -669,10 +676,7 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
return;
}
if (is_group) {
item->is_group = true;
((obs_scene_t *)source->context.data)->is_group = true;
}
item->is_group = source->info.id == group_info.id;
obs_data_set_default_int(item_data, "align",
OBS_ALIGN_TOP | OBS_ALIGN_LEFT);
@ -708,13 +712,6 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
item->crop.right = (uint32_t)obs_data_get_int(item_data, "crop_right");
item->crop.bottom = (uint32_t)obs_data_get_int(item_data, "crop_bottom");
if (item->is_group) {
obs_data_t *group_data = obs_data_get_obj(item_data,
"group_source");
scene_load(source->context.data, group_data);
obs_data_release(group_data);
}
scale_filter_str = obs_data_get_string(item_data, "scale_filter");
item->scale_filter = OBS_SCALE_DISABLE;
@ -808,16 +805,12 @@ static void scene_save_item(obs_data_array_t *array,
obs_data_set_int (item_data, "crop_right", (int)item->crop.right);
obs_data_set_int (item_data, "crop_bottom", (int)item->crop.bottom);
obs_data_set_int (item_data, "id", item->id);
obs_data_set_bool (item_data, "is_group", item->is_group);
obs_data_set_bool (item_data, "group_item_backup", !!backup_group);
if (item->is_group) {
obs_data_t *group_data = obs_data_create();
obs_scene_t *group_scene = item->source->context.data;
obs_sceneitem_t *group_item;
scene_save(group_scene, group_data);
/* save group items as part of main scene, but ignored.
* causes an automatic ungroup if scene collection file
* is loaded in previous versions. */
@ -830,9 +823,6 @@ static void scene_save_item(obs_data_array_t *array,
}
full_unlock(group_scene);
obs_data_set_obj(item_data, "group_source", group_data);
obs_data_release(group_data);
}
if (item->scale_filter == OBS_SCALE_POINT)
@ -1125,6 +1115,27 @@ const struct obs_source_info scene_info =
.enum_all_sources = scene_enum_all_sources
};
const struct obs_source_info group_info =
{
.id = "group",
.type = OBS_SOURCE_TYPE_SCENE,
.output_flags = OBS_SOURCE_VIDEO |
OBS_SOURCE_CUSTOM_DRAW |
OBS_SOURCE_COMPOSITE,
.get_name = group_getname,
.create = scene_create,
.destroy = scene_destroy,
.video_tick = scene_video_tick,
.video_render = scene_video_render,
.audio_render = scene_audio_render,
.get_width = scene_getwidth,
.get_height = scene_getheight,
.load = scene_load,
.save = scene_save,
.enum_active_sources = scene_enum_active_sources,
.enum_all_sources = scene_enum_all_sources
};
static inline obs_scene_t *create_id(const char *id, const char *name)
{
struct obs_source *source = obs_source_create(id, name, NULL,
@ -1505,8 +1516,7 @@ static inline bool source_has_audio(obs_source_t *source)
}
static obs_sceneitem_t *obs_scene_add_internal(obs_scene_t *scene,
obs_source_t *source, obs_sceneitem_t *insert_after,
bool is_group)
obs_source_t *source, obs_sceneitem_t *insert_after)
{
struct obs_scene_item *last;
struct obs_scene_item *item;
@ -1546,7 +1556,7 @@ static obs_sceneitem_t *obs_scene_add_internal(obs_scene_t *scene,
item->actions_mutex = mutex;
item->user_visible = true;
item->locked = false;
item->is_group = is_group;
item->is_group = source->info.id == group_info.id;
item->private_settings = obs_data_create();
os_atomic_set_long(&item->active_refs, 1);
vec2_set(&item->scale, 1.0f, 1.0f);
@ -1602,8 +1612,7 @@ static obs_sceneitem_t *obs_scene_add_internal(obs_scene_t *scene,
obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source)
{
obs_sceneitem_t *item = obs_scene_add_internal(scene, source, NULL,
false);
obs_sceneitem_t *item = obs_scene_add_internal(scene, source, NULL);
struct calldata params;
uint8_t stack[128];
@ -2425,12 +2434,11 @@ obs_sceneitem_t *obs_scene_insert_group(obs_scene_t *scene,
return NULL;
}
obs_scene_t *sub_scene = create_private_id("scene", name);
obs_scene_t *sub_scene = create_id("group", name);
obs_sceneitem_t *last_item = items ? items[count - 1] : NULL;
obs_sceneitem_t *item = obs_scene_add_internal(
scene, sub_scene->source, last_item, true);
sub_scene->custom_size = true;
scene, sub_scene->source, last_item);
obs_scene_release(sub_scene);
@ -2531,6 +2539,8 @@ void obs_sceneitem_group_ungroup(obs_sceneitem_t *item)
last = last->next;
}
subscene->first_item = NULL;
subscene->cx = 0;
subscene->cy = 0;
full_unlock(subscene);
/* ------------------------- */
@ -2822,10 +2832,7 @@ obs_sceneitem_t *obs_sceneitem_get_group(obs_scene_t *scene,
bool obs_source_is_group(const obs_source_t *source)
{
if (!source || source->info.id != scene_info.id)
return false;
return ((obs_scene_t *)source->context.data)->is_group;
return source && source->info.id == group_info.id;
}
bool obs_scene_is_group(const obs_scene_t *scene)
@ -2840,7 +2847,7 @@ void obs_sceneitem_group_enum_items(obs_sceneitem_t *group,
if (!group || !group->is_group)
return;
obs_scene_t *scene = obs_scene_from_source(group->source);
obs_scene_t *scene = group->source->context.data;
if (scene)
obs_scene_enum_items(scene, callback, param);
}

View File

@ -741,6 +741,7 @@ static inline void obs_free_hotkeys(void)
}
extern const struct obs_source_info scene_info;
extern const struct obs_source_info group_info;
extern void log_system_info(void);
@ -771,6 +772,7 @@ static bool obs_init(const char *locale, const char *module_config_path,
obs->module_config_path = bstrdup(module_config_path);
obs->locale = bstrdup(locale);
obs_register_source(&scene_info);
obs_register_source(&group_info);
add_default_module_paths();
return true;
}
@ -1304,10 +1306,14 @@ void obs_enum_sources(bool (*enum_proc)(void*, obs_source_t*), void *param)
obs_source_t *next_source =
(obs_source_t*)source->context.next;
if ((source->info.type == OBS_SOURCE_TYPE_INPUT) != 0 &&
!source->context.private &&
!enum_proc(param, source))
if (source->info.id == group_info.id &&
!enum_proc(param, source)) {
break;
} else if (source->info.type == OBS_SOURCE_TYPE_INPUT &&
!source->context.private &&
!enum_proc(param, source)) {
break;
}
source = next_source;
}