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

OBS 源的菜单创建 及添加源的处理

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

OBS添加各种源的操作 

集中在 void OBSBasic::AddSource(const char *id)

 

这是通过菜单项ACTION 对应的槽函数 跳转到这里的

 

void OBSBasic::AddSourceFromAction()
{
	QAction *action = qobject_cast<QAction*>(sender());
	if (!action)
		return;

	AddSource(QT_TO_UTF8(action->data().toString()));
}

 

一  各个源的菜单项

OBS中  创建源的菜单是在 QMenu *OBSBasic::CreateAddSourcePopupMenu()

 

在这里遍历各个源,并将其加入菜单中

	while (obs_enum_input_types(idx++, &type)) {
		const char *name = obs_source_get_display_name(type);
		uint32_t caps = obs_get_source_output_flags(type);
		//...
		if (g_bMyCustom)
		{
			if (strcmp(type, "image_source") == 0 || strcmp(type, "monitor_capture") == 0 || strcmp(type, "dshow_input") == 0) //...
			{
				if ((caps & OBS_SOURCE_DEPRECATED) == 0) {
					addSource(popup, type, name);					
				}
				else {
					addSource(deprecated, type, name);
					foundDeprecated = true;
				}
				foundValues = true;
			}
		}
		else{
			if ((caps & OBS_SOURCE_DEPRECATED) == 0) {
				addSource(popup, type, name);
				
			}
			else {
				addSource(deprecated, type, name);
				foundDeprecated = true;
			}
			foundValues = true;
		}	
	}
/**
 * Enumerates all available inputs source types.
 *
 *   Inputs are general source inputs (such as capture sources, device sources,
 * etc).
 */
EXPORT bool obs_enum_input_types(size_t idx, const char **id);

我这里遍历项

type                                      name

image_source                      图像

color_source                        色源

slideshow                            图像幻灯片放映

ffmpeg_source                    媒体源

img_TransImg                    透明图

text_gdiplus                        文本 (GDI+)

text_ft2_source                   文本 (FreeType 2)

monitor_capture                 显示器捕获

window_capture                 窗口捕获

game_capture                   游戏捕获

dshow_input                      视频捕获设备

wasapi_input_capture       音频输入捕获

wasapi_output_capture     音频输出捕获

 

在这里将每个源添加到菜单中的函数为 addSource(popup, type, name);


	QMenu *popup = new QMenu(QTStr("Add"), this);
	QMenu *deprecated = new QMenu(QTStr("Deprecated"), popup);

	auto getActionAfter = [] (QMenu *menu, const QString &name)
	{
		if (g_bMyCustom){
			return (QAction*)nullptr;
		}

		QList<QAction*> actions = menu->actions();
		for (QAction *menuAction : actions) {
			if (menuAction->text().compare(name) >= 0)
				return menuAction;
		}

		return (QAction*)nullptr;
	};

	auto addSource = [this, getActionAfter] (QMenu *popup,
			const char *type, const char *name)
	{
		QString qname = QT_UTF8(name);

		if (strcmp(type, "image_source") == 0){
			qname = QStringLiteral("背景图片"); //...
		}
	
		QAction *popupItem = new QAction(qname, this);
		popupItem->setData(QT_UTF8(type));
		connect(popupItem, SIGNAL(triggered(bool)),
				this, SLOT(AddSourceFromAction()));

		QAction *after = getActionAfter(popup, qname);
		popup->insertAction(after, popupItem);
	};

 

其中,创建菜单 并关联槽函数为以下代码

 


        QAction *popupItem = new QAction(qname, this);
        popupItem->setData(QT_UTF8(type));
        connect(popupItem, SIGNAL(triggered(bool)),
                this, SLOT(AddSourceFromAction()));

 

槽函数的处理 :

void OBSBasic::AddSourceFromAction()
{
    QAction *action = qobject_cast<QAction*>(sender());
    if (!action)
        return;

    AddSource(QT_TO_UTF8(action->data().toString()));
}

 

//... 源属性 新添源
void OBSBasic::AddSource(const char *id)  //这里的id  就是上面遍历各源时的type 如 image_source 、dshow_input、monitor_capture

 


 

二 通过OBSBasicSourceSelect 对话框,往场景中添加新源

右键添加源时,会先跳出 创建或选择窗口

