欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

OBS 源窗口 信号槽分析

程序员文章站 2022-07-08 22:19:18
...

 

 

OBS 源窗口 信号槽分析

 

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",
            &params);

也就是从这里触发了  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(&params, stack, sizeof(stack));
	calldata_set_ptr(&params, "scene", scene);
	calldata_set_ptr(&params, "item", item);
	signal_handler_signal(scene->source->context.signals, "item_add",
			&params);

	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层的源列表中,会自动的更新

 

 

 

 

相关标签: obs