From 1cbf9421d48f2504ce91dc5d1afa74e042e22b13 Mon Sep 17 00:00:00 2001 From: Paul Hindt Date: Fri, 7 Jan 2022 18:45:08 -0800 Subject: [PATCH] aja-output-ui: Add the Multi View UI options for new device. --- .../aja-output-ui/AJAOutputUI.cpp | 99 +++++++++- .../aja-output-ui/AJAOutputUI.h | 15 +- .../aja-output-ui/CMakeLists.txt | 20 +- .../aja-output-ui/aja-ui-main.cpp | 181 ++++++++++++++++-- .../aja-output-ui/aja-ui-main.h | 14 ++ .../aja-output-ui/data/locale/en-US.ini | 3 + .../aja-output-ui/forms/output.ui | 10 + plugins/aja/aja-common.cpp | 5 + plugins/aja/aja-common.hpp | 2 + 9 files changed, 329 insertions(+), 20 deletions(-) diff --git a/UI/frontend-plugins/aja-output-ui/AJAOutputUI.cpp b/UI/frontend-plugins/aja-output-ui/AJAOutputUI.cpp index 63c6bd2e8..4c2d83653 100644 --- a/UI/frontend-plugins/aja-output-ui/AJAOutputUI.cpp +++ b/UI/frontend-plugins/aja-output-ui/AJAOutputUI.cpp @@ -1,10 +1,15 @@ #include "AJAOutputUI.h" #include "aja-ui-main.h" +#include "../../../plugins/aja/aja-common.hpp" #include "../../../plugins/aja/aja-ui-props.hpp" #include "../../../plugins/aja/aja-enums.hpp" +#include "../../../plugins/aja/aja-card-manager.hpp" +#include +#include #include +#include #include #include @@ -13,19 +18,19 @@ AJAOutputUI::AJAOutputUI(QWidget *parent) : QDialog(parent), ui(new Ui_Output) { ui->setupUi(this); - setSizeGripEnabled(true); - setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); propertiesView = nullptr; previewPropertiesView = nullptr; + miscPropertiesView = nullptr; } void AJAOutputUI::ShowHideDialog() { SetupPropertiesView(); SetupPreviewPropertiesView(); + SetupMiscPropertiesView(); setVisible(!isVisible()); } @@ -36,7 +41,6 @@ void AJAOutputUI::SetupPropertiesView() delete propertiesView; obs_data_t *settings = obs_data_create(); - OBSData data = load_settings(kProgramPropsFilename); if (data) { obs_data_apply(settings, data); @@ -181,3 +185,92 @@ void AJAOutputUI::PreviewOutputStateChanged(bool active) ui->previewOutputButton->setChecked(active); ui->previewOutputButton->setText(text); } + +static obs_properties_t *create_misc_props_ui(void *vp) +{ + AJAOutputUI *outputUI = (AJAOutputUI *)vp; + if (!outputUI) + return nullptr; + aja::CardManager *cardManager = outputUI->GetCardManager(); + if (!cardManager) + return nullptr; + + bool haveMultiView = false; + for (auto &c : *cardManager) { + auto deviceID = c.second->GetDeviceID(); + for (const auto &id : aja::MultiViewCards()) { + if (deviceID == id) { + haveMultiView = true; + break; + } + } + } + + obs_properties_t *props = obs_properties_create(); + obs_property_t *deviceList = obs_properties_add_list( + props, kUIPropDevice.id, obs_module_text(kUIPropDevice.text), + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); + obs_property_t *multiViewEnable = obs_properties_add_bool( + props, kUIPropMultiViewEnable.id, + obs_module_text(kUIPropMultiViewEnable.text)); + obs_property_t *multiViewAudioSources = obs_properties_add_list( + props, kUIPropMultiViewAudioSource.id, + obs_module_text(kUIPropMultiViewAudioSource.text), + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + + obs_property_list_clear(deviceList); + obs_property_list_clear(multiViewAudioSources); + + NTV2DeviceID firstDeviceID = DEVICE_ID_NOTFOUND; + populate_misc_device_list(deviceList, cardManager, firstDeviceID); + populate_multi_view_audio_sources(multiViewAudioSources, firstDeviceID); + obs_property_set_modified_callback2(deviceList, on_misc_device_selected, + cardManager); + obs_property_set_modified_callback2(multiViewEnable, + on_multi_view_toggle, cardManager); + obs_property_set_modified_callback2(multiViewAudioSources, + on_multi_view_toggle, cardManager); + + outputUI->ui->label_3->setVisible(haveMultiView); + obs_property_set_visible(deviceList, haveMultiView); + obs_property_set_visible(multiViewEnable, haveMultiView); + obs_property_set_visible(multiViewAudioSources, haveMultiView); + + return props; +} + +void AJAOutputUI::MiscPropertiesChanged() +{ + SaveSettings(kMiscPropsFilename, miscPropertiesView->GetSettings()); +} + +void AJAOutputUI::SetCardManager(aja::CardManager *cm) +{ + cardManager = cm; +} + +aja::CardManager *AJAOutputUI::GetCardManager() +{ + return cardManager; +} + +void AJAOutputUI::SetupMiscPropertiesView() +{ + if (miscPropertiesView) + delete miscPropertiesView; + + obs_data_t *settings = obs_data_create(); + OBSData data = load_settings(kMiscPropsFilename); + if (data) { + obs_data_apply(settings, data); + } + + miscPropertiesView = new OBSPropertiesView( + settings, this, (PropertiesReloadCallback)create_misc_props_ui, + nullptr, nullptr, 170); + + ui->miscPropertiesLayout->addWidget(miscPropertiesView); + obs_data_release(settings); + connect(miscPropertiesView, SIGNAL(Changed()), this, + SLOT(MiscPropertiesChanged())); +} diff --git a/UI/frontend-plugins/aja-output-ui/AJAOutputUI.h b/UI/frontend-plugins/aja-output-ui/AJAOutputUI.h index 365b38eb8..dfeb2b09f 100644 --- a/UI/frontend-plugins/aja-output-ui/AJAOutputUI.h +++ b/UI/frontend-plugins/aja-output-ui/AJAOutputUI.h @@ -5,12 +5,17 @@ #include "ui_output.h" #include "../../UI/properties-view.hpp" +namespace aja { +class CardManager; +} + class AJAOutputUI : public QDialog { Q_OBJECT private: OBSPropertiesView *propertiesView; OBSPropertiesView *previewPropertiesView; - + OBSPropertiesView *miscPropertiesView; + aja::CardManager *cardManager; public slots: void on_outputButton_clicked(); void PropertiesChanged(); @@ -20,14 +25,18 @@ public slots: void PreviewPropertiesChanged(); void PreviewOutputStateChanged(bool); + void MiscPropertiesChanged(); + public: std::unique_ptr ui; AJAOutputUI(QWidget *parent); + void SetCardManager(aja::CardManager *cm); + aja::CardManager *GetCardManager(); + void ShowHideDialog(); - void SaveSettings(const char *filename, obs_data_t *settings); - void SetupPropertiesView(); void SetupPreviewPropertiesView(); + void SetupMiscPropertiesView(); }; diff --git a/UI/frontend-plugins/aja-output-ui/CMakeLists.txt b/UI/frontend-plugins/aja-output-ui/CMakeLists.txt index 38a280c74..70614a567 100644 --- a/UI/frontend-plugins/aja-output-ui/CMakeLists.txt +++ b/UI/frontend-plugins/aja-output-ui/CMakeLists.txt @@ -44,8 +44,16 @@ set(aja-output-ui_HEADERS ../../spinbox-ignorewheel.hpp AJAOutputUI.h aja-ui-main.h + ../../../plugins/aja/aja-card-manager.hpp + ../../../plugins/aja/aja-common.hpp + ../../../plugins/aja/aja-enums.hpp + ../../../plugins/aja/aja-presets.hpp + ../../../plugins/aja/aja-props.hpp + ../../../plugins/aja/aja-routing.hpp ../../../plugins/aja/aja-ui-props.hpp - ../../../plugins/aja/aja-enums.hpp) + ../../../plugins/aja/aja-vpid-data.hpp + ../../../plugins/aja/aja-widget-io.hpp + ) set(aja-output-ui_SOURCES ${aja-output-ui_SOURCES} @@ -57,7 +65,15 @@ set(aja-output-ui_SOURCES ../../combobox-ignorewheel.cpp ../../spinbox-ignorewheel.cpp AJAOutputUI.cpp - aja-ui-main.cpp) + aja-ui-main.cpp + ../../../plugins/aja/aja-card-manager.cpp + ../../../plugins/aja/aja-common.cpp + ../../../plugins/aja/aja-presets.cpp + ../../../plugins/aja/aja-props.cpp + ../../../plugins/aja/aja-routing.cpp + ../../../plugins/aja/aja-vpid-data.cpp + ../../../plugins/aja/aja-widget-io.cpp + ) set(aja-output-ui_UI ${aja-output-ui_UI} diff --git a/UI/frontend-plugins/aja-output-ui/aja-ui-main.cpp b/UI/frontend-plugins/aja-output-ui/aja-ui-main.cpp index 4f87b9726..c009addd1 100644 --- a/UI/frontend-plugins/aja-output-ui/aja-ui-main.cpp +++ b/UI/frontend-plugins/aja-output-ui/aja-ui-main.cpp @@ -2,6 +2,8 @@ #include "AJAOutputUI.h" #include "../../../plugins/aja/aja-ui-props.hpp" +#include "../../../plugins/aja/aja-card-manager.hpp" +#include "../../../plugins/aja/aja-routing.hpp" #include #include @@ -16,13 +18,13 @@ OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("aja-output-ui", "en-US") -AJAOutputUI *doUI; +static aja::CardManager *cardManager = nullptr; +static AJAOutputUI *ajaOutputUI = nullptr; +static obs_output_t *output = nullptr; bool main_output_running = false; bool preview_output_running = false; -obs_output_t *output; - struct preview_output { bool enabled; obs_source_t *current_source; @@ -60,7 +62,7 @@ void output_stop() obs_output_stop(output); obs_output_release(output); main_output_running = false; - doUI->OutputStateChanged(false); + ajaOutputUI->OutputStateChanged(false); } void output_start() @@ -76,7 +78,7 @@ void output_start() main_output_running = started; - doUI->OutputStateChanged(started); + ajaOutputUI->OutputStateChanged(started); if (!started) output_stop(); @@ -113,7 +115,7 @@ void preview_output_stop() video_output_close(context.video_queue); preview_output_running = false; - doUI->PreviewOutputStateChanged(false); + ajaOutputUI->PreviewOutputStateChanged(false); } void preview_output_start() @@ -169,7 +171,7 @@ void preview_output_start() obs_data_release(settings); preview_output_running = started; - doUI->PreviewOutputStateChanged(started); + ajaOutputUI->PreviewOutputStateChanged(started); if (!started) preview_output_stop(); @@ -184,6 +186,142 @@ void preview_output_toggle() preview_output_start(); } +void populate_misc_device_list(obs_property_t *list, + aja::CardManager *cardManager, + NTV2DeviceID &firstDeviceID) +{ + for (const auto &iter : *cardManager) { + if (!iter.second) + continue; + if (firstDeviceID == DEVICE_ID_NOTFOUND) + firstDeviceID = iter.second->GetDeviceID(); + obs_property_list_add_string( + list, iter.second->GetDisplayName().c_str(), + iter.second->GetCardID().c_str()); + } +} + +void populate_multi_view_audio_sources(obs_property_t *list, NTV2DeviceID id) +{ + obs_property_list_clear(list); + const QList kMultiViewAudioInputs = { + NTV2_INPUTSOURCE_SDI1, + NTV2_INPUTSOURCE_SDI2, + NTV2_INPUTSOURCE_SDI3, + NTV2_INPUTSOURCE_SDI4, + }; + for (const auto &inp : kMultiViewAudioInputs) { + if (NTV2DeviceCanDoInputSource(id, inp)) { + std::string inputSourceStr = + NTV2InputSourceToString(inp, true); + obs_property_list_add_int(list, inputSourceStr.c_str(), + (long long)inp); + } + } +} + +bool on_misc_device_selected(void *data, obs_properties_t *props, + obs_property_t *list, obs_data_t *settings) +{ + const char *cardID = obs_data_get_string(settings, kUIPropDevice.id); + if (!cardID) + return false; + aja::CardManager *cardManager = (aja::CardManager *)data; + if (!cardManager) + return false; + auto cardEntry = cardManager->GetCardEntry(cardID); + if (!cardEntry) + return false; + + NTV2DeviceID deviceID = cardEntry->GetDeviceID(); + bool enableMultiViewUI = NTV2DeviceCanDoHDMIMultiView(deviceID); + obs_property_t *multiViewCheckbox = + obs_properties_get(props, kUIPropMultiViewEnable.id); + obs_property_t *multiViewAudioSource = + obs_properties_get(props, kUIPropMultiViewAudioSource.id); + populate_multi_view_audio_sources(multiViewAudioSource, deviceID); + obs_property_set_enabled(multiViewCheckbox, enableMultiViewUI); + obs_property_set_enabled(multiViewAudioSource, enableMultiViewUI); + return true; +} + +static void toggle_multi_view(CNTV2Card *card, NTV2InputSource src, bool enable) +{ + std::ostringstream oss; + for (int i = 0; i < 4; i++) { + std::string datastream = std::to_string(i); + oss << "sdi[" << datastream << "][0]->hdmi[0][" << datastream + << "];"; + } + + NTV2DeviceID deviceId = card->GetDeviceID(); + NTV2AudioSystem audioSys = NTV2InputSourceToAudioSystem(src); + if (NTV2DeviceCanDoHDMIMultiView(deviceId)) { + NTV2XptConnections cnx; + if (aja::Routing::ParseRouteString(oss.str(), cnx)) { + card->SetMultiRasterBypassEnable(!enable); + if (enable) { + card->ApplySignalRoute(cnx, false); + if (NTV2DeviceCanDoAudioMixer(deviceId)) { + card->SetAudioMixerInputAudioSystem( + NTV2_AudioMixerInputMain, + audioSys); + card->SetAudioMixerInputChannelSelect( + NTV2_AudioMixerInputMain, + NTV2_AudioChannel1_2); + card->SetAudioMixerInputChannelsMute( + NTV2_AudioMixerInputAux1, + NTV2AudioChannelsMuteAll); + card->SetAudioMixerInputChannelsMute( + NTV2_AudioMixerInputAux2, + NTV2AudioChannelsMuteAll); + } + card->SetAudioLoopBack(NTV2_AUDIO_LOOPBACK_ON, + audioSys); + card->SetAudioOutputMonitorSource( + NTV2_AudioChannel1_2, audioSys); + card->SetHDMIOutAudioChannels( + NTV2_HDMIAudio8Channels); + card->SetHDMIOutAudioSource2Channel( + NTV2_AudioChannel1_2, audioSys); + card->SetHDMIOutAudioSource8Channel( + NTV2_AudioChannel1_8, audioSys); + } else { + card->RemoveConnections(cnx); + } + } + } +} + +bool on_multi_view_toggle(void *data, obs_properties_t *props, + obs_property_t *list, obs_data_t *settings) +{ + UNUSED_PARAMETER(props); + UNUSED_PARAMETER(list); + + bool multiViewEnabled = + obs_data_get_bool(settings, kUIPropMultiViewEnable.id) && + !main_output_running && !preview_output_running; + const int audioInputSource = + obs_data_get_int(settings, kUIPropMultiViewAudioSource.id); + const char *cardID = obs_data_get_string(settings, kUIPropDevice.id); + if (!cardID) + return false; + aja::CardManager *cardManager = (aja::CardManager *)data; + if (!cardManager) + return false; + CNTV2Card *card = cardManager->GetCard(cardID); + if (!card) + return false; + + NTV2InputSource inputSource = (NTV2InputSource)audioInputSource; + toggle_multi_view(card, inputSource, multiViewEnabled); + + return true; +} + +// MISC PROPS + void on_preview_scene_changed(enum obs_frontend_event event, void *param) { auto ctx = (struct preview_output *)param; @@ -278,10 +416,10 @@ void addOutputUI(void) QMainWindow *window = (QMainWindow *)obs_frontend_get_main_window(); obs_frontend_push_ui_translation(obs_module_get_string); - doUI = new AJAOutputUI(window); + ajaOutputUI = new AJAOutputUI(window); obs_frontend_pop_ui_translation(); - auto cb = []() { doUI->ShowHideDialog(); }; + auto cb = []() { ajaOutputUI->ShowHideDialog(); }; action->connect(action, &QAction::triggered, cb); } @@ -290,17 +428,21 @@ static void OBSEvent(enum obs_frontend_event event, void *) { if (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) { OBSData settings = load_settings(kProgramPropsFilename); - if (settings && obs_data_get_bool(settings, kUIPropAutoStartOutput.id)) output_start(); OBSData previewSettings = load_settings(kPreviewPropsFilename); - if (previewSettings && obs_data_get_bool(previewSettings, kUIPropAutoStartOutput.id)) preview_output_start(); + + OBSData miscSettings = load_settings(kMiscPropsFilename); + if (miscSettings && ajaOutputUI) { + on_multi_view_toggle(ajaOutputUI->GetCardManager(), + nullptr, nullptr, miscSettings); + } } else if (event == OBS_FRONTEND_EVENT_EXIT) { if (main_output_running) output_stop(); @@ -309,15 +451,30 @@ static void OBSEvent(enum obs_frontend_event event, void *) } } +static void aja_loaded(void *data, calldata_t *calldata) +{ + // Receive CardManager pointer from the main AJA plugin + calldata_get_ptr(calldata, "card_manager", &cardManager); + if (ajaOutputUI) + ajaOutputUI->SetCardManager(cardManager); +} + bool obs_module_load(void) { CNTV2DeviceScanner scanner; auto numDevices = scanner.GetNumDevices(); - if (numDevices == 0) { + blog(LOG_WARNING, + "No AJA devices found, skipping loading AJA UI plugin"); return false; } + // Signal to wait for AJA plugin to finish loading so we can access the CardManager instance + auto signal_handler = obs_get_signal_handler(); + signal_handler_add(signal_handler, "void aja_loaded(ptr card_manager)"); + signal_handler_connect(signal_handler, "aja_loaded", aja_loaded, + nullptr); + addOutputUI(); obs_frontend_add_event_callback(OBSEvent, nullptr); diff --git a/UI/frontend-plugins/aja-output-ui/aja-ui-main.h b/UI/frontend-plugins/aja-output-ui/aja-ui-main.h index 295635ed8..2cc78e57e 100644 --- a/UI/frontend-plugins/aja-output-ui/aja-ui-main.h +++ b/UI/frontend-plugins/aja-output-ui/aja-ui-main.h @@ -2,9 +2,23 @@ #include +#include +namespace aja { +class CardManager; +} + static const char *kProgramPropsFilename = "ajaOutputProps.json"; static const char *kPreviewPropsFilename = "ajaPreviewOutputProps.json"; +static const char *kMiscPropsFilename = "ajaMiscProps.json"; OBSData load_settings(const char *filename); void output_toggle(); void preview_output_toggle(); +void populate_misc_device_list(obs_property_t *list, + aja::CardManager *cardManager, + NTV2DeviceID &firstDeviceID); +void populate_multi_view_audio_sources(obs_property_t *list, NTV2DeviceID id); +bool on_misc_device_selected(void *data, obs_properties_t *props, + obs_property_t *list, obs_data_t *settings); +bool on_multi_view_toggle(void *data, obs_properties_t *props, + obs_property_t *list, obs_data_t *settings); diff --git a/UI/frontend-plugins/aja-output-ui/data/locale/en-US.ini b/UI/frontend-plugins/aja-output-ui/data/locale/en-US.ini index 92686c3a3..12195e108 100644 --- a/UI/frontend-plugins/aja-output-ui/data/locale/en-US.ini +++ b/UI/frontend-plugins/aja-output-ui/data/locale/en-US.ini @@ -1,3 +1,6 @@ AJAOutput.Device="AJA I/O Device Output" AJAOutput.ProgramOutput="Program Output" AJAOutput.PreviewOutput="Preview Output" +AJAOutput.MiscOutput="Additional Settings" +AJAOutput.MultiViewEnable="Enable Multi View" +AJAOutput.MultiViewAudioSource="Multi View Audio Source" diff --git a/UI/frontend-plugins/aja-output-ui/forms/output.ui b/UI/frontend-plugins/aja-output-ui/forms/output.ui index 364ebc3a3..b119be50c 100644 --- a/UI/frontend-plugins/aja-output-ui/forms/output.ui +++ b/UI/frontend-plugins/aja-output-ui/forms/output.ui @@ -106,6 +106,16 @@ + + + + AJAOutput.MiscOutput + + + + + + diff --git a/plugins/aja/aja-common.cpp b/plugins/aja/aja-common.cpp index 4adb3a470..f13d876f4 100644 --- a/plugins/aja/aja-common.cpp +++ b/plugins/aja/aja-common.cpp @@ -1135,4 +1135,9 @@ VPIDStandard DetermineVPIDStandard(NTV2DeviceID id, IOSelection io, return vpid; } +std::vector MultiViewCards() +{ + return {DEVICE_ID_IOX3}; +} + } // aja diff --git a/plugins/aja/aja-common.hpp b/plugins/aja/aja-common.hpp index 67d06d0b3..f2d931a4b 100644 --- a/plugins/aja/aja-common.hpp +++ b/plugins/aja/aja-common.hpp @@ -98,4 +98,6 @@ extern VPIDStandard DetermineVPIDStandard(NTV2DeviceID id, IOSelection io, NTV2VideoFormat vf, NTV2PixelFormat pf, SDITransport trx, SDITransport4K t4k); +extern std::vector MultiViewCards(); + } // aja