OBS 源的菜单创建 及添加源的处理

 

这个窗口是 OBSBasicSourceSelect

OBS ui文件中,也有这个窗口

 

OBS 源的菜单创建 及添加源的处理

 

class OBSBasic;

class OBSBasicSourceSelect : public QDialog {
	Q_OBJECT

private:
	std::unique_ptr<Ui::OBSBasicSourceSelect> ui;
	const char *id;

	static bool EnumSources(void *data, obs_source_t *source);

	static void OBSSourceRemoved(void *data, calldata_t *calldata);
	static void OBSSourceAdded(void *data, calldata_t *calldata);

private slots:
	void on_buttonBox_accepted();
	void on_buttonBox_rejected();

	void SourceAdded(OBSSource source);
	void SourceRemoved(OBSSource source);

public:
	OBSBasicSourceSelect(OBSBasic *parent, const char *id);

	OBSSource newSource;

	static void SourcePaste(const char *name, bool visible, bool duplicate);
};

 

看看这个类的构造函数

OBSBasicSourceSelect::OBSBasicSourceSelect(OBSBasic *parent, const char *id_)
	: QDialog (parent),
	  ui      (new Ui::OBSBasicSourceSelect),
	  id      (id_)
{
	ui->setupUi(this);

	ui->sourceList->setAttribute(Qt::WA_MacShowFocusRect, false);

	QString placeHolderText{QT_UTF8(GetSourceDisplayName(id))};

	QString text{placeHolderText};
	int i = 2;
	obs_source_t *source = nullptr;
	while ((source = obs_get_source_by_name(QT_TO_UTF8(text)))) {
		obs_source_release(source);
		text = QString("%1 %2").arg(placeHolderText).arg(i++);
	}

	ui->sourceName->setText(text);
	ui->sourceName->setFocus();	//Fixes deselect of text.
	ui->sourceName->selectAll();

	installEventFilter(CreateShortcutFilter());

	if (strcmp(id_, "scene") == 0) {
		OBSBasic *main = reinterpret_cast<OBSBasic*>(
				App()->GetMainWindow());
		OBSSource curSceneSource = main->GetCurrentSceneSource();

		ui->selectExisting->setChecked(true);
		ui->createNew->setChecked(false);
		ui->createNew->setEnabled(false);
		ui->sourceName->setEnabled(false);

		int count = main->ui->scenes->count();
		for (int i = 0; i < count; i++) {
			QListWidgetItem *item = main->ui->scenes->item(i);
			OBSScene scene = GetOBSRef<OBSScene>(item);
			OBSSource sceneSource = obs_scene_get_source(scene);

			if (curSceneSource == sceneSource)
				continue;

			const char *name = obs_source_get_name(sceneSource);
			ui->sourceList->addItem(QT_UTF8(name));
		}
	} else {
		obs_enum_sources(EnumSources, this);
	}


	if (g_bMyCustom)
		on_buttonBox_accepted(); //... 默认创建窗口 ,不让窗口显示
}

 

比如,我们选择添加 视频捕获设备时候,

