OBS 源窗口 信号槽分析
0 派生的QListWidget子类 SourceListWidget
#include <QListWidget>
class QMouseEvent;
class SourceListWidget : public QListWidget {
Q_OBJECT
bool ignoreReorder = false;
public:
inline SourceListWidget(QWidget *parent = nullptr)
: QListWidget(parent)
{
}
bool IgnoreReorder() const { return ignoreReorder; }
protected:
virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
virtual void dropEvent(QDropEvent *event) override;
};
重载了双击事件
void SourceListWidget::mouseDoubleClickEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
QListWidget::mouseDoubleClickEvent(event);
}
1 源的添加
private slots:
void AddSceneItem(OBSSceneItem item);
将场景项目,添加到该场景中
void OBSBasic::AddSceneItem(OBSSceneItem item)
{
obs_scene_t *scene = obs_sceneitem_get_scene(item);
if (GetCurrentScene() == scene)
InsertSceneItem(item);
SaveProject();
/*
obs_source_t *sceneSource = obs_scene_get_source(scene); //场景 源
obs_source_t *itemSource = obs_sceneitem_get_source(item); //场景中的 子项 源
QString name1= obs_source_get_name(itemSource); //视频捕获设备
QString name2 = obs_source_get_id(itemSource); //dshow_input
QString name3 = obs_source_get_name(sceneSource);//场景
blog(LOG_INFO, "User added source '%s' (%s) to scene '%s'",
obs_source_get_name(itemSource),
obs_source_get_id(itemSource),
obs_source_get_name(sceneSource));
*/
if (!disableSaving) {
obs_source_t *sceneSource = obs_scene_get_source(scene);
obs_source_t *itemSource = obs_sceneitem_get_source(item);
blog(LOG_INFO, "User added source '%s' (%s) to scene '%s'",
obs_source_get_name(itemSource),
obs_source_get_id(itemSource),
obs_source_get_name(sceneSource));
}
}
2 源添加槽函数 信号追踪
这是个槽函数,是由什么信号引发的呢
void OBSBasic::SceneItemAdded(void *data, calldata_t *params)
{
OBSBasic *window = static_cast<OBSBasic*>(data);
obs_sceneitem_t *item = (obs_sceneitem_t*)calldata_ptr(params, "item");
QMetaObject::invokeMethod(window, "AddSceneItem",
Q_ARG(OBSSceneItem, OBSSceneItem(item)));
}
SceneItemAdded函数 不是个信号,因此不是这个函数直接引起的,应该是有什么信号,引起了这个函数。
那这个函数是在什么时候调用的呢,我们看看添加场景的函数
其中,定义了一个信号容器
SignalContainer<OBSScene> container
template <typename OBSRef>
struct SignalContainer {
OBSRef ref;
vector<shared_ptr<OBSSignal>> handlers;
};
container.ref = scene;
然后为handlers vector容器赋值
container.handlers.assign({
std::make_shared<OBSSignal>(handler, "item_add",
OBSBasic::SceneItemAdded, this),
std::make_shared<OBSSignal>(handler, "item_remove",
OBSBasic::SceneItemRemoved, this),
std::make_shared<OBSSignal>(handler, "item_select",
OBSBasic::SceneItemSelected, this),
std::make_shared<OBSSignal>(handler, "item_deselect",
OBSBasic::SceneItemDeselected, this),
std::make_shared<OBSSignal>(handler, "reorder",
OBSBasic::SceneReordered, this),
});
这个容器,存放了
该场景的一些信息及其处理方式
signal_handler_t *handler = obs_source_get_signal_handler(source); //场景信号 handler
std::make_shared<OBSSignal>(handler, "item_add",
OBSBasic::SceneItemAdded, this)
看看OBSSignal是如何定义的
/* signal handler connection */
class OBSSignal {
signal_handler_t *handler;
const char *signal;
signal_callback_t callback;
void *param;
public:
inline OBSSignal()
: handler (nullptr),
signal (nullptr),
callback (nullptr),
param (nullptr)
{}
inline OBSSignal(signal_handler_t *handler_,
const char *signal_,
signal_callback_t callback_,
void *param_)
: handler (handler_),
signal (signal_),
callback (callback_),
param (param_)
{
signal_handler_connect(handler, signal, callback, param);
}
inline void Disconnect()
{
signal_handler_disconnect(handler, signal, callback, param);
handler = nullptr;
signal = nullptr;
callback = nullptr;
param = nullptr;
}
inline ~OBSSignal() {Disconnect();}
inline void Connect(signal_handler_t *handler_,
const char *signal_,
signal_callback_t callback_,
void *param_)
{
Disconnect();
handler = handler_;
signal = signal_;
callback = callback_;
param = param_;
signal_handler_connect(handler, signal, callback, param);
}
OBSSignal(const OBSSignal&) = delete;
OBSSignal(OBSSignal &&other)
: handler (other.handler),
signal (other.signal),
callback(other.callback),
param (other.param)
{
other.handler = nullptr;
other.signal = nullptr;
other.callback = nullptr;
other.param = nullptr;
}
OBSSignal &operator=(const OBSSignal &) = delete;
OBSSignal &operator=(OBSSignal &&other)
{
Disconnect();
handler = other.handler;
signal = other.signal;
callback = other.callback;
param = other.param;
other.handler = nullptr;
other.signal = nullptr;
other.callback = nullptr;
other.param = nullptr;
return *this;
}
};
将信号容器,设置为该场景的data
QListWidgetItem *item = new QListWidgetItem(QT_UTF8(name));
SetOBSRef(item, OBSScene(scene));
ui->scenes->addItem(item);
//注意 item为场景,信号容器为该场景data
item->setData(static_cast<int>(QtDataRole::OBSSignals),
QVariant::fromValue(container));
也就是说,每个场景,都有自己的 信号容器
void OBSBasic::AddScene(OBSSource source)
{
const char *name = obs_source_get_name(source);
obs_scene_t *scene = obs_scene_from_source(source);
QListWidgetItem *item = new QListWidgetItem(QT_UTF8(name));
SetOBSRef(item, OBSScene(scene));
ui->scenes->addItem(item);
obs_hotkey_register_source(source, "OBSBasic.SelectScene",
Str("Basic.Hotkeys.SelectScene"),
[](void *data,
obs_hotkey_id, obs_hotkey_t*, bool pressed)
{
OBSBasic *main =
reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
auto potential_source = static_cast<obs_source_t*>(data);
auto source = obs_source_get_ref(potential_source);
if (source && pressed)
main->SetCurrentScene(source);
obs_source_release(source);
}, static_cast<obs_source_t*>(source));
signal_handler_t *handler = obs_source_get_signal_handler(source);
SignalContainer<OBSScene> container;
container.ref = scene;
container.handlers.assign({
std::make_shared<OBSSignal>(handler, "item_add",
OBSBasic::SceneItemAdded, this),
std::make_shared<OBSSignal>(handler, "item_remove",
OBSBasic::SceneItemRemoved, this),
std::make_shared<OBSSignal>(handler, "item_select",
OBSBasic::SceneItemSelected, this),
std::make_shared<OBSSignal>(handler, "item_deselect",
OBSBasic::SceneItemDeselected, this),
std::make_shared<OBSSignal>(handler, "reorder",
OBSBasic::SceneReordered, this),
});
item->setData(static_cast<int>(QtDataRole::OBSSignals),
QVariant::fromValue(container));
/* if the scene already has items (a duplicated scene) add them */
auto addSceneItem = [this] (obs_sceneitem_t *item)
{
AddSceneItem(item);
};
using addSceneItem_t = decltype(addSceneItem);
obs_scene_enum_items(scene,
[] (obs_scene_t*, obs_sceneitem_t *item, void *param)
{
addSceneItem_t *func;
func = reinterpret_cast<addSceneItem_t*>(param);
(*func)(item);
return true;
}, &addSceneItem);
SaveProject();
if (!disableSaving) {
obs_source_t *source = obs_scene_get_source(scene);
blog(LOG_INFO, "User added scene '%s'",
obs_source_get_name(source));
}
if (api)
api->on_event(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED);
}
3 信号的触发
每个场景,都有自己的 信号容器
那这个信号是如何触发的呢?
比如item_add信号
分析libobs核心库 往场景中添加源的代码obs_scene_add
/** Adds/creates a new scene item for a source */
EXPORT obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source);
在这段往场景中添加源的函数中,
有这么一句信号触发函数
signal_handler_signal(scene->source->context.signals, "item_add",
¶ms);
也就是从这里触发了 itam_add信号
因为该信号关联了
std::make_shared<OBSSignal>(handler, "item_add",
OBSBasic::SceneItemAdded, this),
因此会执行SceneItemAdded函数,该函数又触发了了UI层的SceneItemAdded 槽函数
void OBSBasic::SceneItemAdded(void *data, calldata_t *params)
{
OBSBasic *window = static_cast<OBSBasic*>(data);
obs_sceneitem_t *item = (obs_sceneitem_t*)calldata_ptr(params, "item");
QMetaObject::invokeMethod(window, "AddSceneItem",
Q_ARG(OBSSceneItem, OBSSceneItem(item)));
}
obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source)
{
struct obs_scene_item *last;
struct obs_scene_item *item;
struct calldata params;
uint8_t stack[128];
pthread_mutex_t mutex;
struct item_action action = {
.visible = true,
.timestamp = os_gettime_ns()
};
if (!scene)
return NULL;
if (!source) {
blog(LOG_ERROR, "Tried to add a NULL source to a scene");
return NULL;
}
if (pthread_mutex_init(&mutex, NULL) != 0) {
blog(LOG_WARNING, "Failed to create scene item mutex");
return NULL;
}
if (!obs_source_add_active_child(scene->source, source)) {
blog(LOG_WARNING, "Failed to add source to scene due to "
"infinite source recursion");
pthread_mutex_destroy(&mutex);
return NULL;
}
item = bzalloc(sizeof(struct obs_scene_item));
item->source = source;
item->id = ++scene->id_counter;
item->parent = scene;
item->ref = 1;
item->align = OBS_ALIGN_TOP | OBS_ALIGN_LEFT;
item->actions_mutex = mutex;
item->user_visible = true;
item->locked = false;
os_atomic_set_long(&item->active_refs, 1);
vec2_set(&item->scale, 1.0f, 1.0f);
matrix4_identity(&item->draw_transform);
matrix4_identity(&item->box_transform);
obs_source_addref(source);
if (source_has_audio(source)) {
item->visible = false;
da_push_back(item->audio_actions, &action);
} else {
item->visible = true;
}
if (item_texture_enabled(item)) {
obs_enter_graphics();
item->item_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
obs_leave_graphics();
}
full_lock(scene);
last = scene->first_item;
if (!last) {
scene->first_item = item;
} else {
while (last->next)
last = last->next;
last->next = item;
item->prev = last;
}
full_unlock(scene);
if (!scene->source->context.private)
init_hotkeys(scene, item, obs_source_get_name(source));
calldata_init_fixed(¶ms, stack, sizeof(stack));
calldata_set_ptr(¶ms, "scene", scene);
calldata_set_ptr(¶ms, "item", item);
signal_handler_signal(scene->source->context.signals, "item_add",
¶ms);
return item;
}
最后,我们看看 obs_scene_add主要会在哪些情况下调用呢
1) scene_load_item
static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
{
const char *name = obs_data_get_string(item_data, "name");
obs_source_t *source = obs_get_source_by_name(name);
const char *scale_filter_str;
struct obs_scene_item *item;
bool visible;
bool lock;
if (!source) {
blog(LOG_WARNING, "[scene_load_item] Source %s not found!",
name);
return;
}
item = obs_scene_add(scene, source);
2) AddSource
static void AddSource(void *_data, obs_scene_t *scene)
{
AddSourceData *data = (AddSourceData *)_data;
obs_sceneitem_t *sceneitem;
sceneitem = obs_scene_add(scene, data->source);
obs_sceneitem_set_visible(sceneitem, data->visible);
}
而AddSource 主要是创建新源时调用
source = obs_source_create(id, name, NULL, nullptr); //创建初始新源 类型为id 名字name 其它默认
if (source) {
AddSourceData data;
data.source = source;
data.visible = visible;
obs_scene_atomic_update(scene, AddSource, &data);
newSource = source;
success = true;
}
所以,再OBS中,添加新源时,在UI层的源列表中,会自动的更新