#include "window-basic-vcam-config.hpp" #include "window-basic-main.hpp" #include "qt-wrappers.hpp" #include #include enum class VCamOutputType { Internal, Scene, Source, }; enum class VCamInternalType { Default, Preview, }; struct VCamConfig { VCamOutputType type = VCamOutputType::Internal; VCamInternalType internal = VCamInternalType::Default; std::string scene; std::string source; }; static VCamConfig *vCamConfig = nullptr; OBSBasicVCamConfig::OBSBasicVCamConfig(QWidget *parent) : QDialog(parent), ui(new Ui::OBSBasicVCamConfig) { setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); ui->setupUi(this); auto type = (int)vCamConfig->type; ui->outputType->setCurrentIndex(type); OutputTypeChanged(type); connect(ui->outputType, static_cast( &QComboBox::currentIndexChanged), this, &OBSBasicVCamConfig::OutputTypeChanged); connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &OBSBasicVCamConfig::Save); } void OBSBasicVCamConfig::OutputTypeChanged(int type) { auto list = ui->outputSelection; list->clear(); switch ((VCamOutputType)type) { case VCamOutputType::Internal: list->addItem(QTStr("Basic.VCam.InternalDefault")); list->addItem(QTStr("Basic.VCam.InternalPreview")); list->setCurrentIndex((int)vCamConfig->internal); break; case VCamOutputType::Scene: { // Scenes in default order BPtr scenes = obs_frontend_get_scene_names(); int idx = 0; for (char **temp = scenes; *temp; temp++) { list->addItem(*temp); if (vCamConfig->scene.compare(*temp) == 0) list->setCurrentIndex(list->count() - 1); } break; } case VCamOutputType::Source: { // Sources in alphabetical order std::vector sources; auto AddSource = [&](obs_source_t *source) { auto name = obs_source_get_name(source); auto flags = obs_source_get_output_flags(source); if (!(obs_source_get_output_flags(source) & OBS_SOURCE_VIDEO)) return; sources.push_back(name); }; using AddSource_t = decltype(AddSource); obs_enum_sources( [](void *data, obs_source_t *source) { auto &AddSource = *static_cast(data); if (!obs_source_removed(source)) AddSource(source); return true; }, static_cast(&AddSource)); // Sort and select current item sort(sources.begin(), sources.end()); for (auto &&source : sources) { list->addItem(source.c_str()); if (vCamConfig->source == source) list->setCurrentIndex(list->count() - 1); } break; } } } void OBSBasicVCamConfig::Save() { auto type = (VCamOutputType)ui->outputType->currentIndex(); auto out = ui->outputSelection; switch (type) { case VCamOutputType::Internal: vCamConfig->internal = (VCamInternalType)out->currentIndex(); break; case VCamOutputType::Scene: vCamConfig->scene = out->currentText().toStdString(); break; case VCamOutputType::Source: vCamConfig->source = out->currentText().toStdString(); break; default: // unknown value, don't save type return; } vCamConfig->type = type; // If already running just update the source if (obs_frontend_virtualcam_active()) UpdateOutputSource(); } void OBSBasicVCamConfig::SaveData(obs_data_t *data, bool saving) { if (saving) { OBSDataAutoRelease obj = obs_data_create(); obs_data_set_int(obj, "type", (int)vCamConfig->type); obs_data_set_int(obj, "internal", (int)vCamConfig->internal); obs_data_set_string(obj, "scene", vCamConfig->scene.c_str()); obs_data_set_string(obj, "source", vCamConfig->source.c_str()); obs_data_set_obj(data, "virtual-camera", obj); } else { OBSDataAutoRelease obj = obs_data_get_obj(data, "virtual-camera"); vCamConfig->type = (VCamOutputType)obs_data_get_int(obj, "type"); vCamConfig->internal = (VCamInternalType)obs_data_get_int(obj, "internal"); vCamConfig->scene = obs_data_get_string(obj, "scene"); vCamConfig->source = obs_data_get_string(obj, "source"); } } static void EventCallback(enum obs_frontend_event event, void *) { if (vCamConfig->type != VCamOutputType::Internal) return; // Update output source if the preview scene changes // or if the default transition is changed switch (event) { case OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED: if (vCamConfig->internal != VCamInternalType::Preview) return; break; case OBS_FRONTEND_EVENT_TRANSITION_CHANGED: if (vCamConfig->internal != VCamInternalType::Default) return; break; default: return; } OBSBasicVCamConfig::UpdateOutputSource(); } static auto staticConfig = VCamConfig{}; void OBSBasicVCamConfig::Init() { if (vCamConfig) return; vCamConfig = &staticConfig; obs_frontend_add_event_callback(EventCallback, nullptr); } static obs_view_t *view = nullptr; static video_t *video = nullptr; video_t *OBSBasicVCamConfig::StartVideo() { if (!view) view = obs_view_create(); UpdateOutputSource(); if (!video) video = obs_view_add(view); return video; } void OBSBasicVCamConfig::StopVideo() { obs_view_remove(view); obs_view_set_source(view, 0, nullptr); video = nullptr; } void OBSBasicVCamConfig::DestroyView() { StopVideo(); obs_view_destroy(view); view = nullptr; } void OBSBasicVCamConfig::UpdateOutputSource() { if (!view) return; obs_source_t *source = nullptr; switch ((VCamOutputType)vCamConfig->type) { case VCamOutputType::Internal: switch (vCamConfig->internal) { case VCamInternalType::Default: source = obs_get_output_source(0); break; case VCamInternalType::Preview: OBSSource s = OBSBasic::Get()->GetCurrentSceneSource(); obs_source_get_ref(s); source = s; break; } break; case VCamOutputType::Scene: source = obs_get_source_by_name(vCamConfig->scene.c_str()); break; case VCamOutputType::Source: source = obs_get_source_by_name(vCamConfig->source.c_str()); break; } auto current = obs_view_get_source(view, 0); if (source != current) obs_view_set_source(view, 0, source); obs_source_release(source); obs_source_release(current); }