void OBSBasic::AddSource(const char *id)
{
    if (id && *id) {
        OBSBasicSourceSelect sourceSelect(this, id);

 

这里会将视频捕获设备的id dshow_input传递进去,

构造函数中,获得此ID对应的名字 

QString placeHolderText{QT_UTF8(GetSourceDisplayName(id))};

static inline const char *GetSourceDisplayName(const char *id)
{
	if (strcmp(id, "scene") == 0)
		return Str("Basic.Scene");
	return obs_source_get_display_name(id);
}

比如,dshow_input对应的名字为  视频捕获设备

然后,根据名字,获得源,进行遍历

source = obs_get_source_by_name(QT_TO_UTF8(text)

    while ((source = obs_get_source_by_name(QT_TO_UTF8(text)))) {
        obs_source_release(source);
        text = QString("%1 %2").arg(placeHolderText).arg(i++);
    }

 

 

OBS 源的菜单创建 及添加源的处理

 

源选择窗口中,会枚举以存在的同类型源名称

这是通过,obs_enum_sources(EnumSources, this); 实现的

/**
 * Enumerates all input sources
 *
 *   Callback function returns true to continue enumeration, or false to end
 * enumeration.
 *
 *   Use obs_source_get_ref or obs_source_get_weak_source if you want to retain
 * a reference after obs_enum_sources finishes
 */
EXPORT void obs_enum_sources(bool (*enum_proc)(void*, obs_source_t*),
		void *param);

 

bool OBSBasicSourceSelect::EnumSources(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)
		window->ui->sourceList->addItem(QT_UTF8(name));

	return true;
}

 

OBS 源的菜单创建 及添加源的处理

 

 

buttonBox使用的默认的槽函数

private slots:
	void on_buttonBox_accepted();
	void on_buttonBox_rejected();

	void SourceAdded(OBSSource source);
	void SourceRemoved(OBSSource source);

 

 


void OBSBasicSourceSelect::on_buttonBox_accepted()
{
	bool useExisting = ui->selectExisting->isChecked();
	bool visible = ui->sourceVisible->isChecked();

	if (useExisting) {
		QListWidgetItem *item = ui->sourceList->currentItem();
		if (!item)
			return;

		AddExisting(QT_TO_UTF8(item->text()), visible, false);
	} else {
		if (ui->sourceName->text().isEmpty()) {
			OBSMessageBox::information(this,
					QTStr("NoNameEntered.Title"),
					QTStr("NoNameEntered.Text"));
			return;
		}

		if (!AddNew(this, id, QT_TO_UTF8(ui->sourceName->text()),
					visible, newSource))
			return;
	}

	done(DialogCode::Accepted);
}

 

看看 加入已经存在这个名字的 源 的方法,是如何处理的

static void AddExisting(const char *name, bool visible, bool duplicate)
{
	OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
	OBSScene scene = main->GetCurrentScene();
	if (!scene)
		return;

	obs_source_t *source = obs_get_source_by_name(name);
	if (source) {
		if (duplicate) {
			obs_source_t *from = source;
			char *new_name = get_new_source_name(name);
			source = obs_source_duplicate(from, new_name, false);
			bfree(new_name);
			obs_source_release(from);

			if (!source)
				return;
		}

		AddSourceData data;
		data.source = source;
		data.visible = visible;
		obs_scene_atomic_update(scene, AddSource, &data);

		obs_source_release(source);
	}
}

看看 创建新的源是如何处理的

bool AddNew(QWidget *parent, const char *id, const char *name,
		const bool visible, OBSSource &newSource)
{
	OBSBasic     *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
	OBSScene     scene = main->GetCurrentScene();
	bool         success = false;
	if (!scene)
		return false;

	obs_source_t *source = obs_get_source_by_name(name);
	if (source) {
		OBSMessageBox::information(parent,
				QTStr("NameExists.Title"),
				QTStr("NameExists.Text"));

	} else {
		source = obs_source_create(id, name, NULL, nullptr);  //创建新源1 

		if (source) {
			AddSourceData data;
			data.source = source;
			data.visible = visible;
			obs_scene_atomic_update(scene, AddSource, &data);

			newSource = source;

			success = true;
		}
	}

	obs_source_release(source);
	return success;
}

 

这里使用了


/**
 * Creates a source of the specified type with the specified settings.
 *
 *   The "source" context is used for anything related to presenting
 * or modifying video/audio.  Use obs_source_release to release it.
 */
EXPORT obs_source_t *obs_source_create(const char *id, const char *name,
		obs_data_t *settings, obs_data_t *hotkey_data);

source = obs_source_create(id, name, NULL, nullptr);  //创建初始新源   类型为id  名字name 其它默认

然后,将这个源添加到 AddSourceData 结构体中

struct AddSourceData {
    obs_source_t *source;
    bool visible;
};

 

通过  obs_scene_atomic_update  来更新场景

typedef void (*obs_scene_atomic_update_func)(void *, obs_scene_t *scene);
EXPORT void obs_scene_atomic_update(obs_scene_t *scene,
		obs_scene_atomic_update_func func, void *data);

 


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);
}
/** Adds/creates a new scene item for a source */
EXPORT obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source);

使用obs_scene_add往场景中添加一个新的场景项item

使用obs_sceneitem_set_visible 设置该项是否可见

这样通过 OBSBasicSourceSelect 对话框,就往场景中添加了一个新源

 

三 属性窗口 设置源的属性

我们回看下,OBSBasic添加源的部分

void OBSBasic::AddSource(const char *id)

	    	OBSBasicSourceSelect sourceSelect(this, id);


			sourceSelect.exec();
			if (sourceSelect.newSource)
			{
				CreatePropertiesWindow(sourceSelect.newSource, true);  //创建属性窗口
			}

 

OBS 源的菜单创建 及添加源的处理

 

class OBSPropertiesView;
class OBSBasic;

class OBSBasicProperties : public QDialog {
	Q_OBJECT

private:
	QPointer<OBSQTDisplay> preview;

	OBSBasic   *main;
	bool       acceptClicked;

	OBSSource  source;
	OBSSignal  removedSignal;
	OBSSignal  renamedSignal;
	OBSSignal  updatePropertiesSignal;
	OBSData    oldSettings;
	OBSPropertiesView *view;
	QDialogButtonBox *buttonBox;
	QSplitter *windowSplitter;

	static void SourceRemoved(void *data, calldata_t *params);
	static void SourceRenamed(void *data, calldata_t *params);
	static void UpdateProperties(void *data, calldata_t *params);
	static void DrawPreview(void *data, uint32_t cx, uint32_t cy);
	bool ConfirmQuit();
	int  CheckSettings();
	void Cleanup();

private slots:
	void on_buttonBox_clicked(QAbstractButton *button);

public:
	OBSBasicProperties(QWidget *parent, OBSSource source_);
	~OBSBasicProperties();

	void Init();

protected:
	virtual void closeEvent(QCloseEvent *event) override;
	virtual void reject() override;
};

 

这个属性窗口不是UI文件生成的,而是通过代码动态生成的

OBSBasicProperties::OBSBasicProperties(QWidget *parent, OBSSource source_)
	: QDialog                (parent),
	  preview                (new OBSQTDisplay(this)),
	  main                   (qobject_cast<OBSBasic*>(parent)),
	  acceptClicked          (false),
	  source                 (source_),
	  removedSignal          (obs_source_get_signal_handler(source),
	                          "remove", OBSBasicProperties::SourceRemoved,
	                          this),
	  renamedSignal          (obs_source_get_signal_handler(source),
	                          "rename", OBSBasicProperties::SourceRenamed,
	                          this),
	  oldSettings            (obs_data_create()),
	  buttonBox              (new QDialogButtonBox(this))
{
	int cx = (int)config_get_int(App()->GlobalConfig(), "PropertiesWindow",
			"cx");
	int cy = (int)config_get_int(App()->GlobalConfig(), "PropertiesWindow",
			"cy");

	QPushButton *b;
	b = buttonBox->addButton(QTStr("OK"), QDialogButtonBox::AcceptRole);
	buttonBox->addButton(QTStr("Cancel"), QDialogButtonBox::RejectRole);
	buttonBox->addButton(QTStr("Defaults"), QDialogButtonBox::ResetRole);
	buttonBox->setObjectName(QStringLiteral("buttonBox"));

	b->setDefault(true);

	if (cx > 400 && cy > 400)
		resize(cx, cy);
	else
		resize(720, 580);

	QMetaObject::connectSlotsByName(this);

	/* The OBSData constructor increments the reference once */
	obs_data_release(oldSettings);

	OBSData settings = obs_source_get_settings(source);
	obs_data_apply(oldSettings, settings);
	obs_data_release(settings);

	view = new OBSPropertiesView(settings, source,
			(PropertiesReloadCallback)obs_source_properties,
			(PropertiesUpdateCallback)obs_source_update);
	view->setMinimumHeight(150);

	preview->setMinimumSize(20, 150);
	preview->setSizePolicy(QSizePolicy(QSizePolicy::Expanding,
				QSizePolicy::Expanding));

	// Create a QSplitter to keep a unified workflow here.
	windowSplitter = new QSplitter(Qt::Orientation::Vertical, this);
	windowSplitter->addWidget(preview);
	windowSplitter->addWidget(view);
	windowSplitter->setChildrenCollapsible(true);
	//windowSplitter->setSizes(QList<int>({ 16777216, 150 }));
	windowSplitter->setStretchFactor(0, 3);
	windowSplitter->setStretchFactor(1, 1);

	setLayout(new QVBoxLayout(this));
	layout()->addWidget(windowSplitter);
	layout()->addWidget(buttonBox);
	layout()->setAlignment(buttonBox, Qt::AlignRight | Qt::AlignBottom);

	view->show();
	installEventFilter(CreateShortcutFilter());

	const char *name = obs_source_get_name(source);
	setWindowTitle(QTStr("Basic.PropertiesWindow").arg(QT_UTF8(name)));

	obs_source_inc_showing(source);

	updatePropertiesSignal.Connect(obs_source_get_signal_handler(source),
			"update_properties",
			OBSBasicProperties::UpdateProperties,
			this);

	auto addDrawCallback = [this] ()
	{
		obs_display_add_draw_callback(preview->GetDisplay(),
				OBSBasicProperties::DrawPreview, this);
	};

	enum obs_source_type type = obs_source_get_type(source);
	uint32_t caps = obs_source_get_output_flags(source);
	bool drawable_type = type == OBS_SOURCE_TYPE_INPUT ||
		type == OBS_SOURCE_TYPE_SCENE;

	if (drawable_type && (caps & OBS_SOURCE_VIDEO) != 0)
		connect(preview.data(), &OBSQTDisplay::DisplayCreated,
				addDrawCallback);
}

分析下这些动态窗口生成过程

 


1)按钮组   

 int cx = (int)config_get_int(App()->GlobalConfig(), "PropertiesWindow",
            "cx");
    int cy = (int)config_get_int(App()->GlobalConfig(), "PropertiesWindow",
            "cy");

    QPushButton *b;
    b = buttonBox->addButton(QTStr("OK"), QDialogButtonBox::AcceptRole);
    buttonBox->addButton(QTStr("Cancel"), QDialogButtonBox::RejectRole);
    buttonBox->addButton(QTStr("Defaults"), QDialogButtonBox::ResetRole);
    buttonBox->setObjectName(QStringLiteral("buttonBox"));

    b->setDefault(true);

 

OBS 源的菜单创建 及添加源的处理

QMetaObject::connectSlotsByName(this);

connectSlotsByName给控件建立缺省的信号和槽的连接

 

2)源settings

   初始化列表中,生成一个默认settings

   oldSettings            (obs_data_create()),

  获得源的设置,更新此值

    obs_data_release(oldSettings);

    OBSData settings = obs_source_get_settings(source);
    obs_data_apply(oldSettings, settings);
    obs_data_release(settings);

 

3) 创建属性视图

    view = new OBSPropertiesView(settings, source,
            (PropertiesReloadCallback)obs_source_properties,
            (PropertiesUpdateCallback)obs_source_update);
    view->setMinimumHeight(150);

 

   OBS 源的菜单创建 及添加源的处理

 

   


class VScrollArea : public QScrollArea {
	Q_OBJECT

public:
	inline VScrollArea(QWidget *parent = nullptr)
		: QScrollArea(parent)
	{
		setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
	}

protected:
	virtual void resizeEvent(QResizeEvent *event) override;
};



class OBSPropertiesView : public VScrollArea {
	Q_OBJECT

	friend class WidgetInfo;

	using properties_delete_t = decltype(&obs_properties_destroy);
	using properties_t =
		std::unique_ptr<obs_properties_t, properties_delete_t>;

private:
	QWidget                                  *widget = nullptr;
	properties_t                             properties;
	OBSData                                  settings;
	void                                     *obj = nullptr;
	std::string                              type;
	PropertiesReloadCallback                 reloadCallback;
	PropertiesUpdateCallback                 callback = nullptr;
	int                                      minSize;
	std::vector<std::unique_ptr<WidgetInfo>> children;
	std::string                              lastFocused;
	QWidget                                  *lastWidget = nullptr;
	bool                                     deferUpdate;

	QWidget *NewWidget(obs_property_t *prop, QWidget *widget,
			const char *signal);

	QWidget *AddCheckbox(obs_property_t *prop);
	QWidget *AddText(obs_property_t *prop, QFormLayout *layout,
			QLabel *&label);
	void AddPath(obs_property_t *prop, QFormLayout *layout, QLabel **label);
	void AddInt(obs_property_t *prop, QFormLayout *layout, QLabel **label);
	void AddFloat(obs_property_t *prop, QFormLayout *layout,
			QLabel**label);
	QWidget *AddList(obs_property_t *prop, bool &warning);
	void AddEditableList(obs_property_t *prop, QFormLayout *layout,
			QLabel *&label);
	QWidget *AddButton(obs_property_t *prop);
	void AddColor(obs_property_t *prop, QFormLayout *layout, QLabel *&label);
	void AddFont(obs_property_t *prop, QFormLayout *layout, QLabel *&label);
	void AddFrameRate(obs_property_t *prop, bool &warning,
			QFormLayout *layout, QLabel *&label);

	void AddProperty(obs_property_t *property, QFormLayout *layout);

	void resizeEvent(QResizeEvent *event) override;

	void GetScrollPos(int &h, int &v);
	void SetScrollPos(int h, int v);

public slots:
	void ReloadProperties();
	void RefreshProperties();
	void SignalChanged();

signals:
	void PropertiesResized();
	void Changed();

public:
	OBSPropertiesView(OBSData settings, void *obj,
			PropertiesReloadCallback reloadCallback,
			PropertiesUpdateCallback callback,
			int minSize = 0);
	OBSPropertiesView(OBSData settings, const char *type,
			PropertiesReloadCallback reloadCallback,
			int minSize = 0);

	inline obs_data_t *GetSettings() const {return settings;}

	inline void UpdateSettings() {callback(obj, settings);}
	inline bool DeferUpdate() const {return deferUpdate;}
};

 

  由属性视图的定义可见,属性视图是一个可垂直滚动的区域

 

 

 

4)预览窗口

  初始化列表中 

 preview                (new OBSQTDisplay(this)),

 

    preview->setMinimumSize(20, 150);
    preview->setSizePolicy(QSizePolicy(QSizePolicy::Expanding,
                QSizePolicy::Expanding));

 

看看预览视图的定义

class OBSQTDisplay : public QWidget {
	Q_OBJECT

	OBSDisplay display;

	void CreateDisplay();

	void resizeEvent(QResizeEvent *event) override;
	void paintEvent(QPaintEvent *event) override;

signals:
	void DisplayCreated(OBSQTDisplay *window);
	void DisplayResized();

public:
	OBSQTDisplay(QWidget *parent = 0, Qt::WindowFlags flags = 0);

	virtual QPaintEngine *paintEngine() const override;

	inline obs_display_t *GetDisplay() const {return display;}





};


//...................................................................
OBSQTDisplay::OBSQTDisplay(QWidget *parent, Qt::WindowFlags flags)
	: QWidget(parent, flags)
{
	setAttribute(Qt::WA_PaintOnScreen);
	setAttribute(Qt::WA_StaticContents);
	setAttribute(Qt::WA_NoSystemBackground);
	setAttribute(Qt::WA_OpaquePaintEvent);
	setAttribute(Qt::WA_DontCreateNativeAncestors);
	setAttribute(Qt::WA_NativeWindow);

	auto windowVisible = [this] (bool visible)
	{
		if (!visible)
			return;

		if (!display) {
			CreateDisplay();
		} else {
			QSize size = GetPixelSize(this);
			obs_display_resize(display, size.width(), size.height());
		}
	};

	auto sizeChanged = [this] (QScreen*)
	{
		CreateDisplay();

		QSize size = GetPixelSize(this);
		obs_display_resize(display, size.width(), size.height());
	};

	connect(windowHandle(), &QWindow::visibleChanged, windowVisible);
	connect(windowHandle(), &QWindow::screenChanged, sizeChanged);


}




void OBSQTDisplay::CreateDisplay()
{
	if (display || !windowHandle()->isExposed())
		return;

	QSize size = GetPixelSize(this);

	gs_init_data info      = {};
	info.cx                = size.width();
	info.cy                = size.height();
	info.format            = GS_RGBA;
	info.zsformat          = GS_ZS_NONE;

	QTToGSWindow(winId(), info.window);

	display = obs_display_create(&info);

	emit DisplayCreated(this);
}

void OBSQTDisplay::resizeEvent(QResizeEvent *event)
{
	QWidget::resizeEvent(event);

	CreateDisplay();

	if (isVisible() && display) {
		QSize size = GetPixelSize(this);
		obs_display_resize(display, size.width(), size.height());
	}

	emit DisplayResized();
}

void OBSQTDisplay::paintEvent(QPaintEvent *event)
{
	CreateDisplay();

	QWidget::paintEvent(event);
}

QPaintEngine *OBSQTDisplay::paintEngine() const
{
	return nullptr;
}

 

值得注意的是: obs场景中的视图,也是以此为基类的

 

OBS 源的菜单创建 及添加源的处理

 

OBS 源的菜单创建 及添加源的处理

 

 

5)将预览视图和属性视图加入分离器中

    // Create a QSplitter to keep a unified workflow here.
    windowSplitter = new QSplitter(Qt::Orientation::Vertical, this);
    windowSplitter->addWidget(preview);
    windowSplitter->addWidget(view);
    windowSplitter->setChildrenCollapsible(true);
    //windowSplitter->setSizes(QList<int>({ 16777216, 150 }));
    windowSplitter->setStretchFactor(0, 3);
    windowSplitter->setStretchFactor(1, 1);

    setLayout(new QVBoxLayout(this));
    layout()->addWidget(windowSplitter);
    layout()->addWidget(buttonBox);
    layout()->setAlignment(buttonBox, Qt::AlignRight | Qt::AlignBottom);

    view->show();

 

 

    const char *name = obs_source_get_name(source);
    setWindowTitle(QTStr("Basic.PropertiesWindow").arg(QT_UTF8(name)));

 

/**
 * Increments the 'showing' reference counter to indicate that the source is
 * being shown somewhere.  If the reference counter was 0, will call the 'show'
 * callback.
 */

    obs_source_inc_showing(source);

 

6)信号槽

OBS定义了一个信号槽类

/* 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;
	}
};

 

在属性窗口中,定义了信号类成员变量

OBSSignal  updatePropertiesSignal;

 

/** Returns the signal handler for a source */
EXPORT signal_handler_t *obs_source_get_signal_handler(
        const obs_source_t *source);

 

    updatePropertiesSignal.Connect(obs_source_get_signal_handler(source),
            "update_properties",
            OBSBasicProperties::UpdateProperties,
            this);

 

将 属性窗口的更新属性信号,对应到属性视图VIEW的ReloadProperties 重加载属性这

void OBSBasicProperties::UpdateProperties(void *data, calldata_t *)
{
	QMetaObject::invokeMethod(static_cast<OBSBasicProperties*>(data)->view,
			"ReloadProperties");
}


void OBSPropertiesView::ReloadProperties()
{
	if (obj) {
		properties.reset(reloadCallback(obj));
	} else {
		properties.reset(reloadCallback((void*)type.c_str()));
		obs_properties_apply_settings(properties.get(), settings);
	}

	uint32_t flags = obs_properties_get_flags(properties.get());
	deferUpdate = (flags & OBS_PROPERTIES_DEFER_UPDATE) != 0;

	RefreshProperties();
}


void OBSPropertiesView::RefreshProperties()
{
	int h, v;
	GetScrollPos(h, v);

	children.clear();
	if (widget)
		widget->deleteLater();

	widget = new QWidget();

	QFormLayout *layout = new QFormLayout;
	layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
	widget->setLayout(layout);

	QSizePolicy mainPolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
	QSizePolicy policy(QSizePolicy::Preferred, QSizePolicy::Preferred);
	//widget->setSizePolicy(policy);

	layout->setLabelAlignment(Qt::AlignRight);

	obs_property_t *property = obs_properties_first(properties.get());
	bool hasNoProperties = !property;

	while (property) {
		AddProperty(property, layout);
		obs_property_next(&property);
	}

	setWidgetResizable(true);
	setWidget(widget);
	SetScrollPos(h, v);
	setSizePolicy(mainPolicy);

	lastFocused.clear();
	if (lastWidget) {
		lastWidget->setFocus(Qt::OtherFocusReason);
		lastWidget = nullptr;
	}

	if (hasNoProperties) {
		QLabel *noPropertiesLabel = new QLabel(NO_PROPERTIES_STRING);
		layout->addWidget(noPropertiesLabel);
	}
}

 

 


四 自定义各种源的添加处理

 

void OBSBasic::AddSource(const char *id)
 

比如不必显示选择窗口,不必显示属性窗口

根据视频还是显示器等分别处理等。

 

 

 

 

 

 

相关标签: obs