aja: Capture and Output plugin for AJA Video Systems IO devices
* aja: Initial commit of AJA capture/output plugin * aja: Fix clang-format on aja-output-ui code * aja: Remove script used during dev/testing * aja: Address pull request feedback from @RytoEX * aja: Remove the SDK sources and update CMakeLists to point to new headers-only/static libs dependency distribution. * aja: Only build AJA plugin on x64 on macOS for now * aja: Remove the non-English placeholder locale files. The english strings/files will be produced via crowdin, according to @ddrboxman. * aja: Add FindLibAJANTV2.cmake script to locate the ajantv2 headers and static libs in the OBS external deps package(s). Tested on Windows x64. macOS and Linux x64 TBD. * aja: Add ajantv2/includes to FindLibAJANTV2 include search paths * aja: Remove commented code from aja CMakeLists * aja: Remove debug code and comments that are no longer needed. * aja: Fix indentation * aja: Remove disablement of clang-format in routing table and SDIWireFormat map * aja: Use spaces for all indentation in widget crosspoint arrays where we disable clang-format * aja: Address code style comments made by @RytoEX * aja: Fix uneven indentation * aja: More fixes to if/else placement and remove superfluous comments. * aja: Rename 'dwns' to 'deactivateWhileNotShowing' for clarity. The DeckLink plugin still uses the variable name 'dwns' and should be changed, if desired, in a separate PR. * aja: Remove X11Extras dependency from AJA Output frontend plugin * aja: Add patch from Jim to find AJA release/debug libs * aja: Improve AV sync of queued video/audio sent to the AJA card in the AJA Output plugin.
This commit is contained in:
parent
b387047632
commit
ce3ae8e423
@ -329,6 +329,8 @@ bundle_dylibs() {
|
||||
./OBS.app/Contents/PlugIns/obs-x264.so
|
||||
./OBS.app/Contents/PlugIns/text-freetype2.so
|
||||
./OBS.app/Contents/PlugIns/obs-outputs.so
|
||||
./OBS.app/Contents/PlugIns/aja.so
|
||||
./OBS.app/Contents/PlugIns/aja-output-ui.so
|
||||
)
|
||||
if ! [ "${MACOS_CEF_BUILD_VERSION:-${CI_MACOS_CEF_VERSION}}" -le 3770 ]; then
|
||||
"${CI_SCRIPTS}/app/dylibbundler" -cd -of -a ./OBS.app -q -f \
|
||||
|
@ -1,3 +1,4 @@
|
||||
add_subdirectory(decklink-output-ui)
|
||||
add_subdirectory(frontend-tools)
|
||||
add_subdirectory(decklink-captions)
|
||||
add_subdirectory(aja-output-ui)
|
||||
|
169
UI/frontend-plugins/aja-output-ui/AJAOutputUI.cpp
Normal file
169
UI/frontend-plugins/aja-output-ui/AJAOutputUI.cpp
Normal file
@ -0,0 +1,169 @@
|
||||
#include "AJAOutputUI.h"
|
||||
#include "aja-ui-main.h"
|
||||
|
||||
#include "../../../plugins/aja/aja-ui-props.hpp"
|
||||
#include "../../../plugins/aja/aja-enums.hpp"
|
||||
|
||||
#include <ajantv2/includes/ntv2enums.h>
|
||||
|
||||
#include <obs-module.h>
|
||||
#include <util/platform.h>
|
||||
#include <util/util.hpp>
|
||||
|
||||
AJAOutputUI::AJAOutputUI(QWidget *parent) : QDialog(parent), ui(new Ui_Output)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
setSizeGripEnabled(true);
|
||||
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
propertiesView = nullptr;
|
||||
previewPropertiesView = nullptr;
|
||||
}
|
||||
|
||||
void AJAOutputUI::ShowHideDialog()
|
||||
{
|
||||
SetupPropertiesView();
|
||||
SetupPreviewPropertiesView();
|
||||
|
||||
setVisible(!isVisible());
|
||||
}
|
||||
|
||||
void AJAOutputUI::SetupPropertiesView()
|
||||
{
|
||||
if (propertiesView)
|
||||
delete propertiesView;
|
||||
|
||||
obs_data_t *settings = obs_data_create();
|
||||
|
||||
OBSData data = load_settings(kProgramPropsFilename);
|
||||
if (data) {
|
||||
obs_data_apply(settings, data);
|
||||
} else {
|
||||
// apply default settings
|
||||
obs_data_set_int(settings, kUIPropOutput.id,
|
||||
static_cast<long long>(IOSelection::Invalid));
|
||||
obs_data_set_int(settings, kUIPropVideoFormatSelect.id,
|
||||
static_cast<long long>(NTV2_FORMAT_720p_5994));
|
||||
obs_data_set_int(settings, kUIPropPixelFormatSelect.id,
|
||||
static_cast<long long>(NTV2_FBF_8BIT_YCBCR));
|
||||
obs_data_set_int(settings, kUIPropSDI4KTransport.id,
|
||||
static_cast<long long>(
|
||||
SDI4KTransport::TwoSampleInterleave));
|
||||
}
|
||||
|
||||
// Assign an ID to the program output plugin instance for channel usage tracking
|
||||
obs_data_set_string(settings, kUIPropAJAOutputID.id, kProgramOutputID);
|
||||
|
||||
propertiesView = new OBSPropertiesView(
|
||||
settings, "aja_output",
|
||||
(PropertiesReloadCallback)obs_get_output_properties, 170);
|
||||
|
||||
ui->propertiesLayout->addWidget(propertiesView);
|
||||
obs_data_release(settings);
|
||||
|
||||
connect(propertiesView, SIGNAL(Changed()), this,
|
||||
SLOT(PropertiesChanged()));
|
||||
}
|
||||
|
||||
void AJAOutputUI::SaveSettings(const char *filename, obs_data_t *settings)
|
||||
{
|
||||
BPtr<char> modulePath =
|
||||
obs_module_get_config_path(obs_current_module(), "");
|
||||
|
||||
os_mkdirs(modulePath);
|
||||
|
||||
BPtr<char> path =
|
||||
obs_module_get_config_path(obs_current_module(), filename);
|
||||
|
||||
if (settings)
|
||||
obs_data_save_json_safe(settings, path, "tmp", "bak");
|
||||
}
|
||||
|
||||
void AJAOutputUI::SetupPreviewPropertiesView()
|
||||
{
|
||||
if (previewPropertiesView)
|
||||
delete previewPropertiesView;
|
||||
|
||||
obs_data_t *settings = obs_data_create();
|
||||
|
||||
OBSData data = load_settings(kPreviewPropsFilename);
|
||||
if (data) {
|
||||
obs_data_apply(settings, data);
|
||||
} else {
|
||||
// apply default settings
|
||||
obs_data_set_int(settings, kUIPropOutput.id,
|
||||
static_cast<long long>(IOSelection::Invalid));
|
||||
obs_data_set_int(settings, kUIPropVideoFormatSelect.id,
|
||||
static_cast<long long>(NTV2_FORMAT_720p_5994));
|
||||
obs_data_set_int(settings, kUIPropPixelFormatSelect.id,
|
||||
static_cast<long long>(NTV2_FBF_8BIT_YCBCR));
|
||||
obs_data_set_int(settings, kUIPropSDI4KTransport.id,
|
||||
static_cast<long long>(
|
||||
SDI4KTransport::TwoSampleInterleave));
|
||||
}
|
||||
|
||||
// Assign an ID to the program output plugin instance for channel usage tracking
|
||||
obs_data_set_string(settings, kUIPropAJAOutputID.id, kPreviewOutputID);
|
||||
|
||||
previewPropertiesView = new OBSPropertiesView(
|
||||
settings, "aja_output",
|
||||
(PropertiesReloadCallback)obs_get_output_properties, 170);
|
||||
|
||||
ui->previewPropertiesLayout->addWidget(previewPropertiesView);
|
||||
obs_data_release(settings);
|
||||
|
||||
connect(previewPropertiesView, SIGNAL(Changed()), this,
|
||||
SLOT(PreviewPropertiesChanged()));
|
||||
}
|
||||
|
||||
void AJAOutputUI::on_outputButton_clicked()
|
||||
{
|
||||
SaveSettings(kProgramPropsFilename, propertiesView->GetSettings());
|
||||
output_toggle();
|
||||
}
|
||||
|
||||
void AJAOutputUI::PropertiesChanged()
|
||||
{
|
||||
SaveSettings(kProgramPropsFilename, propertiesView->GetSettings());
|
||||
}
|
||||
|
||||
void AJAOutputUI::OutputStateChanged(bool active)
|
||||
{
|
||||
QString text;
|
||||
if (active) {
|
||||
text = QString(obs_module_text("Stop"));
|
||||
} else {
|
||||
text = QString(obs_module_text("Start"));
|
||||
}
|
||||
|
||||
ui->outputButton->setChecked(active);
|
||||
ui->outputButton->setText(text);
|
||||
}
|
||||
|
||||
void AJAOutputUI::on_previewOutputButton_clicked()
|
||||
{
|
||||
SaveSettings(kPreviewPropsFilename,
|
||||
previewPropertiesView->GetSettings());
|
||||
preview_output_toggle();
|
||||
}
|
||||
|
||||
void AJAOutputUI::PreviewPropertiesChanged()
|
||||
{
|
||||
SaveSettings(kPreviewPropsFilename,
|
||||
previewPropertiesView->GetSettings());
|
||||
}
|
||||
|
||||
void AJAOutputUI::PreviewOutputStateChanged(bool active)
|
||||
{
|
||||
QString text;
|
||||
if (active) {
|
||||
text = QString(obs_module_text("Stop"));
|
||||
} else {
|
||||
text = QString(obs_module_text("Start"));
|
||||
}
|
||||
|
||||
ui->previewOutputButton->setChecked(active);
|
||||
ui->previewOutputButton->setText(text);
|
||||
}
|
33
UI/frontend-plugins/aja-output-ui/AJAOutputUI.h
Normal file
33
UI/frontend-plugins/aja-output-ui/AJAOutputUI.h
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "ui_output.h"
|
||||
#include "../../UI/properties-view.hpp"
|
||||
|
||||
class AJAOutputUI : public QDialog {
|
||||
Q_OBJECT
|
||||
private:
|
||||
OBSPropertiesView *propertiesView;
|
||||
OBSPropertiesView *previewPropertiesView;
|
||||
|
||||
public slots:
|
||||
void on_outputButton_clicked();
|
||||
void PropertiesChanged();
|
||||
void OutputStateChanged(bool);
|
||||
|
||||
void on_previewOutputButton_clicked();
|
||||
void PreviewPropertiesChanged();
|
||||
void PreviewOutputStateChanged(bool);
|
||||
|
||||
public:
|
||||
std::unique_ptr<Ui_Output> ui;
|
||||
AJAOutputUI(QWidget *parent);
|
||||
|
||||
void ShowHideDialog();
|
||||
|
||||
void SaveSettings(const char *filename, obs_data_t *settings);
|
||||
|
||||
void SetupPropertiesView();
|
||||
void SetupPreviewPropertiesView();
|
||||
};
|
113
UI/frontend-plugins/aja-output-ui/CMakeLists.txt
Normal file
113
UI/frontend-plugins/aja-output-ui/CMakeLists.txt
Normal file
@ -0,0 +1,113 @@
|
||||
project(aja-output-ui)
|
||||
|
||||
if(DISABLE_AJA)
|
||||
message(STATUS "aja-output-ui plugin disabled")
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
find_package(LibAJANTV2)
|
||||
if (NOT LIBAJANTV2_FOUND)
|
||||
message(STATUS "aja-output-ui plugin disabled (deps not found)")
|
||||
return()
|
||||
else()
|
||||
message("aja-output-ui includes: ${LIBAJANTV2_INCLUDE_DIRS}")
|
||||
message("aja-output-ui libs: ${LIBAJANTV2_LIBRARIES}")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "aja-output-ui disabled (32-bit not supported)")
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
find_library(COCOA Cocoa)
|
||||
include_directories(${COCOA})
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
find_package(X11 REQUIRED)
|
||||
link_libraries(${X11_LIBRARIES})
|
||||
include_directories(${X11_INCLUDE_DIR})
|
||||
|
||||
find_package(Qt5X11Extras REQUIRED)
|
||||
endif()
|
||||
|
||||
set(aja-output-ui_HEADERS
|
||||
${aja-output-ui_HEADERS}
|
||||
../../qt-wrappers.hpp
|
||||
../../properties-view.hpp
|
||||
../../properties-view.moc.hpp
|
||||
../../vertical-scroll-area.hpp
|
||||
../../double-slider.hpp
|
||||
../../slider-ignorewheel.hpp
|
||||
../../combobox-ignorewheel.hpp
|
||||
../../spinbox-ignorewheel.hpp
|
||||
AJAOutputUI.h
|
||||
aja-ui-main.h
|
||||
../../../plugins/aja/aja-ui-props.hpp
|
||||
../../../plugins/aja/aja-enums.hpp)
|
||||
|
||||
set(aja-output-ui_SOURCES
|
||||
${aja-output-ui_SOURCES}
|
||||
../../qt-wrappers.cpp
|
||||
../../properties-view.cpp
|
||||
../../vertical-scroll-area.cpp
|
||||
../../double-slider.cpp
|
||||
../../slider-ignorewheel.cpp
|
||||
../../combobox-ignorewheel.cpp
|
||||
../../spinbox-ignorewheel.cpp
|
||||
AJAOutputUI.cpp
|
||||
aja-ui-main.cpp)
|
||||
|
||||
set(aja-output-ui_UI
|
||||
${aja-output-ui_UI}
|
||||
forms/output.ui)
|
||||
|
||||
if(WIN32)
|
||||
set(MODULE_DESCRIPTION "OBS AJA Output UI")
|
||||
configure_file(${CMAKE_SOURCE_DIR}/cmake/winrc/obs-module.rc.in aja-output-ui.rc)
|
||||
list(APPEND aja-output-ui_SOURCES
|
||||
aja-output-ui.rc)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
set(aja-output-ui_PLATFORM_LIBS
|
||||
${COCOA})
|
||||
endif()
|
||||
|
||||
qt5_wrap_ui(aja-output-ui_UI_HEADERS
|
||||
${aja-output-ui_UI})
|
||||
|
||||
add_library(aja-output-ui MODULE
|
||||
${aja-output-ui_HEADERS}
|
||||
${aja-output-ui_SOURCES}
|
||||
${aja-output-ui_UI_HEADERS})
|
||||
|
||||
if (APPLE)
|
||||
target_compile_definitions(aja-output-ui PUBLIC
|
||||
AJAMac
|
||||
AJA_MAC)
|
||||
elseif(WIN32)
|
||||
target_compile_definitions(aja-output-ui PUBLIC
|
||||
AJA_WINDOWS
|
||||
_WINDOWS
|
||||
WIN32
|
||||
MSWindows)
|
||||
elseif(UNIX AND NOT APPLE)
|
||||
target_compile_definitions(aja-output-ui PUBLIC
|
||||
AJA_LINUX
|
||||
AJALinux)
|
||||
endif()
|
||||
|
||||
target_include_directories(aja-output-ui PUBLIC
|
||||
${LIBAJANTV2_INCLUDE_DIRS})
|
||||
|
||||
target_link_libraries(aja-output-ui
|
||||
${frontend-tools_PLATFORM_LIBS}
|
||||
obs-frontend-api
|
||||
Qt5::Widgets
|
||||
libobs)
|
||||
|
||||
set_target_properties(aja-output-ui PROPERTIES FOLDER "frontend")
|
||||
|
||||
install_obs_plugin_with_data(aja-output-ui data)
|
318
UI/frontend-plugins/aja-output-ui/aja-ui-main.cpp
Normal file
318
UI/frontend-plugins/aja-output-ui/aja-ui-main.cpp
Normal file
@ -0,0 +1,318 @@
|
||||
#include "aja-ui-main.h"
|
||||
#include "AJAOutputUI.h"
|
||||
|
||||
#include "../../../plugins/aja/aja-ui-props.hpp"
|
||||
|
||||
#include <obs-module.h>
|
||||
#include <obs-frontend-api.h>
|
||||
#include <QMainWindow>
|
||||
#include <QAction>
|
||||
#include <util/util.hpp>
|
||||
#include <util/platform.h>
|
||||
#include <media-io/video-io.h>
|
||||
#include <media-io/video-frame.h>
|
||||
|
||||
OBS_DECLARE_MODULE()
|
||||
OBS_MODULE_USE_DEFAULT_LOCALE("aja-output-ui", "en-US")
|
||||
|
||||
AJAOutputUI *doUI;
|
||||
|
||||
bool main_output_running = false;
|
||||
bool preview_output_running = false;
|
||||
|
||||
obs_output_t *output;
|
||||
|
||||
struct preview_output {
|
||||
bool enabled;
|
||||
obs_source_t *current_source;
|
||||
obs_output_t *output;
|
||||
|
||||
video_t *video_queue;
|
||||
gs_texrender_t *texrender;
|
||||
gs_stagesurf_t *stagesurface;
|
||||
uint8_t *video_data;
|
||||
uint32_t video_linesize;
|
||||
|
||||
obs_video_info ovi;
|
||||
};
|
||||
|
||||
static struct preview_output context = {0};
|
||||
|
||||
OBSData load_settings(const char *filename)
|
||||
{
|
||||
BPtr<char> path =
|
||||
obs_module_get_config_path(obs_current_module(), filename);
|
||||
BPtr<char> jsonData = os_quick_read_utf8_file(path);
|
||||
if (!!jsonData) {
|
||||
obs_data_t *data = obs_data_create_from_json(jsonData);
|
||||
OBSData dataRet(data);
|
||||
obs_data_release(data);
|
||||
|
||||
return dataRet;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void output_stop()
|
||||
{
|
||||
obs_output_stop(output);
|
||||
obs_output_release(output);
|
||||
main_output_running = false;
|
||||
doUI->OutputStateChanged(false);
|
||||
}
|
||||
|
||||
void output_start()
|
||||
{
|
||||
OBSData settings = load_settings(kProgramPropsFilename);
|
||||
|
||||
if (settings != nullptr) {
|
||||
output = obs_output_create("aja_output", kProgramOutputID,
|
||||
settings, NULL);
|
||||
|
||||
bool started = obs_output_start(output);
|
||||
obs_data_release(settings);
|
||||
|
||||
main_output_running = started;
|
||||
|
||||
doUI->OutputStateChanged(started);
|
||||
|
||||
if (!started)
|
||||
output_stop();
|
||||
}
|
||||
}
|
||||
|
||||
void output_toggle()
|
||||
{
|
||||
if (main_output_running)
|
||||
output_stop();
|
||||
else
|
||||
output_start();
|
||||
}
|
||||
|
||||
void on_preview_scene_changed(enum obs_frontend_event event, void *param);
|
||||
void render_preview_source(void *param, uint32_t cx, uint32_t cy);
|
||||
|
||||
void preview_output_stop()
|
||||
{
|
||||
obs_output_stop(context.output);
|
||||
obs_output_release(context.output);
|
||||
video_output_stop(context.video_queue);
|
||||
|
||||
obs_remove_main_render_callback(render_preview_source, &context);
|
||||
obs_frontend_remove_event_callback(on_preview_scene_changed, &context);
|
||||
|
||||
obs_source_release(context.current_source);
|
||||
|
||||
obs_enter_graphics();
|
||||
gs_stagesurface_destroy(context.stagesurface);
|
||||
gs_texrender_destroy(context.texrender);
|
||||
obs_leave_graphics();
|
||||
|
||||
video_output_close(context.video_queue);
|
||||
|
||||
preview_output_running = false;
|
||||
doUI->PreviewOutputStateChanged(false);
|
||||
}
|
||||
|
||||
void preview_output_start()
|
||||
{
|
||||
OBSData settings = load_settings(kPreviewPropsFilename);
|
||||
|
||||
if (settings != nullptr) {
|
||||
context.output = obs_output_create(
|
||||
"aja_output", kPreviewOutputID, settings, NULL);
|
||||
|
||||
obs_get_video_info(&context.ovi);
|
||||
|
||||
uint32_t width = context.ovi.base_width;
|
||||
uint32_t height = context.ovi.base_height;
|
||||
|
||||
obs_enter_graphics();
|
||||
context.texrender = gs_texrender_create(GS_BGRA, GS_ZS_NONE);
|
||||
context.stagesurface =
|
||||
gs_stagesurface_create(width, height, GS_BGRA);
|
||||
obs_leave_graphics();
|
||||
|
||||
const video_output_info *mainVOI =
|
||||
video_output_get_info(obs_get_video());
|
||||
|
||||
video_output_info vi = {0};
|
||||
vi.format = VIDEO_FORMAT_BGRA;
|
||||
vi.width = width;
|
||||
vi.height = height;
|
||||
vi.fps_den = context.ovi.fps_den;
|
||||
vi.fps_num = context.ovi.fps_num;
|
||||
vi.cache_size = 16;
|
||||
vi.colorspace = mainVOI->colorspace;
|
||||
vi.range = mainVOI->range;
|
||||
vi.name = kPreviewOutputID;
|
||||
|
||||
video_output_open(&context.video_queue, &vi);
|
||||
|
||||
obs_frontend_add_event_callback(on_preview_scene_changed,
|
||||
&context);
|
||||
if (obs_frontend_preview_program_mode_active()) {
|
||||
context.current_source =
|
||||
obs_frontend_get_current_preview_scene();
|
||||
} else {
|
||||
context.current_source =
|
||||
obs_frontend_get_current_scene();
|
||||
}
|
||||
obs_add_main_render_callback(render_preview_source, &context);
|
||||
|
||||
obs_output_set_media(context.output, context.video_queue,
|
||||
obs_get_audio());
|
||||
bool started = obs_output_start(context.output);
|
||||
|
||||
obs_data_release(settings);
|
||||
|
||||
preview_output_running = started;
|
||||
doUI->PreviewOutputStateChanged(started);
|
||||
|
||||
if (!started)
|
||||
preview_output_stop();
|
||||
}
|
||||
}
|
||||
|
||||
void preview_output_toggle()
|
||||
{
|
||||
if (preview_output_running)
|
||||
preview_output_stop();
|
||||
else
|
||||
preview_output_start();
|
||||
}
|
||||
|
||||
void on_preview_scene_changed(enum obs_frontend_event event, void *param)
|
||||
{
|
||||
auto ctx = (struct preview_output *)param;
|
||||
switch (event) {
|
||||
case OBS_FRONTEND_EVENT_STUDIO_MODE_ENABLED:
|
||||
case OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED:
|
||||
obs_source_release(ctx->current_source);
|
||||
ctx->current_source = obs_frontend_get_current_preview_scene();
|
||||
break;
|
||||
case OBS_FRONTEND_EVENT_STUDIO_MODE_DISABLED:
|
||||
obs_source_release(ctx->current_source);
|
||||
ctx->current_source = obs_frontend_get_current_scene();
|
||||
break;
|
||||
case OBS_FRONTEND_EVENT_SCENE_CHANGED:
|
||||
if (!obs_frontend_preview_program_mode_active()) {
|
||||
obs_source_release(ctx->current_source);
|
||||
ctx->current_source = obs_frontend_get_current_scene();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void render_preview_source(void *param, uint32_t cx, uint32_t cy)
|
||||
{
|
||||
UNUSED_PARAMETER(cx);
|
||||
UNUSED_PARAMETER(cy);
|
||||
|
||||
auto ctx = (struct preview_output *)param;
|
||||
|
||||
if (!ctx->current_source)
|
||||
return;
|
||||
|
||||
uint32_t width = obs_source_get_base_width(ctx->current_source);
|
||||
uint32_t height = obs_source_get_base_height(ctx->current_source);
|
||||
|
||||
gs_texrender_reset(ctx->texrender);
|
||||
|
||||
if (gs_texrender_begin(ctx->texrender, width, height)) {
|
||||
struct vec4 background;
|
||||
vec4_zero(&background);
|
||||
|
||||
gs_clear(GS_CLEAR_COLOR, &background, 0.0f, 0);
|
||||
gs_ortho(0.0f, (float)width, 0.0f, (float)height, -100.0f,
|
||||
100.0f);
|
||||
|
||||
gs_blend_state_push();
|
||||
gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
|
||||
|
||||
obs_source_video_render(ctx->current_source);
|
||||
|
||||
gs_blend_state_pop();
|
||||
gs_texrender_end(ctx->texrender);
|
||||
|
||||
struct video_frame output_frame;
|
||||
if (video_output_lock_frame(ctx->video_queue, &output_frame, 1,
|
||||
os_gettime_ns())) {
|
||||
gs_stage_texture(
|
||||
ctx->stagesurface,
|
||||
gs_texrender_get_texture(ctx->texrender));
|
||||
|
||||
if (gs_stagesurface_map(ctx->stagesurface,
|
||||
&ctx->video_data,
|
||||
&ctx->video_linesize)) {
|
||||
uint32_t linesize = output_frame.linesize[0];
|
||||
for (uint32_t i = 0; i < ctx->ovi.base_height;
|
||||
i++) {
|
||||
uint32_t dst_offset = linesize * i;
|
||||
uint32_t src_offset =
|
||||
ctx->video_linesize * i;
|
||||
memcpy(output_frame.data[0] +
|
||||
dst_offset,
|
||||
ctx->video_data + src_offset,
|
||||
linesize);
|
||||
}
|
||||
|
||||
gs_stagesurface_unmap(ctx->stagesurface);
|
||||
ctx->video_data = nullptr;
|
||||
}
|
||||
|
||||
video_output_unlock_frame(ctx->video_queue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addOutputUI(void)
|
||||
{
|
||||
QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(
|
||||
"AJA I/O Device Output");
|
||||
|
||||
QMainWindow *window = (QMainWindow *)obs_frontend_get_main_window();
|
||||
|
||||
obs_frontend_push_ui_translation(obs_module_get_string);
|
||||
doUI = new AJAOutputUI(window);
|
||||
obs_frontend_pop_ui_translation();
|
||||
|
||||
auto cb = []() { doUI->ShowHideDialog(); };
|
||||
|
||||
action->connect(action, &QAction::triggered, cb);
|
||||
}
|
||||
|
||||
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();
|
||||
} else if (event == OBS_FRONTEND_EVENT_EXIT) {
|
||||
if (main_output_running)
|
||||
output_stop();
|
||||
if (preview_output_running)
|
||||
preview_output_stop();
|
||||
}
|
||||
}
|
||||
|
||||
bool obs_module_load(void)
|
||||
{
|
||||
addOutputUI();
|
||||
|
||||
obs_frontend_add_event_callback(OBSEvent, nullptr);
|
||||
|
||||
return true;
|
||||
}
|
10
UI/frontend-plugins/aja-output-ui/aja-ui-main.h
Normal file
10
UI/frontend-plugins/aja-output-ui/aja-ui-main.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <obs.hpp>
|
||||
|
||||
static const char *kProgramPropsFilename = "ajaOutputProps.json";
|
||||
static const char *kPreviewPropsFilename = "ajaPreviewOutputProps.json";
|
||||
|
||||
OBSData load_settings(const char *filename);
|
||||
void output_toggle();
|
||||
void preview_output_toggle();
|
0
UI/frontend-plugins/aja-output-ui/data/.keepme
Normal file
0
UI/frontend-plugins/aja-output-ui/data/.keepme
Normal file
3
UI/frontend-plugins/aja-output-ui/data/locale/en-US.ini
Normal file
3
UI/frontend-plugins/aja-output-ui/data/locale/en-US.ini
Normal file
@ -0,0 +1,3 @@
|
||||
AJAOutput.Device="AJA I/O Device Output"
|
||||
AJAOutput.ProgramOutput="Program Output"
|
||||
AJAOutput.PreviewOutput="Preview Output"
|
113
UI/frontend-plugins/aja-output-ui/forms/output.ui
Normal file
113
UI/frontend-plugins/aja-output-ui/forms/output.ui
Normal file
@ -0,0 +1,113 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Output</class>
|
||||
<widget class="QDialog" name="Output">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>540</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>AJAOutput.Device</string>
|
||||
</property>
|
||||
<property name="sizeGripEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>AJAOutput.ProgramOutput</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="propertiesLayout"/>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="outputButton">
|
||||
<property name="text">
|
||||
<string>Start</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>AJAOutput.PreviewOutput</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="previewPropertiesLayout"/>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="previewOutputButton">
|
||||
<property name="text">
|
||||
<string>Start</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
113
cmake/Modules/FindLibAJANTV2.cmake
Normal file
113
cmake/Modules/FindLibAJANTV2.cmake
Normal file
@ -0,0 +1,113 @@
|
||||
# Once done these will be defined:
|
||||
#
|
||||
# LIBAJANTV2_FOUND
|
||||
# LIBAJANTV2_INCLUDE_DIRS
|
||||
# LIBAJANTV2_LIBRARIES
|
||||
#
|
||||
find_package(PkgConfig QUIET)
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(_AJA QUIET ajantv2)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(_lib_suffix 64)
|
||||
else()
|
||||
set(_lib_suffix 32)
|
||||
endif()
|
||||
|
||||
find_path(AJA_LIBRARIES_INCLUDE_DIR
|
||||
NAMES ajalibraries
|
||||
HINTS
|
||||
ENV AJASDKPath${_lib_suffix}
|
||||
ENV AJASDKPath
|
||||
ENV DepsPath${_lib_suffix}
|
||||
ENV DepsPath
|
||||
${AJASDKPath${_lib_suffix}}
|
||||
${AJASDKPath}
|
||||
${DepsPath${_lib_suffix}}
|
||||
${DepsPath}
|
||||
${_AJA_NTV2_INCLUDE_DIRS}
|
||||
PATHS
|
||||
/usr/include /usr/local/include /opt/local/include /sw/include
|
||||
PATH_SUFFIXES
|
||||
include)
|
||||
|
||||
find_library(AJA_NTV2_LIB
|
||||
NAMES ${_AJA_NTV2_LIBRARIES} ajantv2 libajantv2
|
||||
HINTS
|
||||
ENV AJASDKPath${_lib_suffix}
|
||||
ENV AJASDKPath
|
||||
ENV DepsPath${_lib_suffix}
|
||||
ENV DepsPath
|
||||
${AJASDKPath${_lib_suffix}}
|
||||
${AJASDKPath}
|
||||
${DepsPath${_lib_suffix}}
|
||||
${DepsPath}
|
||||
${_AJA_NTV2_LIBRARY_DIRS}
|
||||
PATHS
|
||||
/usr/lib /usr/local/lib /opt/local/lib /sw/lib
|
||||
PATH_SUFFIXES
|
||||
lib${_lib_suffix} lib
|
||||
libs${_lib_suffix} libs
|
||||
bin${_lib_suffix} bin
|
||||
../lib${_lib_suffix} ../lib
|
||||
../libs${_lib_suffix} ../libs
|
||||
../bin${_lib_suffix} ../bin)
|
||||
|
||||
find_library(AJA_NTV2_DEBUG_LIB
|
||||
NAMES ajantv2d libajantv2d
|
||||
HINTS
|
||||
ENV AJASDKPath${_lib_suffix}
|
||||
ENV AJASDKPath
|
||||
ENV DepsPath${_lib_suffix}
|
||||
ENV DepsPath
|
||||
${AJASDKPath${_lib_suffix}}
|
||||
${AJASDKPath}
|
||||
${DepsPath${_lib_suffix}}
|
||||
${DepsPath}
|
||||
${_AJA_NTV2_LIBRARY_DIRS}
|
||||
PATHS
|
||||
/usr/lib /usr/local/lib /opt/local/lib /sw/lib
|
||||
PATH_SUFFIXES
|
||||
lib${_lib_suffix} lib
|
||||
libs${_lib_suffix} libs
|
||||
bin${_lib_suffix} bin
|
||||
../lib${_lib_suffix} ../lib
|
||||
../libs${_lib_suffix} ../libs
|
||||
../bin${_lib_suffix} ../bin)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(LibAJANTV2 DEFAULT_MSG AJA_LIBRARIES_INCLUDE_DIR AJA_NTV2_LIB)
|
||||
mark_as_advanced(AJA_LIBRARIES_INCLUDE_DIR AJA_NTV2_LIB)
|
||||
|
||||
if(LIBAJANTV2_FOUND)
|
||||
set(AJA_LIBRARIES_INCLUDE_DIR
|
||||
${AJA_LIBRARIES_INCLUDE_DIR}/ajalibraries)
|
||||
set(AJA_LIBRARIES_INCLUDE_DIRS
|
||||
${AJA_LIBRARIES_INCLUDE_DIR}
|
||||
${AJA_LIBRARIES_INCLUDE_DIR}/ajaanc
|
||||
${AJA_LIBRARIES_INCLUDE_DIR}/ajabase
|
||||
${AJA_LIBRARIES_INCLUDE_DIR}/ajantv2
|
||||
${AJA_LIBRARIES_INCLUDE_DIR}/ajantv2/includes
|
||||
${AJA_LIBRARIES_INCLUDE_DIR}/ajantv2/src)
|
||||
if (WIN32)
|
||||
set(AJA_LIBRARIES_INCLUDE_DIRS
|
||||
${AJA_LIBRARIES_INCLUDE_DIRS}
|
||||
${AJA_LIBRARIES_INCLUDE_DIR}/ajantv2/src/win)
|
||||
elseif (APPLE)
|
||||
set(AJA_LIBRARIES_INCLUDE_DIRS
|
||||
${AJA_LIBRARIES_INCLUDE_DIRS}
|
||||
${AJA_LIBRARIES_INCLUDE_DIR}/ajantv2/src/mac)
|
||||
elseif(UNIX AND NOT APPLE)
|
||||
set(AJA_LIBRARIES_INCLUDE_DIRS
|
||||
${AJA_LIBRARIES_INCLUDE_DIRS}
|
||||
${AJA_LIBRARIES_INCLUDE_DIR}/ajantv2/src/lin)
|
||||
endif()
|
||||
|
||||
set(LIBAJANTV2_LIBRARIES ${AJA_NTV2_LIB})
|
||||
if(AJA_NTV2_DEBUG_LIB STREQUAL "AJA_NTV2_DEBUG_LIB-NOTFOUND")
|
||||
set(AJA_NTV2_DEBUG_LIB ${AJA_NTV2_LIB})
|
||||
endif()
|
||||
set(LIBAJANTV2_DEBUG_LIBRARIES ${AJA_NTV2_DEBUG_LIB})
|
||||
set(LIBAJANTV2_INCLUDE_DIRS ${AJA_LIBRARIES_INCLUDE_DIRS})
|
||||
endif()
|
@ -38,5 +38,6 @@ find . -type d \( -path ./deps \
|
||||
-o -path ./plugins/mac-syphon/syphon-framework \
|
||||
-o -path ./plugins/obs-outputs/ftl-sdk \
|
||||
-o -path ./plugins/obs-vst \
|
||||
-o -path ./plugins/aja/sdk \
|
||||
-o -path ./build \) -prune -type f -o -name '*.h' -or -name '*.hpp' -or -name '*.m' -or -name '*.mm' -or -name '*.c' -or -name '*.cpp' \
|
||||
| xargs -L100 -P${NPROC} ${CLANG_FORMAT} -i -style=file -fallback-style=none
|
||||
|
@ -91,3 +91,4 @@ add_subdirectory(obs-transitions)
|
||||
add_subdirectory(obs-text)
|
||||
add_subdirectory(rtmp-services)
|
||||
add_subdirectory(text-freetype2)
|
||||
add_subdirectory(aja)
|
||||
|
102
plugins/aja/CMakeLists.txt
Normal file
102
plugins/aja/CMakeLists.txt
Normal file
@ -0,0 +1,102 @@
|
||||
project(aja)
|
||||
|
||||
if(DISABLE_AJA)
|
||||
message(STATUS "aja plugin disabled")
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
find_package(LibAJANTV2)
|
||||
if (NOT LIBAJANTV2_FOUND)
|
||||
message(STATUS "aja plugin disabled (deps not found)")
|
||||
return()
|
||||
else()
|
||||
message("aja plugin includes: ${LIBAJANTV2_INCLUDE_DIRS}")
|
||||
message("aja plugin libs: ${LIBAJANTV2_LIBRARIES}")
|
||||
message("aja plugin debug libs: ${LIBAJANTV2_DEBUG_LIBRARIES}")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "aja plugin disabled (32-bit not supported)")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(aja_INCLUDE_DIRS
|
||||
${LIBAJANTV2_INCLUDE_DIRS})
|
||||
set(aja_LIBRARIES
|
||||
$<IF:$<CONFIG:Debug>,${LIBAJANTV2_DEBUG_LIBRARIES},${LIBAJANTV2_LIBRARIES}>
|
||||
libobs)
|
||||
|
||||
set(aja_SOURCES
|
||||
main.cpp
|
||||
aja-card-manager.cpp
|
||||
aja-common.cpp
|
||||
aja-output.cpp
|
||||
aja-props.cpp
|
||||
aja-routing.cpp
|
||||
aja-source.cpp
|
||||
aja-widget-io.cpp)
|
||||
|
||||
set(aja_HEADERS
|
||||
aja-card-manager.hpp
|
||||
aja-common.hpp
|
||||
aja-enums.hpp
|
||||
aja-ui-props.hpp
|
||||
aja-output.hpp
|
||||
aja-props.hpp
|
||||
aja-routing.hpp
|
||||
aja-source.hpp
|
||||
aja-widget-io.hpp)
|
||||
|
||||
# macOS
|
||||
if(APPLE)
|
||||
set(aja_COMPILE_DEFS
|
||||
AJAMac
|
||||
AJA_MAC)
|
||||
|
||||
find_library(IOKIT_FRAMEWORK Iokit)
|
||||
find_library(COREFOUNDATION_LIBRARY CoreFoundation)
|
||||
find_library(APPKIT_FRAMEWORK AppKit)
|
||||
|
||||
list(APPEND aja_LIBRARIES
|
||||
${IOKIT_FRAMEWORK}
|
||||
${COREFOUNDATION_LIBRARY}
|
||||
${APPKIT_FRAMEWORK})
|
||||
# Windows
|
||||
elseif(WIN32)
|
||||
set(aja_COMPILE_DEFS
|
||||
AJA_WINDOWS
|
||||
_WINDOWS
|
||||
WIN32
|
||||
MSWindows)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||
list(APPEND aja_COMPILE_DEFS
|
||||
_DEBUG)
|
||||
else()
|
||||
list(APPEND aja_COMPILE_DEFS
|
||||
NDEBUG)
|
||||
endif()
|
||||
|
||||
list(APPEND aja_LIBRARIES
|
||||
ws2_32.lib
|
||||
setupapi.lib
|
||||
Winmm.lib
|
||||
netapi32.lib
|
||||
Shlwapi.lib)
|
||||
# Linux
|
||||
elseif(UNIX AND NOT APPLE)
|
||||
set(aja_COMPILE_DEFS
|
||||
AJA_LINUX
|
||||
AJALinux)
|
||||
endif()
|
||||
|
||||
add_library(aja MODULE ${aja_SOURCES} ${aja_HEADERS})
|
||||
|
||||
target_include_directories(aja PUBLIC ${aja_INCLUDE_DIRS})
|
||||
target_link_libraries(aja PUBLIC ${aja_LIBRARIES})
|
||||
target_compile_definitions(aja PUBLIC ${aja_COMPILE_DEFS})
|
||||
|
||||
set_target_properties(aja PROPERTIES FOLDER
|
||||
"plugins")
|
||||
|
||||
install_obs_plugin_with_data(aja data)
|
78
plugins/aja/README.md
Normal file
78
plugins/aja/README.md
Normal file
@ -0,0 +1,78 @@
|
||||
# AJA I/O Device Capture and Output Plugin for OBS
|
||||
|
||||
A capture and outptu plugin for OBS for use with AJA NTV2 I/O devices. The plugin communicates with the AJA device driver via the open-source AJA NTV2 SDK (MIT license). See instructions below for installing the AJA device driver.
|
||||
|
||||
---
|
||||
|
||||
## AJA Device Driver Installation
|
||||
|
||||
Usage of this plugin with an AJA I/O device requires installation of the AJA Device Driver.
|
||||
|
||||
Follow one of the methods below to install the AJA device driver for your platform.
|
||||
|
||||
### Method 1 - AJA Software Installer (Windows/macOS/Ubuntu Linux)
|
||||
|
||||
Pre-built driver packages for Windows, macOS and Ubuntu Linux are provided as a component of the AJA Software Installer. Navigate to the AJA Support page and download the latest AJA Software Installer for your platform:
|
||||
|
||||
https://www.aja.com/support
|
||||
|
||||
### Method 2 - Build From Sources (Linux)
|
||||
|
||||
The Linux verison of the AJA device driver can be built from the MIT-licensed AJA NTV2 SDK sources found here:
|
||||
|
||||
https://github.com/aja-video/ntv2
|
||||
|
||||
- Clone the ntv2 repository.
|
||||
- `cd` into the `ntv2/ajadriver/linux` directory
|
||||
- Run `make`
|
||||
- `cd` into the `ntv2/bin` directory and run `sudo sh load_ajantv2` to install the AJA kernel module.
|
||||
|
||||
NOTE: To uniunstall the Linux kernel module, `cd` into the `ntv2/bin` directory and run `sudo sh unload_ajantv2`.
|
||||
|
||||
---
|
||||
|
||||
## How to use the plugin
|
||||
- Capture plugin instances can be added from `Sources -> Add -> AJA I/O Device Capture`.
|
||||
|
||||
- The Output plugin is accessible from `Tools -> AJA I/O Device Output`.
|
||||
|
||||
## Updating the plugin AJA NTV2 SDK code
|
||||
|
||||
The AJA NTV2 SDK is open-source and licensed under the MIT license. A copy of the SDK is included in the source tree of the OBS AJA plugin directory.
|
||||
|
||||
To update the plugin's working copy of the AJA NTV2 SDK:
|
||||
|
||||
1. Clone the AJA NTV2 SDK from https://github.com/aja-video/ntv2
|
||||
1. Place the contents of the ntv2 directory into `plugins/aja/sdk` within the cloned OBS repository.
|
||||
|
||||
You should end up with a directory structure like:
|
||||
|
||||
```
|
||||
plugins/aja/sdk
|
||||
/ajadriver
|
||||
/ajalibraries
|
||||
/cmake
|
||||
/CMakeLists.txt
|
||||
/LICENSE
|
||||
/README.md
|
||||
```
|
||||
|
||||
## Known Issues/TODOS
|
||||
1. Fix AV sync in the AJA I/O Output plugin with certain OBS/device frame rate combinations.
|
||||
1. Improve frame tearing in the AJA I/O Device Capture plugin with certain OBS/device frame rate combinations.
|
||||
1. Capture plugins not always auto-detecting signal properly after disconnecting and reconnecting the physical SDI/HDMI cable from the device.
|
||||
1. Improve handling of certain invalid video/pixel/SDI 4K transport setting combinations.
|
||||
1. Optimize framebuffer memory usage on the device.
|
||||
1. Add support for Analog I/O capture and output on supported devices.
|
||||
1. Luminance is too bright/washed out in output plugin video.
|
||||
1. Certain combinations of video format with RGB pixel format needs additional testing.
|
||||
1. 4K video formats and SDI 4K transports need more testing in general.
|
||||
1. 8K video not yet implemented.
|
||||
1. Add support for selecting an alternate audio input source on supported devices (i.e. Microphone Input on AJA io4K+, analog audio, AES audio, etc). Capture and output currently default to using up to 8 channels of embedded SDI/HDMI audio.
|
||||
1. All translation strings are placeholders and default to English. Translation strings needed for additional language support.
|
||||
|
||||
---
|
||||
|
||||
_Development Credits_
|
||||
- ddrboxman
|
||||
- paulh-aja
|
610
plugins/aja/aja-card-manager.cpp
Normal file
610
plugins/aja/aja-card-manager.cpp
Normal file
@ -0,0 +1,610 @@
|
||||
#include "aja-card-manager.hpp"
|
||||
#include "aja-common.hpp"
|
||||
#include "aja-output.hpp"
|
||||
#include "aja-source.hpp"
|
||||
|
||||
#include "obs-properties.h"
|
||||
|
||||
#include <util/base.h>
|
||||
|
||||
#include <ajantv2/includes/ntv2card.h>
|
||||
#include <ajantv2/includes/ntv2devicescanner.h>
|
||||
#include <ajantv2/includes/ntv2devicefeatures.h>
|
||||
#include <ajantv2/includes/ntv2utils.h>
|
||||
|
||||
#include <ajabase/system/process.h>
|
||||
#include <ajabase/system/systemtime.h>
|
||||
|
||||
static const uint32_t kStreamingAppID = NTV2_FOURCC('O', 'B', 'S', ' ');
|
||||
|
||||
namespace aja {
|
||||
|
||||
CardManager &CardManager::Instance()
|
||||
{
|
||||
static CardManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
CardEntry::CardEntry(uint32_t cardIndex, const std::string &cardID)
|
||||
: mCardIndex{cardIndex},
|
||||
mCardID{cardID},
|
||||
mCard{std::make_unique<CNTV2Card>((UWord)cardIndex)},
|
||||
mChannelPwnz{},
|
||||
mMutex{}
|
||||
{
|
||||
}
|
||||
|
||||
CardEntry::~CardEntry()
|
||||
{
|
||||
if (mCard) {
|
||||
mCard->Close();
|
||||
mCard.reset();
|
||||
}
|
||||
}
|
||||
|
||||
CNTV2Card *CardEntry::GetCard()
|
||||
{
|
||||
return mCard.get();
|
||||
}
|
||||
|
||||
bool CardEntry::Initialize()
|
||||
{
|
||||
if (!mCard) {
|
||||
blog(LOG_ERROR, "Invalid card instance %s!", mCardID.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
const NTV2DeviceID deviceID = mCard->GetDeviceID();
|
||||
|
||||
// Briefly enter Standard Tasks mode to reset card via AJA Daemon/Agent.
|
||||
auto taskMode = NTV2_STANDARD_TASKS;
|
||||
mCard->GetEveryFrameServices(taskMode);
|
||||
if (taskMode != NTV2_STANDARD_TASKS) {
|
||||
mCard->SetEveryFrameServices(NTV2_STANDARD_TASKS);
|
||||
AJATime::Sleep(100);
|
||||
}
|
||||
|
||||
mCard->SetEveryFrameServices(NTV2_OEM_TASKS);
|
||||
|
||||
const int32_t obsPid = (int32_t)AJAProcess::GetPid();
|
||||
mCard->AcquireStreamForApplicationWithReference((ULWord)kStreamingAppID,
|
||||
obsPid);
|
||||
|
||||
mCard->SetSuspendHostAudio(true);
|
||||
|
||||
mCard->ClearRouting();
|
||||
|
||||
if (NTV2DeviceCanDoMultiFormat(deviceID)) {
|
||||
mCard->SetMultiFormatMode(true);
|
||||
}
|
||||
|
||||
mCard->SetReference(NTV2_REFERENCE_FREERUN);
|
||||
|
||||
for (UWord i = 0; i < aja::CardNumAudioSystems(deviceID); i++) {
|
||||
mCard->SetAudioLoopBack(NTV2_AUDIO_LOOPBACK_OFF,
|
||||
static_cast<NTV2AudioSystem>(i));
|
||||
}
|
||||
|
||||
auto numFramestores = aja::CardNumFramestores(deviceID);
|
||||
|
||||
for (UWord i = 0; i < NTV2DeviceGetNumVideoInputs(deviceID); i++) {
|
||||
mCard->SetInputFrame(static_cast<NTV2Channel>(i), 0xff);
|
||||
// Disable 3G Level B converter by default
|
||||
if (NTV2DeviceCanDo3GLevelConversion(deviceID)) {
|
||||
mCard->SetSDIInLevelBtoLevelAConversion(
|
||||
static_cast<NTV2Channel>(i), false);
|
||||
}
|
||||
}
|
||||
|
||||
// SDI Outputs Default State
|
||||
for (UWord i = 0; i < NTV2DeviceGetNumVideoOutputs(deviceID); i++) {
|
||||
auto channel = GetNTV2ChannelForIndex(i);
|
||||
if (NTV2DeviceCanDo3GOut(deviceID, i)) {
|
||||
mCard->SetSDIOut3GEnable(channel, true);
|
||||
mCard->SetSDIOut3GbEnable(channel, false);
|
||||
}
|
||||
if (NTV2DeviceCanDo12GOut(deviceID, i)) {
|
||||
mCard->SetSDIOut6GEnable(channel, false);
|
||||
mCard->SetSDIOut12GEnable(channel, false);
|
||||
}
|
||||
if (NTV2DeviceCanDo3GLevelConversion(deviceID)) {
|
||||
mCard->SetSDIOutLevelAtoLevelBConversion(i, false);
|
||||
mCard->SetSDIOutRGBLevelAConversion(i, false);
|
||||
}
|
||||
}
|
||||
|
||||
for (UWord i = 0; i < numFramestores; i++) {
|
||||
auto channel = GetNTV2ChannelForIndex(i);
|
||||
|
||||
if (isAutoCirculateRunning(channel)) {
|
||||
mCard->AutoCirculateStop(channel, true);
|
||||
}
|
||||
|
||||
mCard->SetVideoFormat(NTV2_FORMAT_1080p_5994_A, false, false,
|
||||
channel);
|
||||
mCard->SetFrameBufferFormat(channel, NTV2_FBF_8BIT_YCBCR);
|
||||
|
||||
mCard->DisableChannel(channel);
|
||||
}
|
||||
|
||||
blog(LOG_DEBUG, "NTV2 Card Initialized: %s", mCardID.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t CardEntry::GetCardIndex() const
|
||||
{
|
||||
return mCardIndex;
|
||||
}
|
||||
|
||||
std::string CardEntry::GetCardID() const
|
||||
{
|
||||
return mCardID;
|
||||
}
|
||||
|
||||
std::string CardEntry::GetDisplayName() const
|
||||
{
|
||||
if (mCard) {
|
||||
std::ostringstream oss;
|
||||
oss << mCard->GetIndexNumber() << " - "
|
||||
<< mCard->GetModelName();
|
||||
const std::string &serial = GetSerial();
|
||||
if (!serial.empty())
|
||||
oss << " (" << serial << ")";
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
// very bad if we get here...
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
std::string CardEntry::GetSerial() const
|
||||
{
|
||||
std::string serial;
|
||||
if (mCard)
|
||||
mCard->GetSerialNumberString(serial);
|
||||
|
||||
return serial;
|
||||
}
|
||||
|
||||
NTV2DeviceID CardEntry::GetDeviceID() const
|
||||
{
|
||||
NTV2DeviceID id = DEVICE_ID_NOTFOUND;
|
||||
if (mCard)
|
||||
id = mCard->GetDeviceID();
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
bool CardEntry::ChannelReady(NTV2Channel chan, const std::string &owner) const
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(mMutex);
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (const auto &pwn : mChannelPwnz) {
|
||||
if (pwn.second & (1 << static_cast<int32_t>(chan))) {
|
||||
return pwn.first == owner;
|
||||
}
|
||||
}
|
||||
|
||||
return !found;
|
||||
}
|
||||
|
||||
bool CardEntry::AcquireChannel(NTV2Channel chan, NTV2Mode mode,
|
||||
const std::string &owner)
|
||||
{
|
||||
bool acquired = false;
|
||||
|
||||
if (ChannelReady(chan, owner)) {
|
||||
const std::lock_guard<std::mutex> lock(mMutex);
|
||||
|
||||
if (mChannelPwnz.find(owner) != mChannelPwnz.end()) {
|
||||
if (mChannelPwnz[owner] &
|
||||
(1 << static_cast<int32_t>(chan))) {
|
||||
acquired = true;
|
||||
} else {
|
||||
mChannelPwnz[owner] |=
|
||||
(1 << static_cast<int32_t>(chan));
|
||||
acquired = true;
|
||||
}
|
||||
} else {
|
||||
mChannelPwnz[owner] |=
|
||||
(1 << static_cast<int32_t>(chan));
|
||||
acquired = true;
|
||||
}
|
||||
|
||||
// Acquire interrupt handles
|
||||
if (acquired && mCard) {
|
||||
if (mode == NTV2_MODE_CAPTURE) {
|
||||
mCard->EnableInputInterrupt(chan);
|
||||
mCard->SubscribeInputVerticalEvent(chan);
|
||||
} else if (mode == NTV2_MODE_DISPLAY) {
|
||||
mCard->EnableOutputInterrupt(chan);
|
||||
mCard->SubscribeOutputVerticalEvent(chan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return acquired;
|
||||
}
|
||||
|
||||
bool CardEntry::ReleaseChannel(NTV2Channel chan, NTV2Mode mode,
|
||||
const std::string &owner)
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(mMutex);
|
||||
|
||||
for (const auto &pwn : mChannelPwnz) {
|
||||
if (pwn.first == owner) {
|
||||
if (mChannelPwnz[owner] &
|
||||
(1 << static_cast<int32_t>(chan))) {
|
||||
mChannelPwnz[owner] ^=
|
||||
(1 << static_cast<int32_t>(chan));
|
||||
|
||||
// Release interrupt handles
|
||||
if (mCard) {
|
||||
if (mode == NTV2_MODE_CAPTURE) {
|
||||
mCard->DisableInputInterrupt(
|
||||
chan);
|
||||
mCard->UnsubscribeInputVerticalEvent(
|
||||
chan);
|
||||
} else if (mode == NTV2_MODE_DISPLAY) {
|
||||
mCard->DisableOutputInterrupt(
|
||||
chan);
|
||||
mCard->UnsubscribeOutputVerticalEvent(
|
||||
chan);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CardEntry::InputSelectionReady(IOSelection io, NTV2DeviceID id,
|
||||
const std::string &owner) const
|
||||
{
|
||||
UNUSED_PARAMETER(id);
|
||||
|
||||
NTV2InputSourceSet inputSources;
|
||||
aja::IOSelectionToInputSources(io, inputSources);
|
||||
|
||||
if (inputSources.size() > 0) {
|
||||
size_t channelsReady = 0;
|
||||
|
||||
for (auto &&src : inputSources) {
|
||||
auto channel = NTV2InputSourceToChannel(src);
|
||||
if (ChannelReady(channel, owner))
|
||||
channelsReady++;
|
||||
}
|
||||
|
||||
if (channelsReady == inputSources.size())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CardEntry::OutputSelectionReady(IOSelection io, NTV2DeviceID id,
|
||||
const std::string &owner) const
|
||||
{
|
||||
/* Handle checking special case outputs before all other outputs.
|
||||
* 1. HDMI Monitor uses framestore 4
|
||||
* 2. SDI Monitor on Io 4K/Io 4K Plus, etc. uses framestore 4.
|
||||
* 3. Everything else...
|
||||
*/
|
||||
if (aja::CardCanDoHDMIMonitorOutput(id) &&
|
||||
io == IOSelection::HDMIMonitorOut) {
|
||||
NTV2Channel hdmiMonChannel = NTV2_CHANNEL4;
|
||||
return ChannelReady(hdmiMonChannel, owner);
|
||||
} else if (aja::CardCanDoSDIMonitorOutput(id) &&
|
||||
io == IOSelection::SDI5) {
|
||||
NTV2Channel sdiMonChannel = NTV2_CHANNEL4;
|
||||
return ChannelReady(sdiMonChannel, owner);
|
||||
} else {
|
||||
NTV2OutputDestinations outputDests;
|
||||
aja::IOSelectionToOutputDests(io, outputDests);
|
||||
if (outputDests.size() > 0) {
|
||||
size_t channelsReady = 0;
|
||||
|
||||
for (auto &&dst : outputDests) {
|
||||
auto channel =
|
||||
NTV2OutputDestinationToChannel(dst);
|
||||
if (ChannelReady(channel, owner))
|
||||
channelsReady++;
|
||||
}
|
||||
|
||||
if (channelsReady == outputDests.size())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CardEntry::AcquireInputSelection(IOSelection io, NTV2DeviceID id,
|
||||
const std::string &owner)
|
||||
{
|
||||
UNUSED_PARAMETER(id);
|
||||
|
||||
NTV2InputSourceSet inputSources;
|
||||
aja::IOSelectionToInputSources(io, inputSources);
|
||||
|
||||
std::vector<NTV2Channel> acquiredChannels;
|
||||
for (auto &&src : inputSources) {
|
||||
auto acqChan = NTV2InputSourceToChannel(src);
|
||||
if (AcquireChannel(acqChan, NTV2_MODE_CAPTURE, owner)) {
|
||||
blog(LOG_DEBUG, "Source %s acquired channel %s",
|
||||
owner.c_str(),
|
||||
NTV2ChannelToString(acqChan).c_str());
|
||||
acquiredChannels.push_back(acqChan);
|
||||
} else {
|
||||
blog(LOG_DEBUG,
|
||||
"Source %s could not acquire channel %s",
|
||||
owner.c_str(),
|
||||
NTV2ChannelToString(acqChan).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Release channels if we couldn't acquire all required channels.
|
||||
if (acquiredChannels.size() != inputSources.size()) {
|
||||
for (auto &&ac : acquiredChannels) {
|
||||
ReleaseChannel(ac, NTV2_MODE_CAPTURE, owner);
|
||||
}
|
||||
}
|
||||
|
||||
return acquiredChannels.size() == inputSources.size();
|
||||
}
|
||||
|
||||
bool CardEntry::ReleaseInputSelection(IOSelection io, NTV2DeviceID id,
|
||||
const std::string &owner)
|
||||
{
|
||||
UNUSED_PARAMETER(id);
|
||||
|
||||
NTV2InputSourceSet currentInputSources;
|
||||
aja::IOSelectionToInputSources(io, currentInputSources);
|
||||
uint32_t releasedCount = 0;
|
||||
for (auto &&src : currentInputSources) {
|
||||
auto relChan = NTV2InputSourceToChannel(src);
|
||||
if (ReleaseChannel(relChan, NTV2_MODE_CAPTURE, owner)) {
|
||||
blog(LOG_DEBUG, "Released Channel %s",
|
||||
NTV2ChannelToString(relChan).c_str());
|
||||
releasedCount++;
|
||||
}
|
||||
}
|
||||
return releasedCount == currentInputSources.size();
|
||||
}
|
||||
|
||||
bool CardEntry::AcquireOutputSelection(IOSelection io, NTV2DeviceID id,
|
||||
const std::string &owner)
|
||||
{
|
||||
std::vector<NTV2Channel> acquiredChannels;
|
||||
NTV2OutputDestinations outputDests;
|
||||
aja::IOSelectionToOutputDests(io, outputDests);
|
||||
|
||||
// Handle acquiring special case outputs --
|
||||
// HDMI Monitor uses framestore 4
|
||||
if (aja::CardCanDoHDMIMonitorOutput(id) &&
|
||||
io == IOSelection::HDMIMonitorOut) {
|
||||
NTV2Channel hdmiMonChannel = NTV2_CHANNEL4;
|
||||
if (AcquireChannel(hdmiMonChannel, NTV2_MODE_DISPLAY, owner)) {
|
||||
blog(LOG_DEBUG, "Output %s acquired channel %s",
|
||||
owner.c_str(),
|
||||
NTV2ChannelToString(hdmiMonChannel).c_str());
|
||||
acquiredChannels.push_back(hdmiMonChannel);
|
||||
} else {
|
||||
blog(LOG_DEBUG,
|
||||
"Output %s could not acquire channel %s",
|
||||
owner.c_str(),
|
||||
NTV2ChannelToString(hdmiMonChannel).c_str());
|
||||
}
|
||||
} else if (aja::CardCanDoSDIMonitorOutput(id) &&
|
||||
io == IOSelection::SDI5) {
|
||||
// SDI Monitor on io4K/io4K+/etc. uses framestore 4
|
||||
NTV2Channel sdiMonChannel = NTV2_CHANNEL4;
|
||||
if (AcquireChannel(sdiMonChannel, NTV2_MODE_DISPLAY, owner)) {
|
||||
blog(LOG_DEBUG, "Output %s acquired channel %s",
|
||||
owner.c_str(),
|
||||
NTV2ChannelToString(sdiMonChannel).c_str());
|
||||
acquiredChannels.push_back(sdiMonChannel);
|
||||
} else {
|
||||
blog(LOG_DEBUG,
|
||||
"Output %s could not acquire channel %s",
|
||||
owner.c_str(),
|
||||
NTV2ChannelToString(sdiMonChannel).c_str());
|
||||
}
|
||||
} else {
|
||||
// Handle acquiring all other channels
|
||||
for (auto &&dst : outputDests) {
|
||||
auto acqChan = NTV2OutputDestinationToChannel(dst);
|
||||
if (AcquireChannel(acqChan, NTV2_MODE_DISPLAY, owner)) {
|
||||
acquiredChannels.push_back(acqChan);
|
||||
blog(LOG_DEBUG, "Output %s acquired channel %s",
|
||||
owner.c_str(),
|
||||
NTV2ChannelToString(acqChan).c_str());
|
||||
} else {
|
||||
blog(LOG_DEBUG,
|
||||
"Output %s could not acquire channel %s",
|
||||
owner.c_str(),
|
||||
NTV2ChannelToString(acqChan).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Release channels if we couldn't acquire all required channels.
|
||||
if (acquiredChannels.size() != outputDests.size()) {
|
||||
for (auto &&ac : acquiredChannels) {
|
||||
ReleaseChannel(ac, NTV2_MODE_DISPLAY, owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return acquiredChannels.size() == outputDests.size();
|
||||
}
|
||||
|
||||
bool CardEntry::ReleaseOutputSelection(IOSelection io, NTV2DeviceID id,
|
||||
const std::string &owner)
|
||||
{
|
||||
NTV2OutputDestinations currentOutputDests;
|
||||
aja::IOSelectionToOutputDests(io, currentOutputDests);
|
||||
uint32_t releasedCount = 0;
|
||||
|
||||
// Handle releasing special case outputs --
|
||||
// HDMI Monitor uses framestore 4
|
||||
if (aja::CardCanDoHDMIMonitorOutput(id) &&
|
||||
io == IOSelection::HDMIMonitorOut) {
|
||||
NTV2Channel hdmiMonChannel = NTV2_CHANNEL4;
|
||||
if (ReleaseChannel(hdmiMonChannel, NTV2_MODE_DISPLAY, owner)) {
|
||||
blog(LOG_DEBUG, "Released Channel %s",
|
||||
NTV2ChannelToString(hdmiMonChannel).c_str());
|
||||
releasedCount++;
|
||||
}
|
||||
} else if (aja::CardCanDoSDIMonitorOutput(id) &&
|
||||
io == IOSelection::SDI5) {
|
||||
// SDI Monitor on io4K/io4K+/etc. uses framestore 4
|
||||
NTV2Channel sdiMonChannel = NTV2_CHANNEL4;
|
||||
if (ReleaseChannel(sdiMonChannel, NTV2_MODE_DISPLAY, owner)) {
|
||||
blog(LOG_DEBUG, "Released Channel %s",
|
||||
NTV2ChannelToString(sdiMonChannel).c_str());
|
||||
releasedCount++;
|
||||
}
|
||||
} else {
|
||||
// Release all other channels
|
||||
for (auto &&dst : currentOutputDests) {
|
||||
auto relChan = NTV2OutputDestinationToChannel(dst);
|
||||
if (ReleaseChannel(relChan, NTV2_MODE_DISPLAY, owner)) {
|
||||
blog(LOG_DEBUG, "Released Channel %s",
|
||||
NTV2ChannelToString(relChan).c_str());
|
||||
releasedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return releasedCount == currentOutputDests.size();
|
||||
}
|
||||
|
||||
bool CardEntry::UpdateChannelOwnerName(const std::string &oldName,
|
||||
const std::string &newName)
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(mMutex);
|
||||
|
||||
for (const auto &pwn : mChannelPwnz) {
|
||||
if (pwn.first == oldName) {
|
||||
mChannelPwnz.insert(std::pair<std::string, int32_t>{
|
||||
newName, pwn.second});
|
||||
|
||||
mChannelPwnz.erase(oldName);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CardEntry::isAutoCirculateRunning(NTV2Channel chan)
|
||||
{
|
||||
if (!mCard)
|
||||
return false;
|
||||
|
||||
AUTOCIRCULATE_STATUS acStatus;
|
||||
if (mCard->AutoCirculateGetStatus(chan, acStatus)) {
|
||||
if (acStatus.acState != NTV2_AUTOCIRCULATE_RUNNING &&
|
||||
acStatus.acState != NTV2_AUTOCIRCULATE_STARTING &&
|
||||
acStatus.acState != NTV2_AUTOCIRCULATE_PAUSED &&
|
||||
acStatus.acState != NTV2_AUTOCIRCULATE_INIT) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CardManager::ClearCardEntries()
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(mMutex);
|
||||
|
||||
for (auto &&entry : mCardEntries) {
|
||||
CNTV2Card *card = entry.second->GetCard();
|
||||
if (card) {
|
||||
card->SetSuspendHostAudio(false);
|
||||
card->SetEveryFrameServices(NTV2_STANDARD_TASKS);
|
||||
// Workaround for AJA internal bug #11378
|
||||
// Set HDMI output back to Audio System 1 on card release
|
||||
if (NTV2DeviceGetNumHDMIVideoOutputs(
|
||||
card->GetDeviceID()) > 0) {
|
||||
card->SetHDMIOutAudioSource8Channel(
|
||||
NTV2_AudioChannel1_8,
|
||||
NTV2_AUDIOSYSTEM_1);
|
||||
}
|
||||
|
||||
int32_t pid = (int32_t)AJAProcess::GetPid();
|
||||
card->ReleaseStreamForApplicationWithReference(
|
||||
(ULWord)kStreamingAppID, pid);
|
||||
}
|
||||
}
|
||||
|
||||
mCardEntries.clear();
|
||||
}
|
||||
|
||||
void CardManager::EnumerateCards()
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(mMutex);
|
||||
|
||||
CNTV2DeviceScanner scanner;
|
||||
for (const auto &iter : scanner.GetDeviceInfoList()) {
|
||||
CNTV2Card card((UWord)iter.deviceIndex);
|
||||
const std::string &cardID = aja::MakeCardID(card);
|
||||
|
||||
// New Card Entry
|
||||
if (mCardEntries.find(cardID) == mCardEntries.end()) {
|
||||
CardEntryPtr cardEntry = std::make_shared<CardEntry>(
|
||||
iter.deviceIndex, cardID);
|
||||
if (cardEntry && cardEntry->Initialize())
|
||||
mCardEntries.emplace(cardID, cardEntry);
|
||||
} else {
|
||||
// Card fell off of the bus and came back with a new physical index?
|
||||
auto currEntry = mCardEntries[cardID];
|
||||
if (currEntry) {
|
||||
if (currEntry->GetCardIndex() !=
|
||||
iter.deviceIndex) {
|
||||
mCardEntries.erase(cardID);
|
||||
CardEntryPtr cardEntry =
|
||||
std::make_shared<CardEntry>(
|
||||
iter.deviceIndex,
|
||||
cardID);
|
||||
if (cardEntry &&
|
||||
cardEntry->Initialize())
|
||||
mCardEntries.emplace(cardID,
|
||||
cardEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t CardManager::NumCardEntries() const
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(mMutex);
|
||||
return mCardEntries.size();
|
||||
}
|
||||
|
||||
const CardEntryPtr CardManager::GetCardEntry(const std::string &cardID) const
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(mMutex);
|
||||
for (const auto &entry : mCardEntries) {
|
||||
if (entry.second && entry.second->GetCardID() == cardID)
|
||||
return entry.second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const CardEntries &CardManager::GetCardEntries() const
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(mMutex);
|
||||
return mCardEntries;
|
||||
}
|
||||
|
||||
} // aja
|
103
plugins/aja/aja-card-manager.hpp
Normal file
103
plugins/aja/aja-card-manager.hpp
Normal file
@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include "aja-props.hpp"
|
||||
|
||||
#include <obs-module.h>
|
||||
|
||||
#include <ajantv2/includes/ntv2enums.h>
|
||||
#include <ajantv2/includes/ntv2publicinterface.h>
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
class CNTV2Card;
|
||||
class AJAOutput;
|
||||
class AJASource;
|
||||
|
||||
namespace aja {
|
||||
|
||||
using ChannelPwnz = std::map<std::string, int32_t>;
|
||||
|
||||
/* A CardEntry for each physical AJA card is added to a map retained by the CardManager.
|
||||
* Each CardEntry itself maintains a map representing the AJA card "Channels" the are
|
||||
* owned by a particular capture or output plugin instance. The Channel ownership map is
|
||||
* then used to determine which "IOSelection" (i.e. SDI1, SDI3+4, HDMI Monitor Output, etc.)
|
||||
* drop-down menu items are either accessible or grayed out in the capture and output plugin UIs.
|
||||
*/
|
||||
class CardEntry {
|
||||
public:
|
||||
CardEntry(uint32_t cardIndex, const std::string &cardID);
|
||||
virtual ~CardEntry();
|
||||
CNTV2Card *GetCard();
|
||||
virtual bool Initialize();
|
||||
virtual uint32_t GetCardIndex() const;
|
||||
virtual std::string GetCardID() const;
|
||||
virtual std::string GetDisplayName() const;
|
||||
virtual std::string GetSerial() const;
|
||||
virtual NTV2DeviceID GetDeviceID() const;
|
||||
virtual bool ChannelReady(NTV2Channel chan,
|
||||
const std::string &owner) const;
|
||||
virtual bool AcquireChannel(NTV2Channel chan, NTV2Mode mode,
|
||||
const std::string &owner);
|
||||
virtual bool ReleaseChannel(NTV2Channel chan, NTV2Mode mode,
|
||||
const std::string &owner);
|
||||
virtual bool InputSelectionReady(IOSelection io, NTV2DeviceID id,
|
||||
const std::string &owner) const;
|
||||
virtual bool OutputSelectionReady(IOSelection io, NTV2DeviceID id,
|
||||
const std::string &owner) const;
|
||||
virtual bool AcquireInputSelection(IOSelection io, NTV2DeviceID id,
|
||||
const std::string &owner);
|
||||
virtual bool ReleaseInputSelection(IOSelection io, NTV2DeviceID id,
|
||||
const std::string &owner);
|
||||
virtual bool AcquireOutputSelection(IOSelection io, NTV2DeviceID id,
|
||||
const std::string &owner);
|
||||
virtual bool ReleaseOutputSelection(IOSelection io, NTV2DeviceID id,
|
||||
const std::string &owner);
|
||||
virtual bool UpdateChannelOwnerName(const std::string &oldName,
|
||||
const std::string &newName);
|
||||
|
||||
private:
|
||||
virtual bool isAutoCirculateRunning(NTV2Channel);
|
||||
|
||||
protected:
|
||||
uint32_t mCardIndex;
|
||||
std::string mCardID;
|
||||
std::unique_ptr<CNTV2Card> mCard;
|
||||
ChannelPwnz mChannelPwnz;
|
||||
mutable std::mutex mMutex;
|
||||
};
|
||||
using CardEntryPtr = std::shared_ptr<CardEntry>;
|
||||
using CardEntries = std::map<std::string, CardEntryPtr>;
|
||||
|
||||
/* The CardManager enumerates the physical AJA cards in the system, reverts them to a default
|
||||
* state on exit, and maintains a map of CardEntry objects corresponding to each physical card.
|
||||
* Each CardEntry object holds a pointer to the CNTV2Card instance and a map of NTV2Channels
|
||||
* that are "owned" by each plugin instance. NTV2Channels are essentially treated as indices
|
||||
* for various firwmare Widgets and sub-systems throughout the AJA NTV2 SDK.
|
||||
*/
|
||||
class CardManager {
|
||||
public:
|
||||
static CardManager &Instance();
|
||||
|
||||
void ClearCardEntries();
|
||||
void EnumerateCards();
|
||||
|
||||
size_t NumCardEntries() const;
|
||||
const CardEntryPtr GetCardEntry(const std::string &cardID) const;
|
||||
const CardEntries &GetCardEntries() const;
|
||||
|
||||
private:
|
||||
CardManager() = default;
|
||||
~CardManager() = default;
|
||||
CardManager(const CardManager &) = delete;
|
||||
CardManager(const CardManager &&) = delete;
|
||||
CardManager &operator=(const CardManager &) = delete;
|
||||
CardManager &operator=(const CardManager &&) = delete;
|
||||
|
||||
CardEntries mCardEntries;
|
||||
mutable std::mutex mMutex;
|
||||
};
|
||||
|
||||
} // aja
|
892
plugins/aja/aja-common.cpp
Normal file
892
plugins/aja/aja-common.cpp
Normal file
@ -0,0 +1,892 @@
|
||||
#include "aja-card-manager.hpp"
|
||||
#include "aja-common.hpp"
|
||||
#include "aja-ui-props.hpp"
|
||||
#include "aja-props.hpp"
|
||||
|
||||
#include <ajantv2/includes/ntv2devicescanner.h>
|
||||
#include <ajantv2/includes/ntv2devicefeatures.h>
|
||||
#include <ajantv2/includes/ntv2utils.h>
|
||||
|
||||
void filter_io_selection_input_list(const std::string &cardID,
|
||||
const std::string &channelOwner,
|
||||
obs_property_t *list)
|
||||
{
|
||||
auto &cardManager = aja::CardManager::Instance();
|
||||
|
||||
auto cardEntry = cardManager.GetCardEntry(cardID);
|
||||
if (!cardEntry) {
|
||||
blog(LOG_DEBUG,
|
||||
"filter_io_selection_input_list: Card Entry not found for %s",
|
||||
cardID.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
NTV2DeviceID deviceID = DEVICE_ID_NOTFOUND;
|
||||
CNTV2Card *card = cardEntry->GetCard();
|
||||
if (card)
|
||||
deviceID = card->GetDeviceID();
|
||||
|
||||
// Gray out the IOSelection list items that are in use by other plugin instances
|
||||
for (size_t idx = 0; idx < obs_property_list_item_count(list); idx++) {
|
||||
auto io_select = static_cast<IOSelection>(
|
||||
obs_property_list_item_int(list, idx));
|
||||
|
||||
if (io_select == IOSelection::Invalid) {
|
||||
obs_property_list_item_disable(list, idx, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool enabled = cardEntry->InputSelectionReady(
|
||||
io_select, deviceID, channelOwner);
|
||||
obs_property_list_item_disable(list, idx, !enabled);
|
||||
blog(LOG_DEBUG, "IOSelection %s = %s",
|
||||
aja::IOSelectionToString(io_select).c_str(),
|
||||
enabled ? "enabled" : "disabled");
|
||||
}
|
||||
}
|
||||
|
||||
void filter_io_selection_output_list(const std::string &cardID,
|
||||
const std::string &channelOwner,
|
||||
obs_property_t *list)
|
||||
{
|
||||
auto &cardManager = aja::CardManager::Instance();
|
||||
|
||||
auto cardEntry = cardManager.GetCardEntry(cardID);
|
||||
if (!cardEntry) {
|
||||
blog(LOG_DEBUG,
|
||||
"filter_io_selection_output_list: Card Entry not found for %s",
|
||||
cardID.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
NTV2DeviceID deviceID = DEVICE_ID_NOTFOUND;
|
||||
CNTV2Card *card = cardEntry->GetCard();
|
||||
if (card)
|
||||
deviceID = card->GetDeviceID();
|
||||
|
||||
// Gray out the IOSelection list items that are in use by other plugin instances
|
||||
for (size_t idx = 0; idx < obs_property_list_item_count(list); idx++) {
|
||||
auto io_select = static_cast<IOSelection>(
|
||||
obs_property_list_item_int(list, idx));
|
||||
if (io_select == IOSelection::Invalid) {
|
||||
obs_property_list_item_disable(list, idx, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool enabled = cardEntry->OutputSelectionReady(
|
||||
io_select, deviceID, channelOwner);
|
||||
obs_property_list_item_disable(list, idx, !enabled);
|
||||
blog(LOG_DEBUG, "IOSelection %s = %s",
|
||||
aja::IOSelectionToString(io_select).c_str(),
|
||||
enabled ? "enabled" : "disabled");
|
||||
}
|
||||
}
|
||||
|
||||
void populate_io_selection_input_list(const std::string &cardID,
|
||||
const std::string &channelOwner,
|
||||
NTV2DeviceID deviceID,
|
||||
obs_property_t *list)
|
||||
{
|
||||
obs_property_list_clear(list);
|
||||
|
||||
obs_property_list_add_int(list, obs_module_text(kUIPropIOSelect.text),
|
||||
static_cast<long long>(IOSelection::Invalid));
|
||||
|
||||
for (auto i = 0; i < static_cast<int32_t>(IOSelection::NumIOSelections);
|
||||
i++) {
|
||||
auto ioSelect = static_cast<IOSelection>(i);
|
||||
if (ioSelect == IOSelection::SDI1_2_Squares ||
|
||||
ioSelect == IOSelection::SDI3_4_Squares)
|
||||
continue;
|
||||
|
||||
if (aja::DeviceCanDoIOSelectionIn(deviceID, ioSelect)) {
|
||||
obs_property_list_add_int(
|
||||
list,
|
||||
aja::IOSelectionToString(ioSelect).c_str(),
|
||||
static_cast<long long>(ioSelect));
|
||||
}
|
||||
}
|
||||
|
||||
filter_io_selection_input_list(cardID, channelOwner, list);
|
||||
}
|
||||
|
||||
void populate_io_selection_output_list(const std::string &cardID,
|
||||
const std::string &channelOwner,
|
||||
NTV2DeviceID deviceID,
|
||||
obs_property_t *list)
|
||||
{
|
||||
obs_property_list_clear(list);
|
||||
|
||||
obs_property_list_add_int(list, obs_module_text(kUIPropIOSelect.text),
|
||||
static_cast<long long>(IOSelection::Invalid));
|
||||
|
||||
if (deviceID == DEVICE_ID_TTAP_PRO) {
|
||||
obs_property_list_add_int(
|
||||
list, "SDI & HDMI",
|
||||
static_cast<long long>(IOSelection::HDMIMonitorOut));
|
||||
} else {
|
||||
for (auto i = 0;
|
||||
i < static_cast<int32_t>(IOSelection::NumIOSelections);
|
||||
i++) {
|
||||
auto ioSelect = static_cast<IOSelection>(i);
|
||||
|
||||
if (ioSelect == IOSelection::Invalid ||
|
||||
ioSelect == IOSelection::SDI1_2_Squares ||
|
||||
ioSelect == IOSelection::SDI3_4_Squares)
|
||||
continue;
|
||||
|
||||
if (aja::DeviceCanDoIOSelectionOut(deviceID,
|
||||
ioSelect)) {
|
||||
obs_property_list_add_int(
|
||||
list,
|
||||
aja::IOSelectionToString(ioSelect)
|
||||
.c_str(),
|
||||
static_cast<long long>(ioSelect));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filter_io_selection_output_list(cardID, channelOwner, list);
|
||||
}
|
||||
|
||||
void populate_video_format_list(NTV2DeviceID deviceID, obs_property_t *list,
|
||||
NTV2VideoFormat genlockFormat)
|
||||
{
|
||||
VideoFormatList videoFormats = {};
|
||||
VideoStandardList orderedStandards = {};
|
||||
orderedStandards.push_back(NTV2_STANDARD_525);
|
||||
orderedStandards.push_back(NTV2_STANDARD_625);
|
||||
if (NTV2DeviceCanDoHDVideo(deviceID)) {
|
||||
orderedStandards.push_back(NTV2_STANDARD_720);
|
||||
orderedStandards.push_back(NTV2_STANDARD_1080);
|
||||
orderedStandards.push_back(NTV2_STANDARD_1080p);
|
||||
}
|
||||
if (NTV2DeviceCanDo2KVideo(deviceID)) {
|
||||
orderedStandards.push_back(NTV2_STANDARD_2K);
|
||||
orderedStandards.push_back(NTV2_STANDARD_2Kx1080p);
|
||||
orderedStandards.push_back(NTV2_STANDARD_2Kx1080i);
|
||||
}
|
||||
if (NTV2DeviceCanDo4KVideo(deviceID)) {
|
||||
orderedStandards.push_back(NTV2_STANDARD_3840i);
|
||||
orderedStandards.push_back(NTV2_STANDARD_3840x2160p);
|
||||
orderedStandards.push_back(NTV2_STANDARD_3840HFR);
|
||||
orderedStandards.push_back(NTV2_STANDARD_4096i);
|
||||
orderedStandards.push_back(NTV2_STANDARD_4096x2160p);
|
||||
orderedStandards.push_back(NTV2_STANDARD_4096HFR);
|
||||
}
|
||||
|
||||
aja::GetSortedVideoFormats(deviceID, orderedStandards, videoFormats);
|
||||
for (const auto &vf : videoFormats) {
|
||||
bool addFormat = true;
|
||||
|
||||
// Filter formats by framerate family if specified
|
||||
if (genlockFormat != NTV2_FORMAT_UNKNOWN)
|
||||
addFormat = IsMultiFormatCompatible(genlockFormat, vf);
|
||||
|
||||
if (addFormat) {
|
||||
std::string name = NTV2VideoFormatToString(vf, true);
|
||||
obs_property_list_add_int(list, name.c_str(), (int)vf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void populate_pixel_format_list(NTV2DeviceID deviceID, obs_property_t *list)
|
||||
{
|
||||
const NTV2PixelFormat supported_pix_fmts[] = {kDefaultAJAPixelFormat,
|
||||
NTV2_FBF_24BIT_BGR};
|
||||
|
||||
for (auto &&pf : supported_pix_fmts) {
|
||||
if (NTV2DeviceCanDoFrameBufferFormat(deviceID, pf)) {
|
||||
obs_property_list_add_int(
|
||||
list,
|
||||
NTV2FrameBufferFormatToString(pf, true).c_str(),
|
||||
static_cast<long long>(pf));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void populate_sdi_4k_transport_list(obs_property_t *list)
|
||||
{
|
||||
obs_property_list_add_int(
|
||||
list,
|
||||
aja::SDI4KTransportToString(SDI4KTransport::Squares).c_str(),
|
||||
static_cast<long long>(SDI4KTransport::Squares));
|
||||
obs_property_list_add_int(
|
||||
list,
|
||||
aja::SDI4KTransportToString(SDI4KTransport::TwoSampleInterleave)
|
||||
.c_str(),
|
||||
static_cast<long long>(SDI4KTransport::TwoSampleInterleave));
|
||||
}
|
||||
|
||||
bool aja_video_format_changed(obs_properties_t *props, obs_property_t *list,
|
||||
obs_data_t *settings)
|
||||
{
|
||||
UNUSED_PARAMETER(list);
|
||||
|
||||
auto vid_fmt = static_cast<NTV2VideoFormat>(
|
||||
obs_data_get_int(settings, kUIPropVideoFormatSelect.id));
|
||||
|
||||
obs_property_t *sdi_4k_trx =
|
||||
obs_properties_get(props, kUIPropSDI4KTransport.id);
|
||||
|
||||
obs_property_set_visible(sdi_4k_trx, NTV2_IS_4K_VIDEO_FORMAT(vid_fmt));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace aja {
|
||||
|
||||
video_format AJAPixelFormatToOBSVideoFormat(NTV2PixelFormat pf)
|
||||
{
|
||||
video_format obs_video_format = VIDEO_FORMAT_NONE;
|
||||
switch (pf) {
|
||||
case NTV2_FBF_8BIT_YCBCR:
|
||||
obs_video_format = VIDEO_FORMAT_UYVY;
|
||||
break;
|
||||
case NTV2_FBF_24BIT_RGB:
|
||||
case NTV2_FBF_24BIT_BGR:
|
||||
obs_video_format = VIDEO_FORMAT_BGR3;
|
||||
break;
|
||||
case NTV2_FBF_ARGB:
|
||||
case NTV2_FBF_ABGR:
|
||||
case NTV2_FBF_RGBA:
|
||||
obs_video_format = VIDEO_FORMAT_BGRA;
|
||||
break;
|
||||
case NTV2_FBF_10BIT_YCBCR:
|
||||
case NTV2_FBF_10BIT_RGB:
|
||||
case NTV2_FBF_8BIT_YCBCR_YUY2:
|
||||
case NTV2_FBF_10BIT_DPX:
|
||||
case NTV2_FBF_10BIT_YCBCR_DPX:
|
||||
case NTV2_FBF_8BIT_DVCPRO:
|
||||
case NTV2_FBF_8BIT_YCBCR_420PL3:
|
||||
case NTV2_FBF_8BIT_HDV:
|
||||
case NTV2_FBF_10BIT_YCBCRA:
|
||||
case NTV2_FBF_10BIT_DPX_LE:
|
||||
case NTV2_FBF_48BIT_RGB:
|
||||
case NTV2_FBF_12BIT_RGB_PACKED:
|
||||
case NTV2_FBF_PRORES_DVCPRO:
|
||||
case NTV2_FBF_PRORES_HDV:
|
||||
case NTV2_FBF_10BIT_RGB_PACKED:
|
||||
case NTV2_FBF_10BIT_ARGB:
|
||||
case NTV2_FBF_16BIT_ARGB:
|
||||
case NTV2_FBF_8BIT_YCBCR_422PL3:
|
||||
case NTV2_FBF_10BIT_RAW_RGB:
|
||||
case NTV2_FBF_10BIT_RAW_YCBCR:
|
||||
case NTV2_FBF_10BIT_YCBCR_420PL3_LE:
|
||||
case NTV2_FBF_10BIT_YCBCR_422PL3_LE:
|
||||
case NTV2_FBF_10BIT_YCBCR_420PL2:
|
||||
case NTV2_FBF_10BIT_YCBCR_422PL2:
|
||||
case NTV2_FBF_8BIT_YCBCR_420PL2:
|
||||
case NTV2_FBF_8BIT_YCBCR_422PL2:
|
||||
default:
|
||||
obs_video_format = VIDEO_FORMAT_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
return obs_video_format;
|
||||
}
|
||||
|
||||
void GetSortedVideoFormats(NTV2DeviceID id, const VideoStandardList &standards,
|
||||
VideoFormatList &videoFormats)
|
||||
{
|
||||
if (standards.empty())
|
||||
return;
|
||||
|
||||
VideoFormatMap videoFormatMap;
|
||||
|
||||
// Bin all the formats based on video standard
|
||||
for (size_t i = (size_t)NTV2_FORMAT_UNKNOWN;
|
||||
i < (size_t)NTV2_MAX_NUM_VIDEO_FORMATS; i++) {
|
||||
NTV2VideoFormat fmt = (NTV2VideoFormat)i;
|
||||
NTV2Standard standard = GetNTV2StandardFromVideoFormat(fmt);
|
||||
|
||||
bool addFmt = true;
|
||||
|
||||
if (id != DEVICE_ID_NOTFOUND) {
|
||||
addFmt = NTV2DeviceCanDoVideoFormat(id, fmt);
|
||||
}
|
||||
|
||||
if (addFmt) {
|
||||
if (videoFormatMap.count(standard)) {
|
||||
videoFormatMap.at(standard).push_back(fmt);
|
||||
} else {
|
||||
std::vector<NTV2VideoFormat> v;
|
||||
|
||||
v.push_back(fmt);
|
||||
|
||||
videoFormatMap.insert(
|
||||
std::pair<NTV2Standard,
|
||||
std::vector<NTV2VideoFormat>>(
|
||||
standard, v));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t v = (size_t)NTV2_STANDARD_1080;
|
||||
v < (size_t)NTV2_NUM_STANDARDS; v++) {
|
||||
NTV2Standard standard = (NTV2Standard)v;
|
||||
|
||||
if (videoFormatMap.count(standard)) {
|
||||
std::sort(videoFormatMap.at(standard).begin(),
|
||||
videoFormatMap.at(standard).end(),
|
||||
[&](const NTV2VideoFormat &d1,
|
||||
const NTV2VideoFormat &d2) {
|
||||
std::string d1Str, d2Str;
|
||||
|
||||
d1Str = NTV2VideoFormatToString(d1);
|
||||
d2Str = NTV2VideoFormatToString(d2);
|
||||
|
||||
return d1Str < d2Str;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t v = 0; v < standards.size(); v++) {
|
||||
NTV2Standard standard = standards.at(v);
|
||||
if (videoFormatMap.count(standard)) {
|
||||
for (size_t i = 0;
|
||||
i < videoFormatMap.at(standard).size(); i++) {
|
||||
NTV2VideoFormat vf =
|
||||
videoFormatMap.at(standard).at(i);
|
||||
videoFormats.push_back(vf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t CardNumFramestores(NTV2DeviceID id)
|
||||
{
|
||||
auto numFramestores = NTV2DeviceGetNumFrameStores(id);
|
||||
if (id == DEVICE_ID_CORVIDHBR) {
|
||||
numFramestores = 1;
|
||||
}
|
||||
return numFramestores;
|
||||
}
|
||||
|
||||
uint32_t CardNumAudioSystems(NTV2DeviceID id)
|
||||
{
|
||||
if (id == DEVICE_ID_KONALHI || id == DEVICE_ID_KONALHEPLUS)
|
||||
return 2;
|
||||
|
||||
return NTV2DeviceGetNumAudioSystems(id);
|
||||
}
|
||||
|
||||
// IO4K and IO4K+ perform SDI Monitor Output on "SDI5" and "Framestore 4".
|
||||
bool CardCanDoSDIMonitorOutput(NTV2DeviceID id)
|
||||
{
|
||||
return (id == DEVICE_ID_IO4K || id == DEVICE_ID_IO4KPLUS);
|
||||
}
|
||||
|
||||
// Cards with a dedicated HDMI Monitor Output tie it to "Framestore 4".
|
||||
bool CardCanDoHDMIMonitorOutput(NTV2DeviceID id)
|
||||
{
|
||||
return (id == DEVICE_ID_IO4K || id == DEVICE_ID_IO4KPLUS ||
|
||||
id == DEVICE_ID_IOXT || id == DEVICE_ID_IOX3 ||
|
||||
id == DEVICE_ID_KONA4 || id == DEVICE_ID_KONA5 ||
|
||||
id == DEVICE_ID_KONA5_8K || id == DEVICE_ID_KONA5_2X4K ||
|
||||
id == DEVICE_ID_KONA5_8KMK);
|
||||
}
|
||||
|
||||
// Cards capable of 1x SDI at 6G/12G.
|
||||
bool CardCanDo1xSDI12G(NTV2DeviceID id)
|
||||
{
|
||||
return (id == DEVICE_ID_KONA5_8K || id == DEVICE_ID_KONA5_8KMK ||
|
||||
id == DEVICE_ID_KONA5 || id == DEVICE_ID_KONA5_2X4K ||
|
||||
id == DEVICE_ID_IO4KPLUS || id == DEVICE_ID_CORVID44_12G);
|
||||
}
|
||||
|
||||
// Check for 3G level-B SDI on the wire.
|
||||
bool Is3GLevelB(CNTV2Card *card, NTV2Channel channel)
|
||||
{
|
||||
if (!card)
|
||||
return false;
|
||||
|
||||
bool levelB = false;
|
||||
auto deviceID = card->GetDeviceID();
|
||||
UWord channelIndex = static_cast<UWord>(channel);
|
||||
|
||||
if (NTV2DeviceCanDo3GIn(deviceID, channelIndex) ||
|
||||
NTV2DeviceCanDo12GIn(deviceID, channelIndex)) {
|
||||
if (!card->GetSDIInput3GbPresent(levelB, channel))
|
||||
return false;
|
||||
}
|
||||
|
||||
return levelB;
|
||||
}
|
||||
|
||||
// Get the 3G Level-A enum for a 3G Level-B format enum.
|
||||
NTV2VideoFormat GetLevelAFormatForLevelBFormat(NTV2VideoFormat vf)
|
||||
{
|
||||
NTV2VideoFormat result = vf;
|
||||
switch (vf) {
|
||||
default:
|
||||
break;
|
||||
case NTV2_FORMAT_1080p_5000_B:
|
||||
result = NTV2_FORMAT_1080p_5000_A;
|
||||
break;
|
||||
case NTV2_FORMAT_1080p_5994_B:
|
||||
result = NTV2_FORMAT_1080p_5994_A;
|
||||
break;
|
||||
case NTV2_FORMAT_1080p_6000_B:
|
||||
result = NTV2_FORMAT_1080p_6000_A;
|
||||
break;
|
||||
case NTV2_FORMAT_1080p_2K_4795_B:
|
||||
result = NTV2_FORMAT_1080p_2K_4795_A;
|
||||
break;
|
||||
case NTV2_FORMAT_1080p_2K_4800_B:
|
||||
result = NTV2_FORMAT_1080p_2K_4800_A;
|
||||
break;
|
||||
case NTV2_FORMAT_1080p_2K_5000_B:
|
||||
result = NTV2_FORMAT_1080p_2K_5000_A;
|
||||
break;
|
||||
case NTV2_FORMAT_1080p_2K_5994_B:
|
||||
result = NTV2_FORMAT_1080p_2K_5994_A;
|
||||
break;
|
||||
case NTV2_FORMAT_1080p_2K_6000_B:
|
||||
result = NTV2_FORMAT_1080p_2K_6000_A;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
NTV2VideoFormat InterlacedFormatForPsfFormat(NTV2VideoFormat vf)
|
||||
{
|
||||
NTV2VideoFormat result = vf;
|
||||
switch (vf) {
|
||||
default:
|
||||
break;
|
||||
case NTV2_FORMAT_1080psf_2500_2:
|
||||
result = NTV2_FORMAT_1080i_5000;
|
||||
break;
|
||||
case NTV2_FORMAT_1080psf_2997_2:
|
||||
result = NTV2_FORMAT_1080i_5994;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Certain cards only have 1 SDI spigot.
|
||||
bool IsSingleSDIDevice(NTV2DeviceID id)
|
||||
{
|
||||
return (id == DEVICE_ID_TTAP_PRO || id == DEVICE_ID_KONA1);
|
||||
}
|
||||
|
||||
bool IsIODevice(NTV2DeviceID id)
|
||||
{
|
||||
return (id == DEVICE_ID_IOXT || id == DEVICE_ID_IOX3 ||
|
||||
id == DEVICE_ID_IO4K || id == DEVICE_ID_IO4KPLUS ||
|
||||
id == DEVICE_ID_IOIP_2022 || id == DEVICE_ID_IOIP_2110);
|
||||
}
|
||||
|
||||
bool IsRetailSDI12G(NTV2DeviceID id)
|
||||
{
|
||||
return (id == DEVICE_ID_KONA5 || id == DEVICE_ID_IO4KPLUS);
|
||||
}
|
||||
|
||||
bool IsOutputOnlyDevice(NTV2DeviceID id)
|
||||
{
|
||||
return id == DEVICE_ID_TTAP_PRO;
|
||||
}
|
||||
|
||||
std::string SDI4KTransportToString(SDI4KTransport mode)
|
||||
{
|
||||
std::string str = "";
|
||||
switch (mode) {
|
||||
case SDI4KTransport::Squares:
|
||||
str = "Squares";
|
||||
break;
|
||||
case SDI4KTransport::TwoSampleInterleave:
|
||||
str = "2SI";
|
||||
break;
|
||||
default:
|
||||
case SDI4KTransport::Unknown:
|
||||
str = "Unknown";
|
||||
break;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string IOSelectionToString(IOSelection io)
|
||||
{
|
||||
std::string str;
|
||||
|
||||
switch (io) {
|
||||
case IOSelection::SDI1:
|
||||
str = "SDI 1";
|
||||
break;
|
||||
case IOSelection::SDI2:
|
||||
str = "SDI 2";
|
||||
break;
|
||||
case IOSelection::SDI3:
|
||||
str = "SDI 3";
|
||||
break;
|
||||
case IOSelection::SDI4:
|
||||
str = "SDI 4";
|
||||
break;
|
||||
case IOSelection::SDI5:
|
||||
str = "SDI 5";
|
||||
break;
|
||||
case IOSelection::SDI6:
|
||||
str = "SDI 6";
|
||||
break;
|
||||
case IOSelection::SDI7:
|
||||
str = "SDI 7";
|
||||
break;
|
||||
case IOSelection::SDI8:
|
||||
str = "SDI 8";
|
||||
break;
|
||||
case IOSelection::SDI1_2:
|
||||
str = "SDI 1 & 2";
|
||||
break;
|
||||
case IOSelection::SDI1_2_Squares:
|
||||
str = "SDI 1 & 2 (4K Squares)";
|
||||
break;
|
||||
case IOSelection::SDI3_4:
|
||||
str = "SDI 3 & 4";
|
||||
break;
|
||||
case IOSelection::SDI3_4_Squares:
|
||||
str = "SDI 3 & 4 (4K Squares)";
|
||||
break;
|
||||
case IOSelection::SDI5_6:
|
||||
str = "SDI 5 & 6";
|
||||
break;
|
||||
case IOSelection::SDI7_8:
|
||||
str = "SDI 7 & 8";
|
||||
break;
|
||||
case IOSelection::SDI1__4:
|
||||
str = "SDI 1-4";
|
||||
break;
|
||||
case IOSelection::SDI5__8:
|
||||
str = "SDI 5-8";
|
||||
break;
|
||||
case IOSelection::HDMI1:
|
||||
str = "HDMI 1";
|
||||
break;
|
||||
case IOSelection::HDMI2:
|
||||
str = "HDMI 2";
|
||||
break;
|
||||
case IOSelection::HDMI3:
|
||||
str = "HDMI 3";
|
||||
break;
|
||||
case IOSelection::HDMI4:
|
||||
str = "HDMI 4";
|
||||
break;
|
||||
case IOSelection::HDMIMonitorIn:
|
||||
str = "HDMI Monitor In";
|
||||
break;
|
||||
case IOSelection::HDMIMonitorOut:
|
||||
str = "HDMI Monitor Out";
|
||||
break;
|
||||
case IOSelection::AnalogIn:
|
||||
str = "Analog In";
|
||||
break;
|
||||
case IOSelection::AnalogOut:
|
||||
str = "Analog Out";
|
||||
break;
|
||||
case IOSelection::Invalid:
|
||||
str = "Invalid";
|
||||
break;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
void IOSelectionToInputSources(IOSelection io, NTV2InputSourceSet &inputSources)
|
||||
{
|
||||
switch (io) {
|
||||
case IOSelection::SDI1:
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI1);
|
||||
break;
|
||||
case IOSelection::SDI2:
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI2);
|
||||
break;
|
||||
case IOSelection::SDI3:
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI3);
|
||||
break;
|
||||
case IOSelection::SDI4:
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI4);
|
||||
break;
|
||||
case IOSelection::SDI5:
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI5);
|
||||
break;
|
||||
case IOSelection::SDI6:
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI6);
|
||||
break;
|
||||
case IOSelection::SDI7:
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI7);
|
||||
break;
|
||||
case IOSelection::SDI8:
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI8);
|
||||
break;
|
||||
case IOSelection::SDI1_2:
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI1);
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI2);
|
||||
break;
|
||||
case IOSelection::SDI1_2_Squares:
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI1);
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI2);
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI3);
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI4);
|
||||
break;
|
||||
case IOSelection::SDI3_4:
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI3);
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI4);
|
||||
break;
|
||||
case IOSelection::SDI3_4_Squares:
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI1);
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI2);
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI3);
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI4);
|
||||
break;
|
||||
case IOSelection::SDI5_6:
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI5);
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI6);
|
||||
break;
|
||||
case IOSelection::SDI7_8:
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI7);
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI8);
|
||||
break;
|
||||
case IOSelection::SDI1__4:
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI1);
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI2);
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI3);
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI4);
|
||||
break;
|
||||
case IOSelection::SDI5__8:
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI5);
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI6);
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI7);
|
||||
inputSources.insert(NTV2_INPUTSOURCE_SDI8);
|
||||
break;
|
||||
case IOSelection::HDMI1:
|
||||
inputSources.insert(NTV2_INPUTSOURCE_HDMI1);
|
||||
break;
|
||||
case IOSelection::HDMI2:
|
||||
inputSources.insert(NTV2_INPUTSOURCE_HDMI2);
|
||||
break;
|
||||
case IOSelection::HDMI3:
|
||||
inputSources.insert(NTV2_INPUTSOURCE_HDMI3);
|
||||
break;
|
||||
case IOSelection::HDMI4:
|
||||
inputSources.insert(NTV2_INPUTSOURCE_HDMI4);
|
||||
break;
|
||||
case IOSelection::HDMIMonitorIn:
|
||||
inputSources.insert(NTV2_INPUTSOURCE_HDMI1);
|
||||
break;
|
||||
case IOSelection::AnalogIn:
|
||||
inputSources.insert(NTV2_INPUTSOURCE_ANALOG1);
|
||||
break;
|
||||
default:
|
||||
case IOSelection::HDMIMonitorOut:
|
||||
case IOSelection::AnalogOut:
|
||||
case IOSelection::Invalid:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void IOSelectionToOutputDests(IOSelection io,
|
||||
NTV2OutputDestinations &outputDests)
|
||||
{
|
||||
switch (io) {
|
||||
case IOSelection::SDI1:
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI1);
|
||||
break;
|
||||
case IOSelection::SDI2:
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI2);
|
||||
break;
|
||||
case IOSelection::SDI3:
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI3);
|
||||
break;
|
||||
case IOSelection::SDI4:
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI4);
|
||||
break;
|
||||
case IOSelection::SDI5:
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI5);
|
||||
break;
|
||||
case IOSelection::SDI6:
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI6);
|
||||
break;
|
||||
case IOSelection::SDI7:
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI7);
|
||||
break;
|
||||
case IOSelection::SDI8:
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI8);
|
||||
break;
|
||||
case IOSelection::SDI1_2:
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI1);
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI2);
|
||||
break;
|
||||
// Requires 4x framestores and 2x SDI spigots
|
||||
case IOSelection::SDI1_2_Squares:
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI1);
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI2);
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI3);
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI4);
|
||||
break;
|
||||
case IOSelection::SDI3_4:
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI3);
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI4);
|
||||
break;
|
||||
// Requires 4x framestores and 2x SDI spigots
|
||||
case IOSelection::SDI3_4_Squares:
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI1);
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI2);
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI3);
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI4);
|
||||
break;
|
||||
case IOSelection::SDI5_6:
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI5);
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI6);
|
||||
break;
|
||||
case IOSelection::SDI7_8:
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI7);
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI8);
|
||||
break;
|
||||
case IOSelection::SDI1__4:
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI1);
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI2);
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI3);
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI4);
|
||||
break;
|
||||
case IOSelection::SDI5__8:
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI5);
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI6);
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI7);
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_SDI8);
|
||||
break;
|
||||
case IOSelection::HDMIMonitorOut:
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_HDMI);
|
||||
break;
|
||||
case IOSelection::AnalogOut:
|
||||
outputDests.insert(NTV2_OUTPUTDESTINATION_ANALOG);
|
||||
break;
|
||||
default:
|
||||
case IOSelection::HDMI1:
|
||||
case IOSelection::HDMI2:
|
||||
case IOSelection::HDMI3:
|
||||
case IOSelection::HDMI4:
|
||||
case IOSelection::HDMIMonitorIn:
|
||||
case IOSelection::AnalogIn:
|
||||
case IOSelection::Invalid:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool DeviceCanDoIOSelectionIn(NTV2DeviceID id, IOSelection io)
|
||||
{
|
||||
NTV2InputSourceSet inputSources;
|
||||
if (io != IOSelection::Invalid) {
|
||||
IOSelectionToInputSources(io, inputSources);
|
||||
size_t numSrcs = inputSources.size();
|
||||
size_t canDo = 0;
|
||||
if (numSrcs > 0) {
|
||||
for (auto &&inp : inputSources) {
|
||||
if (NTV2DeviceCanDoInputSource(id, inp))
|
||||
canDo++;
|
||||
}
|
||||
|
||||
if (canDo == numSrcs)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DeviceCanDoIOSelectionOut(NTV2DeviceID id, IOSelection io)
|
||||
{
|
||||
NTV2OutputDestinations outputDests;
|
||||
if (io != IOSelection::Invalid) {
|
||||
IOSelectionToOutputDests(io, outputDests);
|
||||
size_t numOuts = outputDests.size();
|
||||
size_t canDo = 0;
|
||||
if (numOuts > 0) {
|
||||
for (auto &&out : outputDests) {
|
||||
if (NTV2DeviceCanDoOutputDestination(id, out))
|
||||
canDo++;
|
||||
}
|
||||
|
||||
if (canDo == numOuts)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsSDIOneWireIOSelection(IOSelection io)
|
||||
{
|
||||
bool result = false;
|
||||
switch (io) {
|
||||
case IOSelection::SDI1:
|
||||
case IOSelection::SDI2:
|
||||
case IOSelection::SDI3:
|
||||
case IOSelection::SDI4:
|
||||
case IOSelection::SDI5:
|
||||
case IOSelection::SDI6:
|
||||
case IOSelection::SDI7:
|
||||
case IOSelection::SDI8:
|
||||
result = true;
|
||||
break;
|
||||
default:
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool IsSDITwoWireIOSelection(IOSelection io)
|
||||
{
|
||||
bool result = false;
|
||||
switch (io) {
|
||||
case IOSelection::SDI1_2:
|
||||
case IOSelection::SDI1_2_Squares:
|
||||
case IOSelection::SDI3_4:
|
||||
case IOSelection::SDI3_4_Squares:
|
||||
case IOSelection::SDI5_6:
|
||||
case IOSelection::SDI7_8:
|
||||
result = true;
|
||||
break;
|
||||
default:
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool IsSDIFourWireIOSelection(IOSelection io)
|
||||
{
|
||||
bool result = false;
|
||||
switch (io) {
|
||||
case IOSelection::SDI1__4:
|
||||
case IOSelection::SDI5__8:
|
||||
result = true;
|
||||
break;
|
||||
default:
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool IsMonitorOutputSelection(NTV2DeviceID id, IOSelection io)
|
||||
{
|
||||
if (CardCanDoSDIMonitorOutput(id) && io == IOSelection::SDI5)
|
||||
return true;
|
||||
|
||||
if (CardCanDoHDMIMonitorOutput(id) && io == IOSelection::HDMIMonitorOut)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string MakeCardID(CNTV2Card &card)
|
||||
{
|
||||
std::string cardID;
|
||||
if (card.GetSerialNumberString(cardID)) {
|
||||
// Try to construct CardID from device ID and serial number...
|
||||
cardID = NTV2DeviceIDToString(card.GetDeviceID(), false) + "_" +
|
||||
cardID;
|
||||
} else {
|
||||
// ...otherwise fall back to the CNTV2DeviceScanner method.
|
||||
cardID = CNTV2DeviceScanner::GetDeviceRefName(card);
|
||||
}
|
||||
return cardID;
|
||||
}
|
||||
|
||||
} // aja
|
92
plugins/aja/aja-common.hpp
Normal file
92
plugins/aja/aja-common.hpp
Normal file
@ -0,0 +1,92 @@
|
||||
#include "aja-enums.hpp"
|
||||
|
||||
#include <obs-module.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <ajantv2/includes/ntv2enums.h>
|
||||
#include <ajantv2/includes/ntv2card.h>
|
||||
|
||||
using VideoFormatMap = std::map<NTV2Standard, std::vector<NTV2VideoFormat>>;
|
||||
using VideoFormatList = std::vector<NTV2VideoFormat>;
|
||||
using VideoStandardList = std::vector<NTV2Standard>;
|
||||
|
||||
static const uint32_t kDefaultAudioChannels = 8;
|
||||
static const uint32_t kDefaultAudioSampleRate = 48000;
|
||||
static const uint32_t kDefaultAudioSampleSize = 4;
|
||||
static const int kVideoFormatAuto = -1;
|
||||
static const int kPixelFormatAuto = -1;
|
||||
static const NTV2PixelFormat kDefaultAJAPixelFormat = NTV2_FBF_8BIT_YCBCR;
|
||||
|
||||
// Common OBS property helpers used by both the capture and output plugins
|
||||
extern void filter_io_selection_input_list(const std::string &cardID,
|
||||
const std::string &channelOwner,
|
||||
obs_property_t *list);
|
||||
extern void filter_io_selection_output_list(const std::string &cardID,
|
||||
const std::string &channelOwner,
|
||||
obs_property_t *list);
|
||||
extern void populate_io_selection_input_list(const std::string &cardID,
|
||||
const std::string &channelOwner,
|
||||
NTV2DeviceID deviceID,
|
||||
obs_property_t *list);
|
||||
extern void populate_io_selection_output_list(const std::string &cardID,
|
||||
const std::string &channelOwner,
|
||||
NTV2DeviceID deviceID,
|
||||
obs_property_t *list);
|
||||
extern void
|
||||
populate_video_format_list(NTV2DeviceID deviceID, obs_property_t *list,
|
||||
NTV2VideoFormat genlockFormat = NTV2_FORMAT_UNKNOWN);
|
||||
extern void populate_pixel_format_list(NTV2DeviceID deviceID,
|
||||
obs_property_t *list);
|
||||
extern void populate_sdi_4k_transport_list(obs_property_t *list);
|
||||
extern bool aja_video_format_changed(obs_properties_t *props,
|
||||
obs_property_t *list,
|
||||
obs_data_t *settings);
|
||||
|
||||
// Additional helpers for AJA channel and signal routing configuration not found in the NTV2 SDK
|
||||
namespace aja {
|
||||
|
||||
template<typename T> bool vec_contains(const std::vector<T> &vec, const T &elem)
|
||||
{
|
||||
return std::find(vec.begin(), vec.end(), elem) != vec.end();
|
||||
}
|
||||
|
||||
extern video_format AJAPixelFormatToOBSVideoFormat(NTV2PixelFormat pf);
|
||||
|
||||
extern void GetSortedVideoFormats(NTV2DeviceID id,
|
||||
const VideoStandardList &standards,
|
||||
VideoFormatList &videoFormats);
|
||||
|
||||
extern uint32_t CardNumFramestores(NTV2DeviceID id);
|
||||
extern uint32_t CardNumAudioSystems(NTV2DeviceID id);
|
||||
extern bool CardCanDoSDIMonitorOutput(NTV2DeviceID id);
|
||||
extern bool CardCanDoHDMIMonitorOutput(NTV2DeviceID id);
|
||||
extern bool CardCanDo1xSDI12G(NTV2DeviceID id);
|
||||
extern bool Is3GLevelB(CNTV2Card *card, NTV2Channel channel);
|
||||
extern NTV2VideoFormat GetLevelAFormatForLevelBFormat(NTV2VideoFormat vf);
|
||||
extern NTV2VideoFormat InterlacedFormatForPsfFormat(NTV2VideoFormat vf);
|
||||
extern bool IsSingleSDIDevice(NTV2DeviceID id);
|
||||
extern bool IsIODevice(NTV2DeviceID id);
|
||||
extern bool IsRetailSDI12G(NTV2DeviceID id);
|
||||
extern bool IsOutputOnlyDevice(NTV2DeviceID id);
|
||||
|
||||
extern std::string SDI4KTransportToString(SDI4KTransport mode);
|
||||
|
||||
extern std::string IOSelectionToString(IOSelection io);
|
||||
extern void IOSelectionToInputSources(IOSelection io,
|
||||
NTV2InputSourceSet &inputSources);
|
||||
extern void IOSelectionToOutputDests(IOSelection io,
|
||||
NTV2OutputDestinations &outputDests);
|
||||
extern bool DeviceCanDoIOSelectionIn(NTV2DeviceID id, IOSelection io);
|
||||
extern bool DeviceCanDoIOSelectionOut(NTV2DeviceID id, IOSelection io);
|
||||
extern bool IsSDIOneWireIOSelection(IOSelection io);
|
||||
extern bool IsSDITwoWireIOSelection(IOSelection io);
|
||||
extern bool IsSDIFourWireIOSelection(IOSelection io);
|
||||
extern bool IsMonitorOutputSelection(NTV2DeviceID id, IOSelection io);
|
||||
|
||||
extern std::string MakeCardID(CNTV2Card &card);
|
||||
|
||||
} // aja
|
88
plugins/aja/aja-enums.hpp
Normal file
88
plugins/aja/aja-enums.hpp
Normal file
@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
// Additional enums used throughout the AJA plugins for signal routing configuration.
|
||||
|
||||
enum class IOSelection {
|
||||
SDI1 = 0,
|
||||
SDI2 = 1,
|
||||
SDI3 = 2,
|
||||
SDI4 = 3,
|
||||
SDI5 = 4,
|
||||
SDI6 = 5,
|
||||
SDI7 = 6,
|
||||
SDI8 = 7,
|
||||
SDI1_2 = 8,
|
||||
// special case for 2xSDI 4K Squares (requires 4x framestores)
|
||||
SDI1_2_Squares = 9,
|
||||
SDI3_4 = 10,
|
||||
// special case for 2xSDI 4K Squares (requires 4x framestores)
|
||||
SDI3_4_Squares = 11,
|
||||
SDI5_6 = 12,
|
||||
SDI7_8 = 13,
|
||||
SDI1__4 = 14,
|
||||
SDI5__8 = 15,
|
||||
HDMI1 = 16,
|
||||
HDMI2 = 17,
|
||||
HDMI3 = 18,
|
||||
HDMI4 = 19,
|
||||
HDMIMonitorIn = 20,
|
||||
HDMIMonitorOut = 21,
|
||||
AnalogIn = 22,
|
||||
AnalogOut = 23,
|
||||
Invalid = 24,
|
||||
NumIOSelections = Invalid
|
||||
};
|
||||
|
||||
enum class SDI4KTransport { Squares = 0, TwoSampleInterleave = 1, Unknown = 2 };
|
||||
|
||||
enum class RasterDefinition {
|
||||
SD = 0,
|
||||
HD = 1,
|
||||
UHD_4K = 2,
|
||||
UHD_4K_Retail_12G = 3,
|
||||
UHD2_8K = 4,
|
||||
Unknown = 5
|
||||
};
|
||||
|
||||
enum class SDIWireFormat {
|
||||
SD_ST352 = 0,
|
||||
HD_720p_ST292 = 1,
|
||||
HD_1080_ST292 = 2,
|
||||
HD_1080_ST372_Dual = 3,
|
||||
HD_720p_ST425_3Ga = 4,
|
||||
HD_1080p_ST425_3Ga = 5,
|
||||
HD_1080p_ST425_3Gb_DL = 6,
|
||||
HD_720p_ST425_3Gb = 7,
|
||||
HD_1080p_ST425_3Gb = 8,
|
||||
HD_1080p_ST425_Dual_3Ga = 9,
|
||||
HD_1080p_ST425_Dual_3Gb = 10,
|
||||
UHD4K_ST292_Dual_1_5_Squares = 11,
|
||||
UHD4K_ST292_Quad_1_5_Squares = 12,
|
||||
UHD4K_ST425_Quad_3Ga_Squares = 13,
|
||||
UHD4K_ST425_Quad_3Gb_Squares = 14,
|
||||
UHD4K_ST425_Dual_3Gb_2SI = 15,
|
||||
UHD4K_ST425_Quad_3Ga_2SI = 16,
|
||||
UHD4K_ST425_Quad_3Gb_2SI = 17,
|
||||
UHD4K_ST2018_6G_Squares_2SI = 18,
|
||||
UHD4K_ST2018_6G_Squares_2SI_Kona5_io4KPlus = 19,
|
||||
UHD4K_ST2018_12G_Squares_2SI = 20,
|
||||
UHD4K_ST2018_12G_Squares_2SI_Kona5_io4KPlus = 21,
|
||||
UHD28K_ST2082_Dual_12G = 22,
|
||||
UHD28K_ST2082_RGB_Dual_12G = 23,
|
||||
UHD28K_ST2082_Quad_12G = 24,
|
||||
Unknown = 25,
|
||||
};
|
||||
|
||||
enum class HDMIWireFormat {
|
||||
SD_HDMI = 0,
|
||||
HD_YCBCR_LFR = 1,
|
||||
HD_YCBCR_HFR = 2,
|
||||
HD_RGB_LFR = 3,
|
||||
HD_RGB_HFR = 4,
|
||||
UHD_4K_YCBCR_LFR = 5,
|
||||
UHD_4K_YCBCR_HFR = 6,
|
||||
UHD_4K_RGB_LFR = 7,
|
||||
UHD_4K_RGB_HFR = 8,
|
||||
TTAP_PRO = 9,
|
||||
Unknown = 10
|
||||
};
|
1267
plugins/aja/aja-output.cpp
Normal file
1267
plugins/aja/aja-output.cpp
Normal file
File diff suppressed because it is too large
Load Diff
160
plugins/aja/aja-output.hpp
Normal file
160
plugins/aja/aja-output.hpp
Normal file
@ -0,0 +1,160 @@
|
||||
#pragma once
|
||||
|
||||
#include "aja-props.hpp"
|
||||
|
||||
#include <ajantv2/includes/ntv2testpatterngen.h>
|
||||
|
||||
#include <ajabase/common/types.h>
|
||||
#include <ajabase/system/thread.h>
|
||||
|
||||
// #define AJA_WRITE_DEBUG_WAV
|
||||
#ifdef AJA_WRITE_DEBUG_WAV
|
||||
#include <ajabase/common/wavewriter.h>
|
||||
#endif
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
struct VideoFrame {
|
||||
struct video_data frame;
|
||||
int64_t frameNum;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct AudioFrames {
|
||||
struct audio_data frames;
|
||||
size_t offset;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
//TODO(paulh): Refactor me into OutputProps
|
||||
struct FrameTimes {
|
||||
double obsFps;
|
||||
uint64_t obsFrameTime;
|
||||
double cardFps;
|
||||
uint64_t cardFrameTime;
|
||||
};
|
||||
|
||||
using VideoQueue = std::deque<VideoFrame>;
|
||||
using AudioQueue = std::deque<AudioFrames>;
|
||||
|
||||
class CNTV2Card; // forward decl
|
||||
|
||||
class AJAOutput {
|
||||
public:
|
||||
enum {
|
||||
// min queue sizes computed in AJAOutput
|
||||
kVideoQueueMaxSize = 15,
|
||||
kAudioQueueMaxSize =
|
||||
96, // ~(48000 / 1024 samples per audio_frame) * 2sec
|
||||
};
|
||||
|
||||
AJAOutput(CNTV2Card *card, const std::string &cardID,
|
||||
const std::string &outputID, UWord deviceIndex,
|
||||
const NTV2DeviceID deviceID);
|
||||
|
||||
~AJAOutput();
|
||||
|
||||
CNTV2Card *GetCard();
|
||||
|
||||
void Initialize(const OutputProps &props);
|
||||
|
||||
void SetOBSOutput(obs_output_t *output);
|
||||
obs_output_t *GetOBSOutput();
|
||||
|
||||
void SetOutputProps(const OutputProps &props);
|
||||
OutputProps GetOutputProps() const;
|
||||
|
||||
void GenerateTestPattern(NTV2VideoFormat vf, NTV2PixelFormat pf,
|
||||
NTV2TestPatternSelect pattern);
|
||||
|
||||
void QueueVideoFrame(struct video_data *frame, size_t size);
|
||||
void QueueAudioFrames(struct audio_data *frames, size_t size);
|
||||
void ClearVideoQueue();
|
||||
void ClearAudioQueue();
|
||||
size_t VideoQueueSize();
|
||||
size_t AudioQueueSize();
|
||||
|
||||
bool HaveEnoughAudio(size_t needAudioSize);
|
||||
void DMAAudioFromQueue(NTV2AudioSystem audioSys);
|
||||
void DMAVideoFromQueue();
|
||||
|
||||
void CreateThread(bool enable = false);
|
||||
void StopThread();
|
||||
bool ThreadRunning();
|
||||
static void OutputThread(AJAThread *thread, void *ctx);
|
||||
|
||||
std::string mCardID;
|
||||
std::string mOutputID;
|
||||
UWord mDeviceIndex;
|
||||
NTV2DeviceID mDeviceID;
|
||||
|
||||
FrameTimes mFrameTimes;
|
||||
|
||||
uint32_t mAudioPlayCursor;
|
||||
uint32_t mAudioWriteCursor;
|
||||
uint32_t mAudioWrapAddress;
|
||||
uint32_t mAudioRate;
|
||||
|
||||
uint64_t mAudioQueueSamples;
|
||||
uint64_t mAudioWriteSamples;
|
||||
uint64_t mAudioPlaySamples;
|
||||
|
||||
uint32_t mNumCardFrames;
|
||||
uint32_t mFirstCardFrame;
|
||||
uint32_t mLastCardFrame;
|
||||
uint32_t mWriteCardFrame;
|
||||
uint32_t mPlayCardFrame;
|
||||
uint32_t mPlayCardNext;
|
||||
uint32_t mFrameRateNum;
|
||||
uint32_t mFrameRateDen;
|
||||
|
||||
uint64_t mVideoQueueFrames;
|
||||
uint64_t mVideoWriteFrames;
|
||||
uint64_t mVideoPlayFrames;
|
||||
|
||||
uint64_t mFirstVideoTS;
|
||||
uint64_t mFirstAudioTS;
|
||||
uint64_t mLastVideoTS;
|
||||
uint64_t mLastAudioTS;
|
||||
|
||||
int64_t mVideoDelay;
|
||||
int64_t mAudioDelay;
|
||||
int64_t mAudioVideoSync;
|
||||
int64_t mAudioAdjust;
|
||||
int64_t mLastStatTime;
|
||||
#ifdef AJA_WRITE_DEBUG_WAV
|
||||
AJAWavWriter *mWaveWriter;
|
||||
#endif
|
||||
|
||||
private:
|
||||
void calculate_card_frame_indices(uint32_t numFrames, NTV2DeviceID id,
|
||||
NTV2Channel channel,
|
||||
NTV2VideoFormat vf,
|
||||
NTV2PixelFormat pf);
|
||||
|
||||
uint32_t get_frame_count();
|
||||
|
||||
void dma_audio_samples(NTV2AudioSystem audioSys, uint32_t *data,
|
||||
size_t size);
|
||||
|
||||
CNTV2Card *mCard;
|
||||
|
||||
OutputProps mOutputProps;
|
||||
|
||||
NTV2TestPatternBuffer mTestPattern;
|
||||
|
||||
bool mIsRunning;
|
||||
bool mAudioStarted;
|
||||
|
||||
AJAThread mRunThread;
|
||||
mutable std::mutex mVideoLock;
|
||||
mutable std::mutex mAudioLock;
|
||||
mutable std::mutex mRunThreadLock;
|
||||
|
||||
std::unique_ptr<VideoQueue> mVideoQueue;
|
||||
std::unique_ptr<AudioQueue> mAudioQueue;
|
||||
|
||||
obs_output_t *mOBSOutput;
|
||||
};
|
436
plugins/aja/aja-props.cpp
Normal file
436
plugins/aja/aja-props.cpp
Normal file
@ -0,0 +1,436 @@
|
||||
#include "aja-props.hpp"
|
||||
|
||||
#include <ajantv2/includes/ntv2devicefeatures.h>
|
||||
#include <ajantv2/includes/ntv2utils.h>
|
||||
#include <ajantv2/includes/ntv2vpid.h>
|
||||
|
||||
VPIDData::VPIDData()
|
||||
: mVpidA{0},
|
||||
mVpidB{0},
|
||||
mStandardA{VPIDStandard_Unknown},
|
||||
mStandardB{VPIDStandard_Unknown},
|
||||
mSamplingA{VPIDSampling_XYZ_444},
|
||||
mSamplingB{VPIDSampling_XYZ_444}
|
||||
{
|
||||
}
|
||||
|
||||
VPIDData::VPIDData(ULWord vpidA, ULWord vpidB)
|
||||
: mVpidA{vpidA},
|
||||
mVpidB{vpidB},
|
||||
mStandardA{VPIDStandard_Unknown},
|
||||
mStandardB{VPIDStandard_Unknown},
|
||||
mSamplingA{VPIDSampling_XYZ_444},
|
||||
mSamplingB{VPIDSampling_XYZ_444}
|
||||
{
|
||||
Parse();
|
||||
}
|
||||
|
||||
VPIDData::VPIDData(const VPIDData &other)
|
||||
: mVpidA{other.mVpidA},
|
||||
mVpidB{other.mVpidB},
|
||||
mStandardA{VPIDStandard_Unknown},
|
||||
mStandardB{VPIDStandard_Unknown},
|
||||
mSamplingA{VPIDSampling_XYZ_444},
|
||||
mSamplingB{VPIDSampling_XYZ_444}
|
||||
{
|
||||
Parse();
|
||||
}
|
||||
VPIDData::VPIDData(VPIDData &&other)
|
||||
: mVpidA{other.mVpidA},
|
||||
mVpidB{other.mVpidB},
|
||||
mStandardA{VPIDStandard_Unknown},
|
||||
mStandardB{VPIDStandard_Unknown},
|
||||
mSamplingA{VPIDSampling_XYZ_444},
|
||||
mSamplingB{VPIDSampling_XYZ_444}
|
||||
{
|
||||
Parse();
|
||||
}
|
||||
|
||||
VPIDData &VPIDData::operator=(const VPIDData &other)
|
||||
{
|
||||
mVpidA = other.mVpidA;
|
||||
mVpidB = other.mVpidB;
|
||||
return *this;
|
||||
}
|
||||
|
||||
VPIDData &VPIDData::operator=(VPIDData &&other)
|
||||
{
|
||||
mVpidA = other.mVpidA;
|
||||
mVpidB = other.mVpidB;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool VPIDData::operator==(const VPIDData &rhs) const
|
||||
{
|
||||
return (mVpidA == rhs.mVpidA && mVpidB == rhs.mVpidB);
|
||||
}
|
||||
|
||||
bool VPIDData::operator!=(const VPIDData &rhs) const
|
||||
{
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
void VPIDData::SetA(ULWord vpidA)
|
||||
{
|
||||
mVpidA = vpidA;
|
||||
}
|
||||
|
||||
void VPIDData::SetB(ULWord vpidB)
|
||||
{
|
||||
mVpidB = vpidB;
|
||||
}
|
||||
|
||||
void VPIDData::Parse()
|
||||
{
|
||||
CNTV2VPID parserA;
|
||||
parserA.SetVPID(mVpidA);
|
||||
mStandardA = parserA.GetStandard();
|
||||
mSamplingA = parserA.GetSampling();
|
||||
|
||||
CNTV2VPID parserB;
|
||||
parserB.SetVPID(mVpidB);
|
||||
mStandardB = parserB.GetStandard();
|
||||
mSamplingB = parserB.GetSampling();
|
||||
}
|
||||
|
||||
bool VPIDData::IsRGB() const
|
||||
{
|
||||
switch (mSamplingA) {
|
||||
default:
|
||||
break;
|
||||
case VPIDSampling_GBR_444:
|
||||
case VPIDSampling_GBRA_4444:
|
||||
case VPIDSampling_GBRD_4444:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
VPIDStandard VPIDData::Standard() const
|
||||
{
|
||||
return mStandardA;
|
||||
}
|
||||
|
||||
VPIDSampling VPIDData::Sampling() const
|
||||
{
|
||||
return mSamplingA;
|
||||
}
|
||||
|
||||
// AJASource Properties
|
||||
SourceProps::SourceProps()
|
||||
: deviceID{DEVICE_ID_NOTFOUND},
|
||||
ioSelect{IOSelection::Invalid},
|
||||
inputSource{NTV2_INPUTSOURCE_INVALID},
|
||||
videoFormat{NTV2_FORMAT_UNKNOWN},
|
||||
pixelFormat{NTV2_FBF_INVALID},
|
||||
sdi4kTransport{SDI4KTransport::TwoSampleInterleave},
|
||||
audioNumChannels{8},
|
||||
audioSampleSize{4},
|
||||
audioSampleRate{48000},
|
||||
vpids{},
|
||||
autoDetect{false},
|
||||
deactivateWhileNotShowing{false}
|
||||
{
|
||||
}
|
||||
|
||||
SourceProps::SourceProps(NTV2DeviceID devID)
|
||||
: deviceID{devID},
|
||||
ioSelect{IOSelection::Invalid},
|
||||
inputSource{NTV2_INPUTSOURCE_INVALID},
|
||||
videoFormat{NTV2_FORMAT_UNKNOWN},
|
||||
pixelFormat{NTV2_FBF_INVALID},
|
||||
sdi4kTransport{SDI4KTransport::TwoSampleInterleave},
|
||||
audioNumChannels{8},
|
||||
audioSampleSize{4},
|
||||
audioSampleRate{48000},
|
||||
vpids{},
|
||||
autoDetect{false},
|
||||
deactivateWhileNotShowing{false}
|
||||
{
|
||||
}
|
||||
|
||||
SourceProps::SourceProps(const SourceProps &props)
|
||||
{
|
||||
deviceID = props.deviceID;
|
||||
ioSelect = props.ioSelect;
|
||||
inputSource = props.inputSource;
|
||||
videoFormat = props.videoFormat;
|
||||
pixelFormat = props.pixelFormat;
|
||||
sdi4kTransport = props.sdi4kTransport;
|
||||
audioNumChannels = props.audioNumChannels;
|
||||
audioSampleSize = props.audioSampleSize;
|
||||
audioSampleRate = props.audioSampleRate;
|
||||
vpids = props.vpids;
|
||||
autoDetect = props.autoDetect;
|
||||
deactivateWhileNotShowing = props.deactivateWhileNotShowing;
|
||||
}
|
||||
|
||||
SourceProps::SourceProps(SourceProps &&props)
|
||||
{
|
||||
deviceID = props.deviceID;
|
||||
ioSelect = props.ioSelect;
|
||||
inputSource = props.inputSource;
|
||||
videoFormat = props.videoFormat;
|
||||
pixelFormat = props.pixelFormat;
|
||||
sdi4kTransport = props.sdi4kTransport;
|
||||
audioNumChannels = props.audioNumChannels;
|
||||
audioSampleSize = props.audioSampleSize;
|
||||
audioSampleRate = props.audioSampleRate;
|
||||
vpids = props.vpids;
|
||||
autoDetect = props.autoDetect;
|
||||
deactivateWhileNotShowing = props.deactivateWhileNotShowing;
|
||||
}
|
||||
|
||||
void SourceProps::operator=(const SourceProps &props)
|
||||
{
|
||||
deviceID = props.deviceID;
|
||||
ioSelect = props.ioSelect;
|
||||
inputSource = props.inputSource;
|
||||
videoFormat = props.videoFormat;
|
||||
pixelFormat = props.pixelFormat;
|
||||
sdi4kTransport = props.sdi4kTransport;
|
||||
audioNumChannels = props.audioNumChannels;
|
||||
audioSampleSize = props.audioSampleSize;
|
||||
audioSampleRate = props.audioSampleRate;
|
||||
vpids = props.vpids;
|
||||
autoDetect = props.autoDetect;
|
||||
deactivateWhileNotShowing = props.deactivateWhileNotShowing;
|
||||
}
|
||||
|
||||
void SourceProps::operator=(SourceProps &&props)
|
||||
{
|
||||
deviceID = props.deviceID;
|
||||
ioSelect = props.ioSelect;
|
||||
inputSource = props.inputSource;
|
||||
videoFormat = props.videoFormat;
|
||||
pixelFormat = props.pixelFormat;
|
||||
sdi4kTransport = props.sdi4kTransport;
|
||||
audioNumChannels = props.audioNumChannels;
|
||||
audioSampleSize = props.audioSampleSize;
|
||||
audioSampleRate = props.audioSampleRate;
|
||||
vpids = props.vpids;
|
||||
autoDetect = props.autoDetect;
|
||||
deactivateWhileNotShowing = props.deactivateWhileNotShowing;
|
||||
}
|
||||
|
||||
bool SourceProps::operator==(const SourceProps &props)
|
||||
{
|
||||
return (deviceID == props.deviceID && ioSelect == props.ioSelect &&
|
||||
// inputSource == props.inputSource &&
|
||||
videoFormat == props.videoFormat &&
|
||||
pixelFormat == props.pixelFormat &&
|
||||
// vpid == props.vpid &&
|
||||
autoDetect == props.autoDetect &&
|
||||
sdi4kTransport == props.sdi4kTransport &&
|
||||
audioNumChannels == props.audioNumChannels &&
|
||||
audioSampleSize == props.audioSampleSize &&
|
||||
audioSampleRate == props.audioSampleRate &&
|
||||
deactivateWhileNotShowing == props.deactivateWhileNotShowing);
|
||||
}
|
||||
|
||||
bool SourceProps::operator!=(const SourceProps &props)
|
||||
{
|
||||
return !operator==(props);
|
||||
}
|
||||
|
||||
NTV2Channel SourceProps::Channel() const
|
||||
{
|
||||
return NTV2InputSourceToChannel(inputSource);
|
||||
}
|
||||
|
||||
NTV2AudioSystem SourceProps::AudioSystem() const
|
||||
{
|
||||
return NTV2ChannelToAudioSystem(Channel());
|
||||
}
|
||||
|
||||
NTV2AudioRate SourceProps::AudioRate() const
|
||||
{
|
||||
NTV2AudioRate rate = NTV2_AUDIO_48K;
|
||||
switch (audioSampleRate) {
|
||||
default:
|
||||
case 48000:
|
||||
rate = NTV2_AUDIO_48K;
|
||||
break;
|
||||
case 96000:
|
||||
rate = NTV2_AUDIO_96K;
|
||||
break;
|
||||
case 192000:
|
||||
rate = NTV2_AUDIO_192K;
|
||||
break;
|
||||
}
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
// Size in bytes of N channels of audio
|
||||
size_t SourceProps::AudioSize() const
|
||||
{
|
||||
return audioNumChannels * audioSampleSize;
|
||||
}
|
||||
|
||||
audio_format SourceProps::AudioFormat() const
|
||||
{
|
||||
// NTV2 is always 32-bit PCM
|
||||
return AUDIO_FORMAT_32BIT;
|
||||
}
|
||||
|
||||
speaker_layout SourceProps::SpeakerLayout() const
|
||||
{
|
||||
if (audioNumChannels == 2)
|
||||
return SPEAKERS_STEREO;
|
||||
// NTV2 is always at least 8ch on modern boards
|
||||
return SPEAKERS_7POINT1;
|
||||
}
|
||||
|
||||
//
|
||||
// AJAOutput Properties
|
||||
//
|
||||
OutputProps::OutputProps(NTV2DeviceID devID)
|
||||
: deviceID{devID},
|
||||
ioSelect{IOSelection::Invalid},
|
||||
outputDest{NTV2_OUTPUTDESTINATION_ANALOG},
|
||||
videoFormat{NTV2_FORMAT_UNKNOWN},
|
||||
pixelFormat{NTV2_FBF_INVALID},
|
||||
sdi4kTransport{SDI4KTransport::TwoSampleInterleave},
|
||||
audioNumChannels{8},
|
||||
audioSampleSize{4},
|
||||
audioSampleRate{48000}
|
||||
{
|
||||
}
|
||||
|
||||
OutputProps::OutputProps(OutputProps &&props)
|
||||
{
|
||||
deviceID = props.deviceID;
|
||||
ioSelect = props.ioSelect;
|
||||
outputDest = props.outputDest;
|
||||
videoFormat = props.videoFormat;
|
||||
pixelFormat = props.pixelFormat;
|
||||
sdi4kTransport = props.sdi4kTransport;
|
||||
audioNumChannels = props.audioNumChannels;
|
||||
audioSampleSize = props.audioSampleSize;
|
||||
audioSampleRate = props.audioSampleRate;
|
||||
}
|
||||
|
||||
OutputProps::OutputProps(const OutputProps &props)
|
||||
{
|
||||
deviceID = props.deviceID;
|
||||
ioSelect = props.ioSelect;
|
||||
outputDest = props.outputDest;
|
||||
videoFormat = props.videoFormat;
|
||||
pixelFormat = props.pixelFormat;
|
||||
sdi4kTransport = props.sdi4kTransport;
|
||||
audioNumChannels = props.audioNumChannels;
|
||||
audioSampleSize = props.audioSampleSize;
|
||||
audioSampleRate = props.audioSampleRate;
|
||||
}
|
||||
|
||||
void OutputProps::operator=(const OutputProps &props)
|
||||
{
|
||||
deviceID = props.deviceID;
|
||||
ioSelect = props.ioSelect;
|
||||
outputDest = props.outputDest;
|
||||
videoFormat = props.videoFormat;
|
||||
pixelFormat = props.pixelFormat;
|
||||
sdi4kTransport = props.sdi4kTransport;
|
||||
audioNumChannels = props.audioNumChannels;
|
||||
audioSampleSize = props.audioSampleSize;
|
||||
audioSampleRate = props.audioSampleRate;
|
||||
}
|
||||
|
||||
void OutputProps::operator=(OutputProps &&props)
|
||||
{
|
||||
deviceID = props.deviceID;
|
||||
ioSelect = props.ioSelect;
|
||||
outputDest = props.outputDest;
|
||||
videoFormat = props.videoFormat;
|
||||
pixelFormat = props.pixelFormat;
|
||||
sdi4kTransport = props.sdi4kTransport;
|
||||
audioNumChannels = props.audioNumChannels;
|
||||
audioSampleSize = props.audioSampleSize;
|
||||
audioSampleRate = props.audioSampleRate;
|
||||
}
|
||||
|
||||
bool OutputProps::operator==(const OutputProps &props)
|
||||
{
|
||||
return (deviceID == props.deviceID && ioSelect == props.ioSelect &&
|
||||
// outputDest == props.outputDest &&
|
||||
videoFormat == props.videoFormat &&
|
||||
pixelFormat == props.pixelFormat &&
|
||||
audioNumChannels == props.audioNumChannels &&
|
||||
audioSampleSize == props.audioSampleSize &&
|
||||
audioSampleRate == props.audioSampleRate);
|
||||
}
|
||||
|
||||
bool OutputProps::operator!=(const OutputProps &props)
|
||||
{
|
||||
return !operator==(props);
|
||||
}
|
||||
|
||||
NTV2FormatDesc OutputProps::FormatDesc()
|
||||
{
|
||||
return NTV2FormatDesc(videoFormat, pixelFormat, NTV2_VANCMODE_OFF);
|
||||
}
|
||||
|
||||
NTV2Channel OutputProps::Channel() const
|
||||
{
|
||||
// Output Channel Special Cases
|
||||
// KONA1 -- Has 2 framestores but only 1 bi-directional SDI widget
|
||||
if (deviceID == DEVICE_ID_KONA1) {
|
||||
return NTV2_CHANNEL2;
|
||||
} else if ((deviceID == DEVICE_ID_IO4K ||
|
||||
deviceID == DEVICE_ID_IO4KPLUS) &&
|
||||
outputDest == NTV2_OUTPUTDESTINATION_SDI5) {
|
||||
// IO4K/IO4K+ SDI Monitor - Use framestore 4 but SDI5
|
||||
return NTV2_CHANNEL4;
|
||||
}
|
||||
|
||||
if (NTV2_OUTPUT_DEST_IS_HDMI(outputDest))
|
||||
return static_cast<NTV2Channel>(
|
||||
NTV2DeviceGetNumFrameStores(deviceID) - 1);
|
||||
|
||||
return NTV2OutputDestinationToChannel(outputDest);
|
||||
}
|
||||
|
||||
NTV2AudioSystem OutputProps::AudioSystem() const
|
||||
{
|
||||
return NTV2ChannelToAudioSystem(Channel());
|
||||
}
|
||||
|
||||
NTV2AudioRate OutputProps::AudioRate() const
|
||||
{
|
||||
NTV2AudioRate rate = NTV2_AUDIO_48K;
|
||||
switch (audioSampleRate) {
|
||||
default:
|
||||
case 48000:
|
||||
rate = NTV2_AUDIO_48K;
|
||||
break;
|
||||
case 96000:
|
||||
rate = NTV2_AUDIO_96K;
|
||||
break;
|
||||
case 192000:
|
||||
rate = NTV2_AUDIO_192K;
|
||||
break;
|
||||
}
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
// Size in bytes of N channels of audio
|
||||
size_t OutputProps::AudioSize() const
|
||||
{
|
||||
return audioNumChannels * audioSampleSize;
|
||||
}
|
||||
|
||||
audio_format OutputProps::AudioFormat() const
|
||||
{
|
||||
// NTV2 is always 32-bit PCM
|
||||
return AUDIO_FORMAT_32BIT;
|
||||
}
|
||||
|
||||
speaker_layout OutputProps::SpeakerLayout() const
|
||||
{
|
||||
if (audioNumChannels == 2)
|
||||
return SPEAKERS_STEREO;
|
||||
// NTV2 is always at least 8ch on modern boards
|
||||
return SPEAKERS_7POINT1;
|
||||
}
|
108
plugins/aja/aja-props.hpp
Normal file
108
plugins/aja/aja-props.hpp
Normal file
@ -0,0 +1,108 @@
|
||||
#pragma once
|
||||
|
||||
#include "aja-enums.hpp"
|
||||
|
||||
#include <media-io/audio-io.h>
|
||||
|
||||
#include <ajantv2/includes/ntv2enums.h>
|
||||
#include <ajantv2/includes/ntv2formatdescriptor.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class VPIDData {
|
||||
public:
|
||||
VPIDData();
|
||||
VPIDData(ULWord vpidA, ULWord vpidB);
|
||||
VPIDData(const VPIDData &other);
|
||||
VPIDData(VPIDData &&other);
|
||||
~VPIDData() = default;
|
||||
|
||||
VPIDData &operator=(const VPIDData &other);
|
||||
VPIDData &operator=(VPIDData &&other);
|
||||
bool operator==(const VPIDData &rhs) const;
|
||||
bool operator!=(const VPIDData &rhs) const;
|
||||
|
||||
void SetA(ULWord vpidA);
|
||||
void SetB(ULWord vpidB);
|
||||
void Parse();
|
||||
bool IsRGB() const;
|
||||
|
||||
VPIDStandard Standard() const;
|
||||
VPIDSampling Sampling() const;
|
||||
|
||||
private:
|
||||
ULWord mVpidA;
|
||||
ULWord mVpidB;
|
||||
VPIDStandard mStandardA;
|
||||
VPIDSampling mSamplingA;
|
||||
VPIDStandard mStandardB;
|
||||
VPIDSampling mSamplingB;
|
||||
};
|
||||
|
||||
using VPIDDataList = std::vector<VPIDData>;
|
||||
|
||||
//TODO(paulh): Consolidate the two Props classes
|
||||
class SourceProps {
|
||||
public:
|
||||
explicit SourceProps();
|
||||
explicit SourceProps(NTV2DeviceID devID);
|
||||
~SourceProps() = default;
|
||||
SourceProps(const SourceProps &props);
|
||||
SourceProps(SourceProps &&props);
|
||||
void operator=(const SourceProps &props);
|
||||
void operator=(SourceProps &&props);
|
||||
bool operator==(const SourceProps &props);
|
||||
bool operator!=(const SourceProps &props);
|
||||
|
||||
NTV2Channel Channel() const;
|
||||
NTV2AudioSystem AudioSystem() const;
|
||||
NTV2AudioRate AudioRate() const;
|
||||
size_t AudioSize() const;
|
||||
audio_format AudioFormat() const;
|
||||
speaker_layout SpeakerLayout() const;
|
||||
|
||||
NTV2DeviceID deviceID;
|
||||
IOSelection ioSelect;
|
||||
NTV2InputSource inputSource;
|
||||
NTV2VideoFormat videoFormat;
|
||||
NTV2PixelFormat pixelFormat;
|
||||
SDI4KTransport sdi4kTransport;
|
||||
VPIDDataList vpids;
|
||||
uint32_t audioNumChannels;
|
||||
uint32_t audioSampleSize;
|
||||
uint32_t audioSampleRate;
|
||||
bool autoDetect;
|
||||
bool deactivateWhileNotShowing;
|
||||
};
|
||||
|
||||
class OutputProps {
|
||||
public:
|
||||
explicit OutputProps(NTV2DeviceID devID);
|
||||
~OutputProps() = default;
|
||||
OutputProps(const OutputProps &props);
|
||||
OutputProps(OutputProps &&props);
|
||||
void operator=(const OutputProps &props);
|
||||
void operator=(OutputProps &&props);
|
||||
bool operator==(const OutputProps &props);
|
||||
bool operator!=(const OutputProps &props);
|
||||
|
||||
NTV2FormatDesc FormatDesc();
|
||||
NTV2Channel Channel() const;
|
||||
NTV2AudioSystem AudioSystem() const;
|
||||
NTV2AudioRate AudioRate() const;
|
||||
size_t AudioSize() const;
|
||||
audio_format AudioFormat() const;
|
||||
speaker_layout SpeakerLayout() const;
|
||||
|
||||
NTV2DeviceID deviceID;
|
||||
IOSelection ioSelect;
|
||||
NTV2OutputDestination outputDest;
|
||||
NTV2VideoFormat videoFormat;
|
||||
NTV2PixelFormat pixelFormat;
|
||||
SDI4KTransport sdi4kTransport;
|
||||
uint32_t audioNumChannels;
|
||||
uint32_t audioSampleSize;
|
||||
uint32_t audioSampleRate;
|
||||
};
|
823
plugins/aja/aja-routing.cpp
Normal file
823
plugins/aja/aja-routing.cpp
Normal file
@ -0,0 +1,823 @@
|
||||
#include "aja-card-manager.hpp"
|
||||
#include "aja-common.hpp"
|
||||
#include "aja-routing.hpp"
|
||||
#include "aja-widget-io.hpp"
|
||||
|
||||
// Signal routing crosspoint and register setting tables for SDI/HDMI/etc.
|
||||
#include "routing/hdmi_rgb_capture.h"
|
||||
#include "routing/hdmi_rgb_display.h"
|
||||
#include "routing/hdmi_ycbcr_capture.h"
|
||||
#include "routing/hdmi_ycbcr_display.h"
|
||||
#include "routing/sdi_ycbcr_capture.h"
|
||||
#include "routing/sdi_ycbcr_display.h"
|
||||
#include "routing/sdi_rgb_capture.h"
|
||||
#include "routing/sdi_rgb_display.h"
|
||||
|
||||
#include <ajabase/common/common.h>
|
||||
#include <ajantv2/includes/ntv2card.h>
|
||||
#include <ajantv2/includes/ntv2devicefeatures.h>
|
||||
|
||||
#include <obs-module.h>
|
||||
|
||||
RasterDefinition GetRasterDefinition(IOSelection io, NTV2VideoFormat vf,
|
||||
NTV2DeviceID deviceID)
|
||||
{
|
||||
RasterDefinition def = RasterDefinition::Unknown;
|
||||
|
||||
if (NTV2_IS_SD_VIDEO_FORMAT(vf)) {
|
||||
def = RasterDefinition::SD;
|
||||
} else if (NTV2_IS_HD_VIDEO_FORMAT(vf)) {
|
||||
def = RasterDefinition::HD;
|
||||
} else if (NTV2_IS_QUAD_FRAME_FORMAT(vf)) {
|
||||
def = RasterDefinition::UHD_4K;
|
||||
|
||||
/* NOTE(paulh): Special enum for Kona5 Retail & IO4K+ firmwares which route UHD/4K formats
|
||||
* over 1x 6G/12G SDI using an undocumented crosspoint config.
|
||||
*/
|
||||
if (aja::IsSDIOneWireIOSelection(io) &&
|
||||
aja::IsRetailSDI12G(deviceID))
|
||||
def = RasterDefinition::UHD_4K_Retail_12G;
|
||||
} else if (NTV2_IS_QUAD_QUAD_FORMAT(vf)) {
|
||||
def = RasterDefinition::UHD2_8K;
|
||||
} else {
|
||||
def = RasterDefinition::Unknown;
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
#define NTV2UTILS_ENUM_CASE_RETURN_STR(enum_name) \
|
||||
case (enum_name): \
|
||||
return #enum_name
|
||||
std::string RasterDefinitionToString(RasterDefinition rd)
|
||||
{
|
||||
std::string str = "";
|
||||
|
||||
switch (rd) {
|
||||
NTV2UTILS_ENUM_CASE_RETURN_STR(RasterDefinition::SD);
|
||||
NTV2UTILS_ENUM_CASE_RETURN_STR(RasterDefinition::HD);
|
||||
NTV2UTILS_ENUM_CASE_RETURN_STR(RasterDefinition::UHD_4K);
|
||||
NTV2UTILS_ENUM_CASE_RETURN_STR(RasterDefinition::UHD2_8K);
|
||||
NTV2UTILS_ENUM_CASE_RETURN_STR(RasterDefinition::Unknown);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the widget routing shorthand string into a map of input and output NTV2CrosspointIDs.
|
||||
* For example "sdi[0][0]->fb[0][0]" is shorthand for connecting the output crosspoint for
|
||||
* SDI1/Datastream1 (NTV2_XptSDIIn1) to the input crosspoint for Framestore1/Datastream1 (NTV2_XptFrameBuffer1Input).
|
||||
* These routing shorthand strings are found within the RoutingConfig structs in the "routing" sub-directory of the plugin.
|
||||
*/
|
||||
bool Routing::ParseRouteString(const std::string &route,
|
||||
NTV2XptConnections &cnx)
|
||||
{
|
||||
blog(LOG_DEBUG, "aja::Routing::ParseRouteString: Input string: %s",
|
||||
route.c_str());
|
||||
|
||||
std::string route_lower(route);
|
||||
route_lower = aja::lower(route_lower);
|
||||
const std::string &route_strip = aja::replace(route_lower, " ", "");
|
||||
|
||||
if (route_strip.empty()) {
|
||||
blog(LOG_DEBUG,
|
||||
"Routing::ParseRouteString: input string is empty!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* TODO(paulh): Tally up the lines and tokens and check that they are all parsed OK.
|
||||
* Right now we just return true if ANY tokens were parsed. This is OK _for now_ because
|
||||
* the route strings currently only come from a known set.
|
||||
*/
|
||||
NTV2StringList lines;
|
||||
NTV2StringList tokens;
|
||||
|
||||
lines = aja::split(route_strip, ';');
|
||||
if (lines.empty())
|
||||
lines.push_back(route_strip);
|
||||
|
||||
int32_t parse_ok = 0;
|
||||
for (const auto &l : lines) {
|
||||
if (l.empty()) {
|
||||
blog(LOG_DEBUG,
|
||||
"aja::Routing::ParseRouteString: Empty line!");
|
||||
continue;
|
||||
}
|
||||
|
||||
blog(LOG_DEBUG, "aja::Routing::ParseRouteString: Line: %s",
|
||||
l.c_str());
|
||||
|
||||
NTV2StringList tokens = aja::split(l, "->");
|
||||
if (tokens.empty() || tokens.size() != 2) {
|
||||
blog(LOG_DEBUG,
|
||||
"aja::Routing::ParseRouteString: Invalid token count!");
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string &left = tokens[0]; // output crosspoint
|
||||
const std::string &right = tokens[1]; // input crosspoint
|
||||
if (left.empty() || left.length() > 64) {
|
||||
blog(LOG_DEBUG,
|
||||
"aja::Routing::ParseRouteString: Invalid Left token!");
|
||||
continue;
|
||||
}
|
||||
if (right.empty() || right.length() > 64) {
|
||||
blog(LOG_DEBUG,
|
||||
"aja::Routing::ParseRouteString: Invalid right token!");
|
||||
continue;
|
||||
}
|
||||
|
||||
blog(LOG_DEBUG,
|
||||
"aja::Routing::ParseRouteString: Left Token: %s -> Right Token: %s",
|
||||
left.c_str(), right.c_str());
|
||||
|
||||
// Parse Output Crosspoint from left token
|
||||
int32_t out_chan = 0;
|
||||
int32_t out_ds = 0;
|
||||
std::string out_name(64, ' ');
|
||||
if (std::sscanf(left.c_str(), "%[A-Za-z_0-9][%d][%d]",
|
||||
&out_name[0], &out_chan, &out_ds)) {
|
||||
out_name = aja::rstrip(out_name).substr(
|
||||
0, out_name.find_first_of('\0'));
|
||||
|
||||
WidgetOutputSocket widget_out;
|
||||
if (WidgetOutputSocket::Find(out_name,
|
||||
(NTV2Channel)out_chan,
|
||||
out_ds, widget_out)) {
|
||||
blog(LOG_DEBUG,
|
||||
"aja::Routing::ParseRouteString: Found NTV2OutputCrosspointID %s",
|
||||
NTV2OutputCrosspointIDToString(
|
||||
widget_out.id)
|
||||
.c_str());
|
||||
|
||||
// Parse Input Crosspoint from right token
|
||||
int32_t inp_chan = 0;
|
||||
int32_t inp_ds = 0;
|
||||
std::string inp_name(64, ' ');
|
||||
if (std::sscanf(right.c_str(),
|
||||
"%[A-Za-z_0-9][%d][%d]",
|
||||
&inp_name[0], &inp_chan,
|
||||
&inp_ds)) {
|
||||
inp_name = aja::rstrip(inp_name).substr(
|
||||
0,
|
||||
inp_name.find_first_of('\0'));
|
||||
|
||||
WidgetInputSocket widget_inp;
|
||||
if (WidgetInputSocket::Find(
|
||||
inp_name,
|
||||
(NTV2Channel)inp_chan,
|
||||
inp_ds, widget_inp)) {
|
||||
blog(LOG_DEBUG,
|
||||
"aja::Routing::ParseRouteString: Found NTV2InputCrosspointID %s",
|
||||
NTV2InputCrosspointIDToString(
|
||||
widget_inp.id)
|
||||
.c_str());
|
||||
|
||||
cnx[widget_inp.id] =
|
||||
widget_out.id;
|
||||
parse_ok++;
|
||||
} else {
|
||||
blog(LOG_DEBUG,
|
||||
"aja::Routing::ParseRouteString: NTV2InputCrosspointID not found!");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
blog(LOG_DEBUG,
|
||||
"aja::Routing::ParseRouteString: NTV2OutputCrosspointID not found!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parse_ok > 0;
|
||||
}
|
||||
|
||||
// Determine the appropriate SDIWireFormat based on the specified device ID and VPID specification.
|
||||
bool Routing::DetermineSDIWireFormat(NTV2DeviceID deviceID, VPIDSpec spec,
|
||||
SDIWireFormat &swf)
|
||||
{
|
||||
if (deviceID == DEVICE_ID_KONA5 || deviceID == DEVICE_ID_IO4KPLUS) {
|
||||
static const std::vector<VPIDStandard> kRetail6GVpidStandards = {
|
||||
VPIDStandard_2160_Single_6Gb,
|
||||
VPIDStandard_1080_Single_6Gb,
|
||||
VPIDStandard_1080_AFR_Single_6Gb,
|
||||
};
|
||||
static const std::vector<VPIDStandard> kRetail12GVpidStandards =
|
||||
{VPIDStandard_2160_Single_12Gb,
|
||||
VPIDStandard_1080_10_12_AFR_Single_12Gb};
|
||||
if (spec.first == RasterDefinition::UHD_4K &&
|
||||
aja::vec_contains<VPIDStandard>(kRetail6GVpidStandards,
|
||||
spec.second)) {
|
||||
swf = SDIWireFormat::
|
||||
UHD4K_ST2018_6G_Squares_2SI_Kona5_io4KPlus;
|
||||
return true;
|
||||
} else if (spec.first == RasterDefinition::UHD_4K &&
|
||||
aja::vec_contains<VPIDStandard>(
|
||||
kRetail12GVpidStandards, spec.second)) {
|
||||
swf = SDIWireFormat::
|
||||
UHD4K_ST2018_12G_Squares_2SI_Kona5_io4KPlus;
|
||||
return true;
|
||||
} else {
|
||||
if (kSDIWireFormats.find(spec) !=
|
||||
kSDIWireFormats.end()) {
|
||||
swf = kSDIWireFormats.at(spec);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (kSDIWireFormats.find(spec) != kSDIWireFormats.end()) {
|
||||
swf = kSDIWireFormats.at(spec);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Lookup configuration for HDMI input/output in the routing table.
|
||||
bool Routing::FindRoutingConfigHDMI(HDMIWireFormat hwf, NTV2Mode mode,
|
||||
bool isRGB, NTV2DeviceID deviceID,
|
||||
RoutingConfig &routing)
|
||||
{
|
||||
if (isRGB) {
|
||||
if (mode == NTV2_MODE_CAPTURE) {
|
||||
if (kHDMIRGBCaptureConfigs.find(hwf) !=
|
||||
kHDMIRGBCaptureConfigs.end()) {
|
||||
routing = kHDMIRGBCaptureConfigs.at(hwf);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (deviceID == DEVICE_ID_TTAP_PRO) {
|
||||
routing = kHDMIRGBDisplayConfigs.at(
|
||||
HDMIWireFormat::TTAP_PRO);
|
||||
return true;
|
||||
}
|
||||
if (kHDMIRGBDisplayConfigs.find(hwf) !=
|
||||
kHDMIRGBDisplayConfigs.end()) {
|
||||
routing = kHDMIRGBDisplayConfigs.at(hwf);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (mode == NTV2_MODE_CAPTURE) {
|
||||
if (kHDMIYCbCrCaptureConfigs.find(hwf) !=
|
||||
kHDMIYCbCrCaptureConfigs.end()) {
|
||||
routing = kHDMIYCbCrCaptureConfigs.at(hwf);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (kHDMIYCbCrDisplayConfigs.find(hwf) !=
|
||||
kHDMIYCbCrDisplayConfigs.end()) {
|
||||
routing = kHDMIYCbCrDisplayConfigs.at(hwf);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Lookup configuration for SDI input/output in the routing table.
|
||||
bool Routing::FindRoutingConfigSDI(SDIWireFormat swf, NTV2Mode mode, bool isRGB,
|
||||
NTV2DeviceID deviceID,
|
||||
RoutingConfig &routing)
|
||||
{
|
||||
UNUSED_PARAMETER(deviceID);
|
||||
|
||||
if (isRGB) {
|
||||
if (mode == NTV2_MODE_CAPTURE) {
|
||||
if (kSDIRGBCaptureConfigs.find(swf) !=
|
||||
kSDIRGBCaptureConfigs.end()) {
|
||||
routing = kSDIRGBCaptureConfigs.at(swf);
|
||||
return true;
|
||||
}
|
||||
} else if (mode == NTV2_MODE_DISPLAY) {
|
||||
if (kSDIRGBDisplayConfigs.find(swf) !=
|
||||
kSDIRGBDisplayConfigs.end()) {
|
||||
routing = kSDIRGBDisplayConfigs.at(swf);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (mode == NTV2_MODE_CAPTURE) {
|
||||
if (kSDIYCbCrCaptureConfigs.find(swf) !=
|
||||
kSDIYCbCrCaptureConfigs.end()) {
|
||||
routing = kSDIYCbCrCaptureConfigs.at(swf);
|
||||
return true;
|
||||
}
|
||||
} else if (mode == NTV2_MODE_DISPLAY) {
|
||||
if (kSDIYCbCrDisplayConfigs.find(swf) !=
|
||||
kSDIYCbCrDisplayConfigs.end()) {
|
||||
routing = kSDIYCbCrDisplayConfigs.at(swf);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Routing::StartSourceAudio(const SourceProps &props, CNTV2Card *card)
|
||||
{
|
||||
if (!card)
|
||||
return;
|
||||
|
||||
auto inputSrc = props.inputSource;
|
||||
auto channel = props.Channel();
|
||||
auto audioSys = props.AudioSystem();
|
||||
|
||||
card->WriteAudioSource(0, channel);
|
||||
card->SetAudioSystemInputSource(
|
||||
audioSys, NTV2InputSourceToAudioSource(inputSrc),
|
||||
NTV2InputSourceToEmbeddedAudioInput(inputSrc));
|
||||
|
||||
card->SetNumberAudioChannels(props.audioNumChannels, audioSys);
|
||||
card->SetAudioRate(props.AudioRate(), audioSys);
|
||||
card->SetAudioBufferSize(NTV2_AUDIO_BUFFER_BIG, audioSys);
|
||||
|
||||
// Fix for AJA NTV2 internal bug #11467
|
||||
ULWord magicAudioBits = 0;
|
||||
if (NTV2_INPUT_SOURCE_IS_HDMI(inputSrc)) {
|
||||
magicAudioBits = 0x00100000;
|
||||
switch (inputSrc) {
|
||||
default:
|
||||
case NTV2_INPUTSOURCE_HDMI1:
|
||||
magicAudioBits = 0x00100000;
|
||||
break;
|
||||
case NTV2_INPUTSOURCE_HDMI2:
|
||||
magicAudioBits = 0x00110000;
|
||||
break;
|
||||
case NTV2_INPUTSOURCE_HDMI3:
|
||||
magicAudioBits = 0x00900000;
|
||||
break;
|
||||
case NTV2_INPUTSOURCE_HDMI4:
|
||||
magicAudioBits = 0x00910000;
|
||||
break;
|
||||
}
|
||||
} else if (NTV2_INPUT_SOURCE_IS_ANALOG(inputSrc)) {
|
||||
magicAudioBits = 0x00000990;
|
||||
} else { // SDI
|
||||
magicAudioBits = 0x00000320;
|
||||
}
|
||||
|
||||
// TODO(paulh): Ask aja-seanl about these deprecated calls and if they are still needed
|
||||
ULWord oldValue = 0;
|
||||
if (card->ReadAudioSource(oldValue, channel)) {
|
||||
card->WriteAudioSource(oldValue | magicAudioBits, channel);
|
||||
}
|
||||
|
||||
for (int a = 0; a < NTV2DeviceGetNumAudioSystems(card->GetDeviceID());
|
||||
a++) {
|
||||
card->SetAudioLoopBack(NTV2_AUDIO_LOOPBACK_OFF,
|
||||
NTV2AudioSystem(a));
|
||||
}
|
||||
|
||||
card->StartAudioInput(audioSys);
|
||||
card->SetAudioCaptureEnable(audioSys, true);
|
||||
}
|
||||
|
||||
void Routing::StopSourceAudio(const SourceProps &props, CNTV2Card *card)
|
||||
{
|
||||
if (card) {
|
||||
auto audioSys = props.AudioSystem();
|
||||
card->SetAudioCaptureEnable(audioSys, false);
|
||||
card->StopAudioInput(audioSys);
|
||||
}
|
||||
}
|
||||
|
||||
// Guess an SDIWireFormat based on specified Video Format, IOSelection, 4K Transport and device ID.
|
||||
SDIWireFormat GuessSDIWireFormat(NTV2VideoFormat vf, IOSelection io,
|
||||
SDI4KTransport t4k,
|
||||
NTV2DeviceID device_id = DEVICE_ID_NOTFOUND)
|
||||
{
|
||||
auto rd = GetRasterDefinition(io, vf, device_id);
|
||||
auto fg = GetNTV2FrameGeometryFromVideoFormat(vf);
|
||||
|
||||
SDIWireFormat swf = SDIWireFormat::Unknown;
|
||||
if (rd == RasterDefinition::SD) {
|
||||
swf = SDIWireFormat::SD_ST352;
|
||||
} else if (rd == RasterDefinition::HD) {
|
||||
if (fg == NTV2_FG_1280x720) {
|
||||
swf = SDIWireFormat::HD_720p_ST292;
|
||||
} else if (fg == NTV2_FG_1920x1080 || fg == NTV2_FG_2048x1080) {
|
||||
swf = SDIWireFormat::HD_1080_ST292;
|
||||
}
|
||||
} else if (rd == RasterDefinition::UHD_4K) {
|
||||
if (t4k == SDI4KTransport::Squares) {
|
||||
if (aja::IsSDIFourWireIOSelection(io)) {
|
||||
swf = SDIWireFormat::UHD4K_ST292_Quad_1_5_Squares;
|
||||
} else if (aja::IsSDITwoWireIOSelection(io)) {
|
||||
if (t4k == SDI4KTransport::Squares) {
|
||||
swf = SDIWireFormat::
|
||||
UHD4K_ST292_Dual_1_5_Squares;
|
||||
} else {
|
||||
swf = SDIWireFormat::
|
||||
UHD4K_ST425_Dual_3Gb_2SI;
|
||||
}
|
||||
}
|
||||
} else if (t4k == SDI4KTransport::TwoSampleInterleave) {
|
||||
if (aja::IsSDIOneWireIOSelection(io)) {
|
||||
if (NTV2_IS_4K_HFR_VIDEO_FORMAT(vf)) {
|
||||
if (aja::IsRetailSDI12G(device_id)) {
|
||||
swf = SDIWireFormat::
|
||||
UHD4K_ST2018_12G_Squares_2SI_Kona5_io4KPlus;
|
||||
} else {
|
||||
swf = SDIWireFormat::
|
||||
UHD4K_ST2018_12G_Squares_2SI;
|
||||
}
|
||||
} else {
|
||||
if (aja::IsRetailSDI12G(device_id)) {
|
||||
swf = SDIWireFormat::
|
||||
UHD4K_ST2018_6G_Squares_2SI_Kona5_io4KPlus;
|
||||
} else {
|
||||
swf = SDIWireFormat::
|
||||
UHD4K_ST2018_6G_Squares_2SI;
|
||||
}
|
||||
}
|
||||
} else if (aja::IsSDITwoWireIOSelection(io)) {
|
||||
swf = SDIWireFormat::UHD4K_ST425_Dual_3Gb_2SI;
|
||||
} else if (aja::IsSDIFourWireIOSelection(io)) {
|
||||
swf = SDIWireFormat::UHD4K_ST425_Quad_3Gb_2SI;
|
||||
}
|
||||
}
|
||||
}
|
||||
return swf;
|
||||
}
|
||||
|
||||
bool Routing::ConfigureSourceRoute(const SourceProps &props, NTV2Mode mode,
|
||||
CNTV2Card *card)
|
||||
{
|
||||
if (!card)
|
||||
return false;
|
||||
|
||||
auto deviceID = props.deviceID;
|
||||
|
||||
NTV2VideoFormat vf = props.videoFormat;
|
||||
if (NTV2_VIDEO_FORMAT_IS_B(props.videoFormat)) {
|
||||
vf = aja::GetLevelAFormatForLevelBFormat(props.videoFormat);
|
||||
}
|
||||
|
||||
NTV2InputSourceSet inputSources;
|
||||
aja::IOSelectionToInputSources(props.ioSelect, inputSources);
|
||||
if (inputSources.empty()) {
|
||||
blog(LOG_DEBUG,
|
||||
"No Input Sources specified to configure routing!");
|
||||
return false;
|
||||
}
|
||||
auto init_src = *inputSources.begin();
|
||||
auto init_channel = NTV2InputSourceToChannel(init_src);
|
||||
|
||||
RoutingConfig rc;
|
||||
if (NTV2_INPUT_SOURCE_IS_SDI(init_src)) {
|
||||
SDIWireFormat swf = SDIWireFormat::Unknown;
|
||||
auto standard = VPIDStandard_Unknown;
|
||||
auto vpidList = props.vpids;
|
||||
if (vpidList.size() > 0)
|
||||
standard = vpidList.at(0).Standard();
|
||||
|
||||
if (standard != VPIDStandard_Unknown) {
|
||||
// Determine SDI format based on raster "definition" and VPID byte 1 value (AKA SMPTE standard)
|
||||
auto rasterDef = GetRasterDefinition(props.ioSelect, vf,
|
||||
props.deviceID);
|
||||
VPIDSpec vpidSpec = std::make_pair(rasterDef, standard);
|
||||
DetermineSDIWireFormat(deviceID, vpidSpec, swf);
|
||||
} else {
|
||||
// Best guess SDI format from incoming video format if no VPIDs detected
|
||||
swf = GuessSDIWireFormat(vf, props.ioSelect,
|
||||
props.sdi4kTransport,
|
||||
props.deviceID);
|
||||
}
|
||||
|
||||
if (swf == SDIWireFormat::Unknown) {
|
||||
blog(LOG_DEBUG, "Could not determine SDI Wire Format!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FindRoutingConfigSDI(swf, mode,
|
||||
NTV2_IS_FBF_RGB(props.pixelFormat),
|
||||
props.deviceID, rc)) {
|
||||
blog(LOG_DEBUG,
|
||||
"Could not find RoutingConfig for SDI Wire Format!");
|
||||
return false;
|
||||
}
|
||||
} else if (NTV2_INPUT_SOURCE_IS_HDMI(init_src)) {
|
||||
HDMIWireFormat hwf = HDMIWireFormat::Unknown;
|
||||
|
||||
if (NTV2_IS_FBF_RGB(props.pixelFormat)) {
|
||||
if (NTV2_IS_HD_VIDEO_FORMAT(vf))
|
||||
hwf = HDMIWireFormat::HD_RGB_LFR;
|
||||
} else {
|
||||
if (NTV2_IS_HD_VIDEO_FORMAT(vf))
|
||||
hwf = HDMIWireFormat::HD_YCBCR_LFR;
|
||||
else if (NTV2_IS_4K_VIDEO_FORMAT(vf))
|
||||
hwf = HDMIWireFormat::UHD_4K_YCBCR_LFR;
|
||||
}
|
||||
|
||||
if (!FindRoutingConfigHDMI(hwf, mode,
|
||||
NTV2_IS_FBF_RGB(props.pixelFormat),
|
||||
props.deviceID, rc)) {
|
||||
blog(LOG_DEBUG,
|
||||
"Could not find RoutingConfig for HDMI Wire Format!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Substitute channel placeholders for actual indices
|
||||
std::string route_string = rc.route_string;
|
||||
ULWord start_channel_index = GetIndexForNTV2Channel(init_channel);
|
||||
for (ULWord c = 0; c < 8; c++) {
|
||||
std::string channel_placeholder =
|
||||
std::string("{ch" + aja::to_string(c + 1) + "}");
|
||||
route_string =
|
||||
aja::replace(route_string, channel_placeholder,
|
||||
aja::to_string(start_channel_index++));
|
||||
}
|
||||
|
||||
NTV2XptConnections cnx;
|
||||
ParseRouteString(route_string, cnx);
|
||||
|
||||
card->ApplySignalRoute(cnx, false);
|
||||
|
||||
// Apply SDI widget settings
|
||||
start_channel_index = GetIndexForNTV2Channel(init_channel);
|
||||
for (uint32_t i = (uint32_t)start_channel_index;
|
||||
i < (start_channel_index + rc.num_wires); i++) {
|
||||
NTV2Channel channel = GetNTV2ChannelForIndex(i);
|
||||
if (::NTV2DeviceHasBiDirectionalSDI(deviceID)) {
|
||||
card->SetSDITransmitEnable(channel,
|
||||
mode == NTV2_MODE_DISPLAY);
|
||||
}
|
||||
card->SetSDIOut3GEnable(channel, rc.enable_3g_out);
|
||||
card->SetSDIOut3GbEnable(channel, rc.enable_3gb_out);
|
||||
card->SetSDIOut6GEnable(channel, rc.enable_6g_out);
|
||||
card->SetSDIOut12GEnable(channel, rc.enable_12g_out);
|
||||
card->SetSDIInLevelBtoLevelAConversion((UWord)i,
|
||||
rc.convert_3g_in);
|
||||
card->SetSDIOutLevelAtoLevelBConversion((UWord)i,
|
||||
rc.convert_3g_out);
|
||||
card->SetSDIOutRGBLevelAConversion((UWord)i,
|
||||
rc.enable_rgb_3ga_convert);
|
||||
}
|
||||
|
||||
// Apply Framestore settings
|
||||
for (uint32_t i = (uint32_t)start_channel_index;
|
||||
i < (start_channel_index + rc.num_framestores); i++) {
|
||||
NTV2Channel channel = GetNTV2ChannelForIndex(i);
|
||||
card->EnableChannel(channel);
|
||||
card->SetMode(channel, mode);
|
||||
card->SetVANCMode(NTV2_VANCMODE_OFF, channel);
|
||||
card->SetVideoFormat(vf, false, false, channel);
|
||||
card->SetFrameBufferFormat(channel, props.pixelFormat);
|
||||
card->SetTsiFrameEnable(rc.enable_tsi, channel);
|
||||
card->Set4kSquaresEnable(rc.enable_4k_squares, channel);
|
||||
card->SetQuadQuadSquaresEnable(rc.enable_8k_squares, channel);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Routing::ConfigureOutputRoute(const OutputProps &props, NTV2Mode mode,
|
||||
CNTV2Card *card)
|
||||
{
|
||||
if (!card)
|
||||
return false;
|
||||
|
||||
auto deviceID = props.deviceID;
|
||||
|
||||
NTV2OutputDestinations outputDests;
|
||||
aja::IOSelectionToOutputDests(props.ioSelect, outputDests);
|
||||
if (outputDests.empty()) {
|
||||
blog(LOG_DEBUG,
|
||||
"No Output Destinations specified to configure routing!");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto init_dest = *outputDests.begin();
|
||||
auto init_channel = NTV2OutputDestinationToChannel(init_dest);
|
||||
|
||||
RoutingConfig rc;
|
||||
if (NTV2_OUTPUT_DEST_IS_SDI(init_dest)) {
|
||||
SDIWireFormat swf = GuessSDIWireFormat(props.videoFormat,
|
||||
props.ioSelect,
|
||||
props.sdi4kTransport,
|
||||
props.deviceID);
|
||||
|
||||
if (swf == SDIWireFormat::Unknown) {
|
||||
blog(LOG_DEBUG, "Could not determine SDI Wire Format!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FindRoutingConfigSDI(swf, mode,
|
||||
NTV2_IS_FBF_RGB(props.pixelFormat),
|
||||
props.deviceID, rc)) {
|
||||
blog(LOG_DEBUG,
|
||||
"Could not find RoutingConfig for SDI Wire Format!");
|
||||
return false;
|
||||
}
|
||||
} else if (NTV2_OUTPUT_DEST_IS_HDMI(init_dest)) {
|
||||
HDMIWireFormat hwf = HDMIWireFormat::Unknown;
|
||||
|
||||
// special case devices...
|
||||
if (props.deviceID == DEVICE_ID_TTAP_PRO) {
|
||||
hwf = HDMIWireFormat::TTAP_PRO;
|
||||
} else {
|
||||
// ...all other devices.
|
||||
if (NTV2_IS_FBF_RGB(props.pixelFormat)) {
|
||||
if (NTV2_IS_HD_VIDEO_FORMAT(props.videoFormat))
|
||||
hwf = HDMIWireFormat::HD_RGB_LFR;
|
||||
} else {
|
||||
if (NTV2_IS_HD_VIDEO_FORMAT(
|
||||
props.videoFormat)) {
|
||||
hwf = HDMIWireFormat::HD_YCBCR_LFR;
|
||||
} else if (NTV2_IS_4K_VIDEO_FORMAT(
|
||||
props.videoFormat)) {
|
||||
hwf = HDMIWireFormat::UHD_4K_YCBCR_LFR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!FindRoutingConfigHDMI(hwf, mode,
|
||||
NTV2_IS_FBF_RGB(props.pixelFormat),
|
||||
props.deviceID, rc)) {
|
||||
blog(LOG_DEBUG,
|
||||
"Could not find RoutingConfig for HDMI Wire Format!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string route_string = rc.route_string;
|
||||
|
||||
// Replace framestore channel placeholders
|
||||
ULWord start_framestore_index = initial_framestore_output_index(
|
||||
deviceID, props.ioSelect, init_channel);
|
||||
for (ULWord c = 0; c < NTV2_MAX_NUM_CHANNELS; c++) {
|
||||
std::string fs_channel_placeholder =
|
||||
std::string("fb[{ch" + aja::to_string(c + 1) + "}]");
|
||||
route_string = aja::replace(
|
||||
route_string, fs_channel_placeholder,
|
||||
"fb[" + aja::to_string(start_framestore_index++) + "]");
|
||||
}
|
||||
|
||||
// Replace other channel placeholders
|
||||
ULWord start_channel_index = GetIndexForNTV2Channel(init_channel);
|
||||
for (ULWord c = 0; c < NTV2_MAX_NUM_CHANNELS; c++) {
|
||||
std::string channel_placeholder =
|
||||
std::string("{ch" + aja::to_string(c + 1) + "}");
|
||||
route_string =
|
||||
aja::replace(route_string, channel_placeholder,
|
||||
aja::to_string(start_channel_index++));
|
||||
}
|
||||
|
||||
NTV2XptConnections cnx;
|
||||
ParseRouteString(route_string, cnx);
|
||||
card->ApplySignalRoute(cnx, false);
|
||||
|
||||
// Apply SDI widget settings
|
||||
if (props.ioSelect != IOSelection::HDMIMonitorOut) {
|
||||
start_channel_index = GetIndexForNTV2Channel(init_channel);
|
||||
for (uint32_t i = (uint32_t)start_channel_index;
|
||||
i < (start_channel_index + rc.num_wires); i++) {
|
||||
NTV2Channel channel = GetNTV2ChannelForIndex(i);
|
||||
if (::NTV2DeviceHasBiDirectionalSDI(deviceID)) {
|
||||
card->SetSDITransmitEnable(
|
||||
channel, mode == NTV2_MODE_DISPLAY);
|
||||
}
|
||||
card->SetSDIOut3GEnable(channel, rc.enable_3g_out);
|
||||
card->SetSDIOut3GbEnable(channel, rc.enable_3gb_out);
|
||||
card->SetSDIOut6GEnable(channel, rc.enable_6g_out);
|
||||
card->SetSDIOut12GEnable(channel, rc.enable_12g_out);
|
||||
card->SetSDIInLevelBtoLevelAConversion(
|
||||
(UWord)i, rc.convert_3g_in);
|
||||
card->SetSDIOutLevelAtoLevelBConversion(
|
||||
(UWord)i, rc.convert_3g_out);
|
||||
card->SetSDIOutRGBLevelAConversion(
|
||||
(UWord)i, rc.enable_rgb_3ga_convert);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply Framestore settings
|
||||
start_framestore_index = initial_framestore_output_index(
|
||||
deviceID, props.ioSelect, init_channel);
|
||||
for (uint32_t i = (uint32_t)start_framestore_index;
|
||||
i < (start_framestore_index + rc.num_framestores); i++) {
|
||||
NTV2Channel channel = GetNTV2ChannelForIndex(i);
|
||||
card->EnableChannel(channel);
|
||||
card->SetMode(channel, mode);
|
||||
card->SetVANCMode(NTV2_VANCMODE_OFF, channel);
|
||||
card->SetVideoFormat(props.videoFormat, false, false, channel);
|
||||
card->SetFrameBufferFormat(channel, props.pixelFormat);
|
||||
card->SetTsiFrameEnable(rc.enable_tsi, channel);
|
||||
card->Set4kSquaresEnable(rc.enable_4k_squares, channel);
|
||||
card->SetQuadQuadSquaresEnable(rc.enable_8k_squares, channel);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ULWord Routing::initial_framestore_output_index(NTV2DeviceID deviceID,
|
||||
IOSelection io,
|
||||
NTV2Channel init_channel)
|
||||
{
|
||||
if (deviceID == DEVICE_ID_TTAP_PRO) {
|
||||
return 0;
|
||||
} else if (deviceID == DEVICE_ID_KONA1) {
|
||||
return 1;
|
||||
} else if (deviceID == DEVICE_ID_IO4K ||
|
||||
deviceID == DEVICE_ID_IO4KPLUS) {
|
||||
// SDI Monitor output uses framestore 4
|
||||
if (io == IOSelection::SDI5)
|
||||
return 3;
|
||||
}
|
||||
|
||||
// HDMI Monitor output uses framestore 4
|
||||
if (io == IOSelection::HDMIMonitorOut) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
return GetIndexForNTV2Channel(init_channel);
|
||||
}
|
||||
|
||||
// Output Routing
|
||||
void Routing::ConfigureOutputAudio(const OutputProps &props, CNTV2Card *card)
|
||||
{
|
||||
if (!card)
|
||||
return;
|
||||
|
||||
auto deviceID = card->GetDeviceID();
|
||||
auto audioSys = props.AudioSystem();
|
||||
auto channel = props.Channel();
|
||||
|
||||
card->SetNumberAudioChannels(props.audioNumChannels, audioSys);
|
||||
card->SetAudioRate(props.AudioRate(), audioSys);
|
||||
card->SetAudioBufferSize(NTV2_AUDIO_BUFFER_BIG, audioSys);
|
||||
card->SetAudioOutputDelay(audioSys, 0);
|
||||
|
||||
card->SetSDIOutputAudioSystem(channel, audioSys);
|
||||
card->SetSDIOutputDS2AudioSystem(channel, audioSys);
|
||||
|
||||
/* NOTE(paulh):
|
||||
* The SDK has a specifies an SDI audio system by Channel rather than by SDI output
|
||||
* and certain devices require setting the SDI audio system to NTV2_CHANNEL1.
|
||||
* i.e.
|
||||
* SDI 1 = NTV2_CHANNEL1
|
||||
* SDI 2 = NTV2_CHANNEL2
|
||||
* ...
|
||||
* SDI 5/Monitor = NTV2_CHANNEL5
|
||||
* etc...
|
||||
*
|
||||
* This fixes AJA internal bugs: 10730, 10986, 16274
|
||||
*/
|
||||
if (deviceID == DEVICE_ID_IOXT || deviceID == DEVICE_ID_IO4KUFC ||
|
||||
deviceID == DEVICE_ID_IO4KPLUS || deviceID == DEVICE_ID_KONA1 ||
|
||||
deviceID == DEVICE_ID_KONA3G || deviceID == DEVICE_ID_KONA4UFC ||
|
||||
deviceID == DEVICE_ID_KONA5 || deviceID == DEVICE_ID_KONA5_2X4K) {
|
||||
// Make sure SDI out 1 (aka Channel 1) is set to the correct sub-system
|
||||
card->SetSDIOutputAudioSystem(NTV2_CHANNEL1, audioSys);
|
||||
card->SetSDIOutputDS2AudioSystem(NTV2_CHANNEL1, audioSys);
|
||||
}
|
||||
|
||||
// make sure that audio is setup for the SDI monitor output on devices that support it
|
||||
if (NTV2DeviceCanDoWidget(deviceID, NTV2_WgtSDIMonOut1)) {
|
||||
card->SetSDIOutputAudioSystem(NTV2_CHANNEL5, audioSys);
|
||||
card->SetSDIOutputDS2AudioSystem(NTV2_CHANNEL5, audioSys);
|
||||
}
|
||||
|
||||
card->SetHDMIOutAudioRate(props.AudioRate());
|
||||
card->SetHDMIOutAudioFormat(NTV2_AUDIO_FORMAT_LPCM);
|
||||
|
||||
card->SetAudioOutputMonitorSource(NTV2_AudioChannel1_2, channel);
|
||||
card->SetAESOutputSource(NTV2_AudioChannel1_4, audioSys,
|
||||
NTV2_AudioChannel1_4);
|
||||
card->SetAESOutputSource(NTV2_AudioChannel5_8, audioSys,
|
||||
NTV2_AudioChannel5_8);
|
||||
card->SetAESOutputSource(NTV2_AudioChannel9_12, audioSys,
|
||||
NTV2_AudioChannel9_12);
|
||||
card->SetAESOutputSource(NTV2_AudioChannel13_16, audioSys,
|
||||
NTV2_AudioChannel13_16);
|
||||
|
||||
// make sure that audio is setup for HDMI output on devices that support it
|
||||
if (NTV2DeviceGetNumHDMIVideoOutputs(deviceID) > 0) {
|
||||
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->SetHDMIOutAudioChannels(NTV2_HDMIAudio8Channels);
|
||||
card->SetHDMIOutAudioSource2Channel(NTV2_AudioChannel1_2,
|
||||
audioSys);
|
||||
card->SetHDMIOutAudioSource8Channel(NTV2_AudioChannel1_8,
|
||||
audioSys);
|
||||
}
|
||||
|
||||
card->SetAudioLoopBack(NTV2_AUDIO_LOOPBACK_OFF, audioSys);
|
||||
|
||||
card->StopAudioOutput(audioSys);
|
||||
}
|
139
plugins/aja/aja-routing.hpp
Normal file
139
plugins/aja/aja-routing.hpp
Normal file
@ -0,0 +1,139 @@
|
||||
#pragma once
|
||||
#include "aja-props.hpp"
|
||||
|
||||
#include <ajantv2/includes/ntv2enums.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
class CNTV2Card;
|
||||
|
||||
/* The AJA hardware and NTV2 SDK uses a concept called "Signal Routing" to connect high-level firmware
|
||||
* blocks known as "Widgets" to one another via "crosspoint" connections. This facilitates streaming
|
||||
* data from one Widget to another to achieve specific functionality.
|
||||
* Such functionality may include SDI/HDMI capture/output, colorspace conversion, hardware LUTs, etc.
|
||||
*
|
||||
* This code references a table of RoutingConfig entries, where each entry contains the settings required
|
||||
* to configure an AJA device for a particular capture or output task. These settings include the number of
|
||||
* physical IO Widgets (SDI or HDMI) required, number of framestore Widgets required, register settings
|
||||
* that must be enabled or disabled, and a special short-hand "route string".
|
||||
* Of special note is the route string, which is parsed into a map of NTV2XptConnections. These connections
|
||||
* are then applied as the "signal route", connecting the Widget's crosspoints together.
|
||||
*/
|
||||
|
||||
struct RoutingConfig {
|
||||
NTV2Mode mode; // capture or playout?
|
||||
uint32_t num_wires; // number of physical connections
|
||||
uint32_t num_framestores; // number of framestores used
|
||||
bool enable_3g_out; // enable register for 3G SDI Output?
|
||||
bool enable_6g_out; // enable register for 6G SDI Output?
|
||||
bool enable_12g_out; // enable register for 12G SDI Output?
|
||||
bool convert_3g_in; // enable register for 3G level-B -> level-A SDI input conversion?
|
||||
bool convert_3g_out; // enable register for 3G level-A -> level-B SDI output conversion?
|
||||
bool enable_rgb_3ga_convert; // enable register for RGB 3G level-B -> level-A SDI output conversion?
|
||||
bool enable_3gb_out; // enable register for 3G level-B SDI output?
|
||||
bool enable_4k_squares; // enable register for 4K square division?
|
||||
bool enable_8k_squares; // enable register for 8K square division?
|
||||
bool enable_tsi; // enable register for two-sample interleave (UHD/4K/8K)
|
||||
std::string
|
||||
route_string; // signal routing shorthand string to parse into crosspoint connections
|
||||
};
|
||||
|
||||
/* This table is used to correlate a particular "raster definition" (i.e. SD/HD/4K/etc.)
|
||||
* and SMPTE VPID transport byte (VPIDStandard) to an SDIWireFormat enum.
|
||||
* This allows mapping SDI video signals to the correct format, particularly in the case
|
||||
* where multiple SDI formats share the same VPID transport value.
|
||||
* For example: VPIDStandard_1080 (0x85) is used on the wire for both single-link (1x SDI wire)
|
||||
* 1080-line HD SDI video AND quad-link (4x SDI wires) UHD/4K "square-division" video.
|
||||
*/
|
||||
using VPIDSpec = std::pair<RasterDefinition, VPIDStandard>;
|
||||
|
||||
static const std::map<VPIDSpec, SDIWireFormat> kSDIWireFormats = {
|
||||
{{RasterDefinition::SD, VPIDStandard_483_576}, SDIWireFormat::SD_ST352},
|
||||
{{RasterDefinition::HD, VPIDStandard_720},
|
||||
SDIWireFormat::HD_720p_ST292},
|
||||
{{RasterDefinition::HD, VPIDStandard_1080},
|
||||
SDIWireFormat::HD_1080_ST292},
|
||||
{{RasterDefinition::HD, VPIDStandard_1080_DualLink},
|
||||
SDIWireFormat::HD_1080_ST372_Dual},
|
||||
{{RasterDefinition::HD, VPIDStandard_720_3Ga},
|
||||
SDIWireFormat::HD_720p_ST425_3Ga},
|
||||
{{RasterDefinition::HD, VPIDStandard_1080_3Ga},
|
||||
SDIWireFormat::HD_1080p_ST425_3Ga},
|
||||
{{RasterDefinition::HD, VPIDStandard_1080_DualLink_3Gb},
|
||||
SDIWireFormat::HD_1080p_ST425_3Gb_DL},
|
||||
{{RasterDefinition::HD, VPIDStandard_720_3Gb},
|
||||
SDIWireFormat::HD_720p_ST425_3Gb},
|
||||
{{RasterDefinition::HD, VPIDStandard_1080_3Gb},
|
||||
SDIWireFormat::HD_1080p_ST425_3Gb},
|
||||
{{RasterDefinition::HD, VPIDStandard_1080_Dual_3Ga},
|
||||
SDIWireFormat::HD_1080p_ST425_Dual_3Ga},
|
||||
{{RasterDefinition::HD, VPIDStandard_1080_Dual_3Gb},
|
||||
SDIWireFormat::HD_1080p_ST425_Dual_3Gb},
|
||||
{{RasterDefinition::UHD_4K, VPIDStandard_1080_3Gb},
|
||||
SDIWireFormat::UHD4K_ST292_Dual_1_5_Squares},
|
||||
{{RasterDefinition::UHD_4K, VPIDStandard_1080},
|
||||
SDIWireFormat::UHD4K_ST292_Quad_1_5_Squares},
|
||||
{{RasterDefinition::UHD_4K, VPIDStandard_1080_3Ga},
|
||||
SDIWireFormat::UHD4K_ST425_Quad_3Ga_Squares},
|
||||
{{RasterDefinition::UHD_4K, VPIDStandard_1080_DualLink_3Gb},
|
||||
SDIWireFormat::UHD4K_ST425_Quad_3Gb_Squares},
|
||||
{{RasterDefinition::UHD_4K, VPIDStandard_2160_DualLink},
|
||||
SDIWireFormat::UHD4K_ST425_Dual_3Gb_2SI},
|
||||
{{RasterDefinition::UHD_4K, VPIDStandard_2160_QuadLink_3Ga},
|
||||
SDIWireFormat::UHD4K_ST425_Quad_3Ga_2SI},
|
||||
{{RasterDefinition::UHD_4K, VPIDStandard_2160_QuadDualLink_3Gb},
|
||||
SDIWireFormat::UHD4K_ST425_Quad_3Gb_2SI},
|
||||
{{RasterDefinition::UHD_4K, VPIDStandard_2160_Single_6Gb},
|
||||
SDIWireFormat::UHD4K_ST2018_6G_Squares_2SI},
|
||||
{{RasterDefinition::UHD_4K_Retail_12G, VPIDStandard_2160_Single_6Gb},
|
||||
SDIWireFormat::UHD4K_ST2018_6G_Squares_2SI_Kona5_io4KPlus},
|
||||
{{RasterDefinition::UHD_4K, VPIDStandard_2160_Single_12Gb},
|
||||
SDIWireFormat::UHD4K_ST2018_12G_Squares_2SI},
|
||||
{{RasterDefinition::UHD_4K_Retail_12G, VPIDStandard_2160_Single_12Gb},
|
||||
SDIWireFormat::UHD4K_ST2018_12G_Squares_2SI_Kona5_io4KPlus},
|
||||
{{RasterDefinition::UHD2_8K, VPIDStandard_4320_DualLink_12Gb},
|
||||
SDIWireFormat::UHD28K_ST2082_Dual_12G},
|
||||
{{RasterDefinition::UHD2_8K, VPIDStandard_2160_DualLink_12Gb},
|
||||
SDIWireFormat::UHD28K_ST2082_RGB_Dual_12G},
|
||||
{{RasterDefinition::UHD2_8K, VPIDStandard_4320_QuadLink_12Gb},
|
||||
SDIWireFormat::UHD28K_ST2082_Quad_12G},
|
||||
};
|
||||
|
||||
extern RasterDefinition
|
||||
GetRasterDefinition(IOSelection io, NTV2VideoFormat vf,
|
||||
NTV2DeviceID deviceID = DEVICE_ID_NOTFOUND);
|
||||
|
||||
extern std::string RasterDefinitionToString(RasterDefinition rd);
|
||||
|
||||
// Applies RoutingConfig settings to the card to configure a specific SDI/HDMI capture/output mode.
|
||||
class Routing {
|
||||
public:
|
||||
static bool ParseRouteString(const std::string &route,
|
||||
NTV2XptConnections &cnx);
|
||||
static bool DetermineSDIWireFormat(NTV2DeviceID deviceID, VPIDSpec spec,
|
||||
SDIWireFormat &swf);
|
||||
static bool FindRoutingConfigHDMI(HDMIWireFormat hwf, NTV2Mode mode,
|
||||
bool isRGB, NTV2DeviceID deviceID,
|
||||
RoutingConfig &routing);
|
||||
static bool FindRoutingConfigSDI(SDIWireFormat swf, NTV2Mode mode,
|
||||
bool isRGB, NTV2DeviceID deviceID,
|
||||
RoutingConfig &routing);
|
||||
|
||||
static void StartSourceAudio(const SourceProps &props, CNTV2Card *card);
|
||||
|
||||
static void StopSourceAudio(const SourceProps &props, CNTV2Card *card);
|
||||
|
||||
static bool ConfigureSourceRoute(const SourceProps &props,
|
||||
NTV2Mode mode, CNTV2Card *card);
|
||||
static bool ConfigureOutputRoute(const OutputProps &props,
|
||||
NTV2Mode mode, CNTV2Card *card);
|
||||
static ULWord initial_framestore_output_index(NTV2DeviceID deviceID,
|
||||
IOSelection io,
|
||||
NTV2Channel init_channel);
|
||||
|
||||
static void ConfigureOutputAudio(const OutputProps &props,
|
||||
CNTV2Card *card);
|
||||
};
|
1184
plugins/aja/aja-source.cpp
Normal file
1184
plugins/aja/aja-source.cpp
Normal file
File diff suppressed because it is too large
Load Diff
83
plugins/aja/aja-source.hpp
Normal file
83
plugins/aja/aja-source.hpp
Normal file
@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
#include "aja-props.hpp"
|
||||
|
||||
#include <obs-module.h>
|
||||
|
||||
#include <ajantv2/includes/ajatypes.h>
|
||||
#include <ajantv2/includes/ntv2testpatterngen.h>
|
||||
|
||||
#include <ajabase/common/types.h>
|
||||
#include <ajabase/system/thread.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
class CNTV2Card;
|
||||
|
||||
class AJASource {
|
||||
public:
|
||||
explicit AJASource(obs_source_t *source);
|
||||
|
||||
~AJASource();
|
||||
|
||||
void SetCard(CNTV2Card *card);
|
||||
CNTV2Card *GetCard();
|
||||
|
||||
void SetOBSSource(obs_source_t *source);
|
||||
obs_source_t *GetOBSSource(void) const;
|
||||
void SetName(const std::string &name);
|
||||
std::string GetName() const;
|
||||
|
||||
void GenerateTestPattern(NTV2VideoFormat vf, NTV2PixelFormat pf,
|
||||
NTV2TestPatternSelect ps);
|
||||
|
||||
// Capture Thread stuff
|
||||
static void CaptureThread(AJAThread *thread, void *data);
|
||||
void Activate(bool enable = false);
|
||||
void Deactivate();
|
||||
bool IsCapturing() const;
|
||||
void SetCapturing(bool capturing);
|
||||
|
||||
// CardEntry/Device stuff
|
||||
std::string CardID() const;
|
||||
void SetCardID(const std::string &cardID);
|
||||
uint32_t DeviceIndex() const;
|
||||
void SetDeviceIndex(uint32_t index);
|
||||
|
||||
// Source Props
|
||||
void SetSourceProps(const SourceProps &props);
|
||||
SourceProps GetSourceProps() const;
|
||||
|
||||
bool ReadChannelVPIDs(NTV2Channel channel, VPIDData &vpids);
|
||||
|
||||
bool ReadWireFormats(NTV2DeviceID device_id,
|
||||
const NTV2InputSourceSet &srcs,
|
||||
NTV2VideoFormat &vf, NTV2PixelFormat &pf,
|
||||
VPIDDataList &vpids);
|
||||
|
||||
void ResetVideoBuffer(NTV2VideoFormat vf, NTV2PixelFormat pf);
|
||||
|
||||
void ResetAudioBuffer(size_t size);
|
||||
|
||||
NTV2_POINTER mVideoBuffer;
|
||||
NTV2_POINTER mAudioBuffer;
|
||||
|
||||
private:
|
||||
CNTV2Card *mCard;
|
||||
|
||||
std::string mSourceName;
|
||||
std::string mCardID;
|
||||
UWord mDeviceIndex;
|
||||
|
||||
bool mBuffering;
|
||||
bool mIsCapturing;
|
||||
|
||||
SourceProps mSourceProps;
|
||||
|
||||
NTV2TestPatternBuffer mTestPattern;
|
||||
|
||||
AJAThread *mCaptureThread;
|
||||
std::mutex mMutex;
|
||||
|
||||
obs_source_t *mSource;
|
||||
};
|
91
plugins/aja/aja-ui-props.hpp
Normal file
91
plugins/aja/aja-ui-props.hpp
Normal file
@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
|
||||
#include <obs-module.h>
|
||||
|
||||
static const char *kProgramOutputID = "aja_output";
|
||||
static const char *kPreviewOutputID = "aja_preview_output";
|
||||
|
||||
struct UIProperty {
|
||||
const char *id;
|
||||
const char *text;
|
||||
const char *tooltip;
|
||||
};
|
||||
|
||||
static const UIProperty kUIPropCaptureModule = {
|
||||
"aja_source",
|
||||
obs_module_text("AJACapture.Device"),
|
||||
"",
|
||||
};
|
||||
|
||||
static const UIProperty kUIPropOutputModule = {
|
||||
"aja_output",
|
||||
obs_module_text("AJAOutput.Device"),
|
||||
"",
|
||||
};
|
||||
|
||||
// This is used as an "invisible" property to give the program and preview
|
||||
// plugin instances an identifier before the output has been created/started.
|
||||
// This ID is then used by the CardManager class for tracking device channel
|
||||
// usage across the capture and output plugin instances.
|
||||
static const UIProperty kUIPropAJAOutputID = {
|
||||
"aja_output_id",
|
||||
"",
|
||||
"",
|
||||
};
|
||||
|
||||
static const UIProperty kUIPropDevice = {
|
||||
"ui_prop_device",
|
||||
obs_module_text("Device"),
|
||||
"",
|
||||
};
|
||||
|
||||
static const UIProperty kUIPropOutput = {
|
||||
"ui_prop_output",
|
||||
obs_module_text("Output"),
|
||||
"",
|
||||
};
|
||||
|
||||
static const UIProperty kUIPropInput = {
|
||||
"ui_prop_input",
|
||||
obs_module_text("Input"),
|
||||
"",
|
||||
};
|
||||
|
||||
static const UIProperty kUIPropIOSelect = {"ui_prop_select_input",
|
||||
obs_module_text("IOSelect"), ""};
|
||||
|
||||
static const UIProperty kUIPropSDI4KTransport = {
|
||||
"ui_prop_sdi_transport",
|
||||
obs_module_text("SDI4KTransport"),
|
||||
"",
|
||||
};
|
||||
|
||||
static const UIProperty kUIPropVideoFormatSelect = {
|
||||
"ui_prop_vid_fmt",
|
||||
obs_module_text("VideoFormat"),
|
||||
"",
|
||||
};
|
||||
|
||||
static const UIProperty kUIPropPixelFormatSelect = {
|
||||
"ui_prop_pix_fmt",
|
||||
obs_module_text("PixelFormat"),
|
||||
"",
|
||||
};
|
||||
|
||||
static const UIProperty kUIPropAutoStartOutput = {
|
||||
"ui_prop_auto_start_output",
|
||||
obs_module_text("AutoStart"),
|
||||
"",
|
||||
};
|
||||
|
||||
static const UIProperty kUIPropDeactivateWhenNotShowing = {
|
||||
"ui_prop_deactivate_when_not_showing",
|
||||
obs_module_text("DeactivateWhenNotShowing"),
|
||||
"",
|
||||
};
|
||||
|
||||
static const UIProperty kUIPropBuffering = {
|
||||
"ui_prop_buffering",
|
||||
obs_module_text("Buffering"),
|
||||
"",
|
||||
};
|
475
plugins/aja/aja-widget-io.cpp
Normal file
475
plugins/aja/aja-widget-io.cpp
Normal file
@ -0,0 +1,475 @@
|
||||
#include "aja-widget-io.hpp"
|
||||
|
||||
#include <ajantv2/includes/ntv2utils.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
// firmware widget nicknames used by signal routing syntax parser
|
||||
static const char *kFramebufferNickname = "fb";
|
||||
static const char *kCSCNickname = "csc";
|
||||
static const char *kDualLinkInNickname = "dli";
|
||||
static const char *kDualLinkOutNickname = "dlo";
|
||||
static const char *kLUTNickname = "lut";
|
||||
static const char *kSDINickname = "sdi";
|
||||
static const char *kMultiLinkNickname = "ml";
|
||||
static const char *kMixerNickname = "mix";
|
||||
static const char *kHDMINickname = "hdmi";
|
||||
static const char *kLUT3DNickname = "lut3d";
|
||||
static const char *k4KDownConvertNickname = "4kdc";
|
||||
static const char *kAnalogNickname = "analog";
|
||||
static const char *kTSIMuxNickname = "tsi";
|
||||
static const char *kUpDownConvertNickname = "udc";
|
||||
static const char *kCompositeNickname = "composite";
|
||||
static const char *kStereoCompNickname = "stereo";
|
||||
static const char *kWatermarkNickname = "watermark";
|
||||
static const char *kBlackNickname = "black";
|
||||
static const char *kCompressionNickname = "comp";
|
||||
static const char *kFrameSyncNickname = "fsync";
|
||||
static const char *kTestPatternNickname = "pat";
|
||||
static const char *kOENickname = "oe";
|
||||
|
||||
// Table of firmware widget's input crosspoint/id/channel/name/datastream index
|
||||
// clang-format off
|
||||
static const WidgetInputSocket kWidgetInputSockets[] = {
|
||||
//NTV2InputCrosspointID | NTV2WidgetID | NTV2Channel | Name | DatastreamIndex
|
||||
{ NTV2_INPUT_CROSSPOINT_INVALID, NTV2_WIDGET_INVALID, NTV2_CHANNEL_INVALID, "", -1},
|
||||
{ NTV2_XptFrameBuffer1Input, NTV2_WgtFrameBuffer1, NTV2_CHANNEL1, kFramebufferNickname, 0},
|
||||
{ NTV2_XptFrameBuffer1BInput, NTV2_WgtFrameBuffer1, NTV2_CHANNEL1, kFramebufferNickname, 1},
|
||||
{ NTV2_XptFrameBuffer2Input, NTV2_WgtFrameBuffer2, NTV2_CHANNEL2, kFramebufferNickname, 0},
|
||||
{ NTV2_XptFrameBuffer2BInput, NTV2_WgtFrameBuffer2, NTV2_CHANNEL2, kFramebufferNickname, 1},
|
||||
{ NTV2_XptFrameBuffer3Input, NTV2_WgtFrameBuffer3, NTV2_CHANNEL3, kFramebufferNickname, 0},
|
||||
{ NTV2_XptFrameBuffer3BInput, NTV2_WgtFrameBuffer3, NTV2_CHANNEL3, kFramebufferNickname, 1},
|
||||
{ NTV2_XptFrameBuffer4Input, NTV2_WgtFrameBuffer4, NTV2_CHANNEL4, kFramebufferNickname, 0},
|
||||
{ NTV2_XptFrameBuffer4BInput, NTV2_WgtFrameBuffer4, NTV2_CHANNEL4, kFramebufferNickname, 1},
|
||||
{ NTV2_XptFrameBuffer5Input, NTV2_WgtFrameBuffer5, NTV2_CHANNEL5, kFramebufferNickname, 0},
|
||||
{ NTV2_XptFrameBuffer5BInput, NTV2_WgtFrameBuffer5, NTV2_CHANNEL5, kFramebufferNickname, 1},
|
||||
{ NTV2_XptFrameBuffer6Input, NTV2_WgtFrameBuffer6, NTV2_CHANNEL6, kFramebufferNickname, 0},
|
||||
{ NTV2_XptFrameBuffer6BInput, NTV2_WgtFrameBuffer6, NTV2_CHANNEL6, kFramebufferNickname, 1},
|
||||
{ NTV2_XptFrameBuffer7Input, NTV2_WgtFrameBuffer7, NTV2_CHANNEL7, kFramebufferNickname, 0},
|
||||
{ NTV2_XptFrameBuffer7BInput, NTV2_WgtFrameBuffer7, NTV2_CHANNEL7, kFramebufferNickname, 1},
|
||||
{ NTV2_XptFrameBuffer8Input, NTV2_WgtFrameBuffer8, NTV2_CHANNEL8, kFramebufferNickname, 0},
|
||||
{ NTV2_XptFrameBuffer8BInput, NTV2_WgtFrameBuffer8, NTV2_CHANNEL8, kFramebufferNickname, 1},
|
||||
{ NTV2_XptCSC1VidInput, NTV2_WgtCSC1, NTV2_CHANNEL1, kCSCNickname, 0},
|
||||
{ NTV2_XptCSC1KeyInput, NTV2_WgtCSC1, NTV2_CHANNEL1, kCSCNickname, 1},
|
||||
{ NTV2_XptCSC2VidInput, NTV2_WgtCSC2, NTV2_CHANNEL2, kCSCNickname, 0},
|
||||
{ NTV2_XptCSC2KeyInput, NTV2_WgtCSC2, NTV2_CHANNEL2, kCSCNickname, 1},
|
||||
{ NTV2_XptCSC3VidInput, NTV2_WgtCSC3, NTV2_CHANNEL3, kCSCNickname, 0},
|
||||
{ NTV2_XptCSC3KeyInput, NTV2_WgtCSC3, NTV2_CHANNEL3, kCSCNickname, 1},
|
||||
{ NTV2_XptCSC4VidInput, NTV2_WgtCSC4, NTV2_CHANNEL4, kCSCNickname, 0},
|
||||
{ NTV2_XptCSC4KeyInput, NTV2_WgtCSC4, NTV2_CHANNEL4, kCSCNickname, 1},
|
||||
{ NTV2_XptCSC5VidInput, NTV2_WgtCSC5, NTV2_CHANNEL5, kCSCNickname, 0},
|
||||
{ NTV2_XptCSC5KeyInput, NTV2_WgtCSC5, NTV2_CHANNEL5, kCSCNickname, 1},
|
||||
{ NTV2_XptCSC6VidInput, NTV2_WgtCSC6, NTV2_CHANNEL6, kCSCNickname, 0},
|
||||
{ NTV2_XptCSC6KeyInput, NTV2_WgtCSC6, NTV2_CHANNEL6, kCSCNickname, 1},
|
||||
{ NTV2_XptCSC7VidInput, NTV2_WgtCSC7, NTV2_CHANNEL7, kCSCNickname, 0},
|
||||
{ NTV2_XptCSC7KeyInput, NTV2_WgtCSC7, NTV2_CHANNEL7, kCSCNickname, 1},
|
||||
{ NTV2_XptCSC8VidInput, NTV2_WgtCSC8, NTV2_CHANNEL8, kCSCNickname, 0},
|
||||
{ NTV2_XptCSC8KeyInput, NTV2_WgtCSC8, NTV2_CHANNEL8, kCSCNickname, 1},
|
||||
{ NTV2_XptLUT1Input, NTV2_WgtLUT1, NTV2_CHANNEL1, kLUTNickname, 0},
|
||||
{ NTV2_XptLUT2Input, NTV2_WgtLUT2, NTV2_CHANNEL2, kLUTNickname, 0},
|
||||
{ NTV2_XptLUT3Input, NTV2_WgtLUT3, NTV2_CHANNEL3, kLUTNickname, 0},
|
||||
{ NTV2_XptLUT4Input, NTV2_WgtLUT4, NTV2_CHANNEL4, kLUTNickname, 0},
|
||||
{ NTV2_XptLUT5Input, NTV2_WgtLUT5, NTV2_CHANNEL5, kLUTNickname, 0},
|
||||
{ NTV2_XptLUT6Input, NTV2_WgtLUT6, NTV2_CHANNEL6, kLUTNickname, 0},
|
||||
{ NTV2_XptLUT7Input, NTV2_WgtLUT7, NTV2_CHANNEL7, kLUTNickname, 0},
|
||||
{ NTV2_XptLUT8Input, NTV2_WgtLUT8, NTV2_CHANNEL8, kLUTNickname, 0},
|
||||
{ NTV2_XptMultiLinkOut1Input, NTV2_WgtMultiLinkOut1, NTV2_CHANNEL1, kMultiLinkNickname, 0},
|
||||
{ NTV2_XptMultiLinkOut1InputDS2, NTV2_WgtMultiLinkOut1, NTV2_CHANNEL1, kMultiLinkNickname, 0},
|
||||
{ NTV2_XptMultiLinkOut2Input, NTV2_WgtMultiLinkOut2, NTV2_CHANNEL1, kMultiLinkNickname, 0},
|
||||
{ NTV2_XptMultiLinkOut2InputDS2, NTV2_WgtMultiLinkOut2, NTV2_CHANNEL1, kMultiLinkNickname, 0},
|
||||
{ NTV2_XptSDIOut1Input, NTV2_WgtSDIOut1, NTV2_CHANNEL1, kSDINickname, 0},
|
||||
{ NTV2_XptSDIOut1InputDS2, NTV2_Wgt3GSDIOut1, NTV2_CHANNEL1, kSDINickname, 1},
|
||||
{ NTV2_XptSDIOut2Input, NTV2_WgtSDIOut2, NTV2_CHANNEL2, kSDINickname, 0},
|
||||
{ NTV2_XptSDIOut2InputDS2, NTV2_Wgt3GSDIOut2, NTV2_CHANNEL2, kSDINickname, 1},
|
||||
{ NTV2_XptSDIOut3Input, NTV2_WgtSDIOut3, NTV2_CHANNEL3, kSDINickname, 0},
|
||||
{ NTV2_XptSDIOut3InputDS2, NTV2_Wgt3GSDIOut3, NTV2_CHANNEL3, kSDINickname, 1},
|
||||
{ NTV2_XptSDIOut4Input, NTV2_WgtSDIOut4, NTV2_CHANNEL4, kSDINickname, 0},
|
||||
{ NTV2_XptSDIOut4InputDS2, NTV2_Wgt3GSDIOut4, NTV2_CHANNEL4, kSDINickname, 1},
|
||||
{ NTV2_XptSDIOut5Input, NTV2_WgtSDIMonOut1, NTV2_CHANNEL5, kSDINickname, 0},
|
||||
{ NTV2_XptSDIOut5InputDS2, NTV2_WgtSDIMonOut1, NTV2_CHANNEL5, kSDINickname, 1},
|
||||
{ NTV2_XptSDIOut6Input, NTV2_Wgt3GSDIOut6, NTV2_CHANNEL6, kSDINickname, 0},
|
||||
{ NTV2_XptSDIOut6InputDS2, NTV2_Wgt3GSDIOut6, NTV2_CHANNEL6, kSDINickname, 1},
|
||||
{ NTV2_XptSDIOut7Input, NTV2_Wgt3GSDIOut7, NTV2_CHANNEL7, kSDINickname, 0},
|
||||
{ NTV2_XptSDIOut7InputDS2, NTV2_Wgt3GSDIOut7, NTV2_CHANNEL7, kSDINickname, 1},
|
||||
{ NTV2_XptSDIOut8Input, NTV2_Wgt3GSDIOut8, NTV2_CHANNEL8, kSDINickname, 0},
|
||||
{ NTV2_XptSDIOut8InputDS2, NTV2_Wgt3GSDIOut8, NTV2_CHANNEL8, kSDINickname, 1},
|
||||
{ NTV2_XptDualLinkIn1Input, NTV2_WgtDualLinkV2In1, NTV2_CHANNEL1, kDualLinkInNickname, 0},
|
||||
{ NTV2_XptDualLinkIn1DSInput, NTV2_WgtDualLinkV2In1, NTV2_CHANNEL1, kDualLinkInNickname, 1},
|
||||
{ NTV2_XptDualLinkIn2Input, NTV2_WgtDualLinkV2In2, NTV2_CHANNEL2, kDualLinkInNickname, 0},
|
||||
{ NTV2_XptDualLinkIn2DSInput, NTV2_WgtDualLinkV2In2, NTV2_CHANNEL2, kDualLinkInNickname, 1},
|
||||
{ NTV2_XptDualLinkIn3Input, NTV2_WgtDualLinkV2In3, NTV2_CHANNEL3, kDualLinkInNickname, 0},
|
||||
{ NTV2_XptDualLinkIn3DSInput, NTV2_WgtDualLinkV2In3, NTV2_CHANNEL3, kDualLinkInNickname, 1},
|
||||
{ NTV2_XptDualLinkIn4Input, NTV2_WgtDualLinkV2In4, NTV2_CHANNEL4, kDualLinkInNickname, 0},
|
||||
{ NTV2_XptDualLinkIn4DSInput, NTV2_WgtDualLinkV2In4, NTV2_CHANNEL4, kDualLinkInNickname, 1},
|
||||
{ NTV2_XptDualLinkIn5Input, NTV2_WgtDualLinkV2In5, NTV2_CHANNEL5, kDualLinkInNickname, 0},
|
||||
{ NTV2_XptDualLinkIn5DSInput, NTV2_WgtDualLinkV2In5, NTV2_CHANNEL5, kDualLinkInNickname, 1},
|
||||
{ NTV2_XptDualLinkIn6Input, NTV2_WgtDualLinkV2In6, NTV2_CHANNEL6, kDualLinkInNickname, 0},
|
||||
{ NTV2_XptDualLinkIn6DSInput, NTV2_WgtDualLinkV2In6, NTV2_CHANNEL6, kDualLinkInNickname, 1},
|
||||
{ NTV2_XptDualLinkIn7Input, NTV2_WgtDualLinkV2In7, NTV2_CHANNEL7, kDualLinkInNickname, 0},
|
||||
{ NTV2_XptDualLinkIn7DSInput, NTV2_WgtDualLinkV2In7, NTV2_CHANNEL7, kDualLinkInNickname, 1},
|
||||
{ NTV2_XptDualLinkIn8Input, NTV2_WgtDualLinkV2In8, NTV2_CHANNEL8, kDualLinkInNickname, 0},
|
||||
{ NTV2_XptDualLinkIn8DSInput, NTV2_WgtDualLinkV2In8, NTV2_CHANNEL8, kDualLinkInNickname, 1},
|
||||
{ NTV2_XptDualLinkOut1Input, NTV2_WgtDualLinkV2Out1, NTV2_CHANNEL1, kDualLinkOutNickname, 0},
|
||||
{ NTV2_XptDualLinkOut2Input, NTV2_WgtDualLinkV2Out2, NTV2_CHANNEL2, kDualLinkOutNickname, 0},
|
||||
{ NTV2_XptDualLinkOut3Input, NTV2_WgtDualLinkV2Out3, NTV2_CHANNEL3, kDualLinkOutNickname, 0},
|
||||
{ NTV2_XptDualLinkOut4Input, NTV2_WgtDualLinkV2Out4, NTV2_CHANNEL4, kDualLinkOutNickname, 0},
|
||||
{ NTV2_XptDualLinkOut5Input, NTV2_WgtDualLinkV2Out5, NTV2_CHANNEL5, kDualLinkOutNickname, 0},
|
||||
{ NTV2_XptDualLinkOut6Input, NTV2_WgtDualLinkV2Out6, NTV2_CHANNEL6, kDualLinkOutNickname, 0},
|
||||
{ NTV2_XptDualLinkOut7Input, NTV2_WgtDualLinkV2Out7, NTV2_CHANNEL7, kDualLinkOutNickname, 0},
|
||||
{ NTV2_XptDualLinkOut8Input, NTV2_WgtDualLinkV2Out8, NTV2_CHANNEL8, kDualLinkOutNickname, 0},
|
||||
{ NTV2_XptMixer1BGKeyInput, NTV2_WgtMixer1, NTV2_CHANNEL1, kMixerNickname, 3},
|
||||
{ NTV2_XptMixer1BGVidInput, NTV2_WgtMixer1, NTV2_CHANNEL1, kMixerNickname, 2},
|
||||
{ NTV2_XptMixer1FGKeyInput, NTV2_WgtMixer1, NTV2_CHANNEL1, kMixerNickname, 1},
|
||||
{ NTV2_XptMixer1FGVidInput, NTV2_WgtMixer1, NTV2_CHANNEL1, kMixerNickname, 0},
|
||||
{ NTV2_XptMixer2BGKeyInput, NTV2_WgtMixer2, NTV2_CHANNEL2, kMixerNickname, 3},
|
||||
{ NTV2_XptMixer2BGVidInput, NTV2_WgtMixer2, NTV2_CHANNEL2, kMixerNickname, 2},
|
||||
{ NTV2_XptMixer2FGKeyInput, NTV2_WgtMixer2, NTV2_CHANNEL2, kMixerNickname, 1},
|
||||
{ NTV2_XptMixer2FGVidInput, NTV2_WgtMixer2, NTV2_CHANNEL2, kMixerNickname, 0},
|
||||
{ NTV2_XptMixer3BGKeyInput, NTV2_WgtMixer3, NTV2_CHANNEL3, kMixerNickname, 3},
|
||||
{ NTV2_XptMixer3BGVidInput, NTV2_WgtMixer3, NTV2_CHANNEL3, kMixerNickname, 2},
|
||||
{ NTV2_XptMixer3FGKeyInput, NTV2_WgtMixer3, NTV2_CHANNEL3, kMixerNickname, 1},
|
||||
{ NTV2_XptMixer3FGVidInput, NTV2_WgtMixer3, NTV2_CHANNEL3, kMixerNickname, 0},
|
||||
{ NTV2_XptMixer4BGKeyInput, NTV2_WgtMixer4, NTV2_CHANNEL4, kMixerNickname, 3},
|
||||
{ NTV2_XptMixer4BGVidInput, NTV2_WgtMixer4, NTV2_CHANNEL4, kMixerNickname, 2},
|
||||
{ NTV2_XptMixer4FGKeyInput, NTV2_WgtMixer4, NTV2_CHANNEL4, kMixerNickname, 1},
|
||||
{ NTV2_XptMixer4FGVidInput, NTV2_WgtMixer4, NTV2_CHANNEL4, kMixerNickname, 0},
|
||||
{ NTV2_XptHDMIOutInput, NTV2_WgtHDMIOut1, NTV2_CHANNEL1, kHDMINickname, 0},
|
||||
{ NTV2_XptHDMIOutQ2Input, NTV2_WgtHDMIOut1v2, NTV2_CHANNEL1, kHDMINickname, 1},
|
||||
{ NTV2_XptHDMIOutQ3Input, NTV2_WgtHDMIOut1v2, NTV2_CHANNEL1, kHDMINickname, 2},
|
||||
{ NTV2_XptHDMIOutQ4Input, NTV2_WgtHDMIOut1v2, NTV2_CHANNEL1, kHDMINickname, 3},
|
||||
{ NTV2_Xpt4KDCQ1Input, NTV2_Wgt4KDownConverter, NTV2_CHANNEL1, k4KDownConvertNickname, 0},
|
||||
{ NTV2_Xpt4KDCQ2Input, NTV2_Wgt4KDownConverter, NTV2_CHANNEL2, k4KDownConvertNickname, 0},
|
||||
{ NTV2_Xpt4KDCQ3Input, NTV2_Wgt4KDownConverter, NTV2_CHANNEL3, k4KDownConvertNickname, 0},
|
||||
{ NTV2_Xpt4KDCQ4Input, NTV2_Wgt4KDownConverter, NTV2_CHANNEL4, k4KDownConvertNickname, 0},
|
||||
{ NTV2_Xpt425Mux1AInput, NTV2_Wgt425Mux1, NTV2_CHANNEL1, kTSIMuxNickname, 0},
|
||||
{ NTV2_Xpt425Mux1BInput, NTV2_Wgt425Mux1, NTV2_CHANNEL1, kTSIMuxNickname, 1},
|
||||
{ NTV2_Xpt425Mux2AInput, NTV2_Wgt425Mux2, NTV2_CHANNEL2, kTSIMuxNickname, 0},
|
||||
{ NTV2_Xpt425Mux2BInput, NTV2_Wgt425Mux2, NTV2_CHANNEL2, kTSIMuxNickname, 1},
|
||||
{ NTV2_Xpt425Mux3AInput, NTV2_Wgt425Mux3, NTV2_CHANNEL3, kTSIMuxNickname, 0},
|
||||
{ NTV2_Xpt425Mux3BInput, NTV2_Wgt425Mux3, NTV2_CHANNEL3, kTSIMuxNickname, 1},
|
||||
{ NTV2_Xpt425Mux4AInput, NTV2_Wgt425Mux4, NTV2_CHANNEL4, kTSIMuxNickname, 0},
|
||||
{ NTV2_Xpt425Mux4BInput, NTV2_Wgt425Mux4, NTV2_CHANNEL4, kTSIMuxNickname, 1},
|
||||
{ NTV2_XptAnalogOutInput, NTV2_WgtAnalogOut1, NTV2_CHANNEL1, kAnalogNickname, 0},
|
||||
{ NTV2_Xpt3DLUT1Input, NTV2_Wgt3DLUT1, NTV2_CHANNEL1, kLUT3DNickname, 0},
|
||||
{ NTV2_XptAnalogOutCompositeOut, NTV2_WgtAnalogCompositeOut1, NTV2_CHANNEL1, kCompositeNickname, 0},
|
||||
{ NTV2_XptStereoLeftInput, NTV2_WgtStereoCompressor, NTV2_CHANNEL1, kStereoCompNickname, 0},
|
||||
{ NTV2_XptStereoRightInput, NTV2_WgtStereoCompressor, NTV2_CHANNEL1, kStereoCompNickname, 0},
|
||||
{ NTV2_XptWaterMarker1Input, NTV2_WgtWaterMarker1, NTV2_CHANNEL1, kWatermarkNickname, 0},
|
||||
{ NTV2_XptWaterMarker2Input, NTV2_WgtWaterMarker2, NTV2_CHANNEL2, kWatermarkNickname, 0},
|
||||
{ NTV2_XptConversionMod2Input, NTV2_WgtUpDownConverter2, NTV2_CHANNEL2, kUpDownConvertNickname, 0},
|
||||
{ NTV2_XptCompressionModInput, NTV2_WgtCompression1, NTV2_CHANNEL1, kCompressionNickname, 0},
|
||||
{ NTV2_XptConversionModInput, NTV2_WgtUpDownConverter1, NTV2_CHANNEL1, kUpDownConvertNickname, 0},
|
||||
{ NTV2_XptFrameSync2Input, NTV2_WgtFrameSync2, NTV2_CHANNEL2, kFrameSyncNickname, 0},
|
||||
};
|
||||
|
||||
// Table of firmware widget's output crosspoint/id/channel/name/datastream index
|
||||
static WidgetOutputSocket kWidgetOutputSockets[] = {
|
||||
{ NTV2_OUTPUT_CROSSPOINT_INVALID, NTV2_WIDGET_INVALID, NTV2_CHANNEL_INVALID, "", -1},
|
||||
{ NTV2_XptBlack, NTV2_WgtUndefined, NTV2_CHANNEL1, kBlackNickname, 0},
|
||||
{ NTV2_XptSDIIn1, NTV2_WgtSDIIn1, NTV2_CHANNEL1, kSDINickname, 0},
|
||||
{ NTV2_XptSDIIn2, NTV2_WgtSDIIn2, NTV2_CHANNEL2, kSDINickname, 0},
|
||||
{ NTV2_XptLUT1YUV, NTV2_WgtLUT1, NTV2_CHANNEL1, kSDINickname, 0},
|
||||
{ NTV2_XptCSC1VidYUV, NTV2_WgtCSC1, NTV2_CHANNEL1, kCSCNickname, 0},
|
||||
{ NTV2_XptConversionModule, NTV2_WgtUpDownConverter1, NTV2_CHANNEL1, kUpDownConvertNickname, 0},
|
||||
{ NTV2_XptCompressionModule, NTV2_WgtCompression1, NTV2_CHANNEL1, kCompressionNickname, 0},
|
||||
{ NTV2_XptFrameBuffer1YUV, NTV2_WgtFrameBuffer1, NTV2_CHANNEL1, kFramebufferNickname, 0},
|
||||
{ NTV2_XptFrameSync1YUV, NTV2_WgtFrameSync1, NTV2_CHANNEL1, kFrameSyncNickname, 0},
|
||||
{ NTV2_XptFrameSync2YUV, NTV2_WgtFrameSync2, NTV2_CHANNEL2, kFrameSyncNickname, 0},
|
||||
{ NTV2_XptDuallinkOut1, NTV2_WgtDualLinkV2Out1, NTV2_CHANNEL1, kDualLinkOutNickname, 0},
|
||||
{ NTV2_XptCSC1KeyYUV, NTV2_WgtCSC1, NTV2_CHANNEL1, kCSCNickname, 2},
|
||||
{ NTV2_XptFrameBuffer2YUV, NTV2_WgtFrameBuffer2, NTV2_CHANNEL2, kFramebufferNickname, 0},
|
||||
{ NTV2_XptCSC2VidYUV, NTV2_WgtCSC2, NTV2_CHANNEL2, kCSCNickname, 0},
|
||||
{ NTV2_XptCSC2KeyYUV, NTV2_WgtCSC2, NTV2_CHANNEL2, kCSCNickname, 2},
|
||||
{ NTV2_XptMixer1VidYUV, NTV2_WgtMixer1, NTV2_CHANNEL1, kMixerNickname, 0},
|
||||
{ NTV2_XptMixer1KeyYUV, NTV2_WgtMixer1, NTV2_CHANNEL1, kMixerNickname, 1},
|
||||
{ NTV2_XptMultiLinkOut1DS1, NTV2_WgtMultiLinkOut1, NTV2_CHANNEL1, kMultiLinkNickname, 0},
|
||||
{ NTV2_XptMultiLinkOut1DS2, NTV2_WgtMultiLinkOut1, NTV2_CHANNEL1, kMultiLinkNickname, 1},
|
||||
{ NTV2_XptAnalogIn, NTV2_WgtAnalogIn1, NTV2_CHANNEL1, kAnalogNickname, 0},
|
||||
{ NTV2_XptHDMIIn1, NTV2_WgtHDMIIn1, NTV2_CHANNEL1, kHDMINickname, 0},
|
||||
{ NTV2_XptMultiLinkOut1DS3, NTV2_WgtMultiLinkOut1, NTV2_CHANNEL1, kMultiLinkNickname, 2},
|
||||
{ NTV2_XptMultiLinkOut1DS4, NTV2_WgtMultiLinkOut1, NTV2_CHANNEL1, kMultiLinkNickname, 3},
|
||||
{ NTV2_XptMultiLinkOut2DS1, NTV2_WgtMultiLinkOut2, NTV2_CHANNEL2, kMultiLinkNickname, 0},
|
||||
{ NTV2_XptMultiLinkOut2DS2, NTV2_WgtMultiLinkOut2, NTV2_CHANNEL2, kMultiLinkNickname, 1},
|
||||
{ NTV2_XptDuallinkOut2, NTV2_WgtDualLinkV2Out2, NTV2_CHANNEL2, kDualLinkOutNickname, 0},
|
||||
{ NTV2_XptTestPatternYUV, NTV2_WgtTestPattern1, NTV2_CHANNEL1, kTestPatternNickname, 0},
|
||||
{ NTV2_XptSDIIn1DS2, NTV2_Wgt3GSDIIn1, NTV2_CHANNEL1, kSDINickname, 1},
|
||||
{ NTV2_XptSDIIn2DS2, NTV2_Wgt3GSDIIn2, NTV2_CHANNEL2, kSDINickname, 1},
|
||||
{ NTV2_XptMixer2VidYUV, NTV2_WgtMixer2, NTV2_CHANNEL2, kMixerNickname, 0},
|
||||
{ NTV2_XptMixer2KeyYUV, NTV2_WgtMixer2, NTV2_CHANNEL2, kMixerNickname, 1},
|
||||
{ NTV2_XptStereoCompressorOut, NTV2_WgtStereoCompressor, NTV2_CHANNEL1, kStereoCompNickname, 0},
|
||||
{ NTV2_XptFrameBuffer3YUV, NTV2_WgtFrameBuffer3, NTV2_CHANNEL3, kFramebufferNickname, 0},
|
||||
{ NTV2_XptFrameBuffer4YUV, NTV2_WgtFrameBuffer4, NTV2_CHANNEL4, kFramebufferNickname, 0},
|
||||
{ NTV2_XptDuallinkOut1DS2, NTV2_WgtDualLinkV2Out1, NTV2_CHANNEL1, kDualLinkOutNickname, 1},
|
||||
{ NTV2_XptDuallinkOut2DS2, NTV2_WgtDualLinkV2Out2, NTV2_CHANNEL2, kDualLinkOutNickname, 1},
|
||||
{ NTV2_XptCSC5VidYUV, NTV2_WgtCSC5, NTV2_CHANNEL5, kCSCNickname, 0},
|
||||
{ NTV2_XptCSC5KeyYUV, NTV2_WgtCSC5, NTV2_CHANNEL5, kCSCNickname, 1},
|
||||
{ NTV2_XptMultiLinkOut2DS3, NTV2_WgtMultiLinkOut2, NTV2_CHANNEL2, kMultiLinkNickname, 2},
|
||||
{ NTV2_XptMultiLinkOut2DS4, NTV2_WgtMultiLinkOut2, NTV2_CHANNEL2, kMultiLinkNickname, 3},
|
||||
{ NTV2_XptSDIIn3, NTV2_Wgt3GSDIIn3, NTV2_CHANNEL3, kSDINickname, 0},
|
||||
{ NTV2_XptSDIIn4, NTV2_Wgt3GSDIIn4, NTV2_CHANNEL4, kSDINickname, 0},
|
||||
{ NTV2_XptSDIIn3DS2, NTV2_Wgt3GSDIIn3, NTV2_CHANNEL3, kSDINickname, 1},
|
||||
{ NTV2_XptSDIIn4DS2, NTV2_Wgt3GSDIIn4, NTV2_CHANNEL4, kSDINickname, 1},
|
||||
{ NTV2_XptDuallinkOut3, NTV2_WgtDualLinkV2Out3, NTV2_CHANNEL3, kDualLinkOutNickname, 0},
|
||||
{ NTV2_XptDuallinkOut3DS2, NTV2_WgtDualLinkV2Out3, NTV2_CHANNEL3, kDualLinkOutNickname, 1},
|
||||
{ NTV2_XptDuallinkOut4, NTV2_WgtDualLinkV2Out4, NTV2_CHANNEL4, kDualLinkOutNickname, 0},
|
||||
{ NTV2_XptDuallinkOut4DS2, NTV2_WgtDualLinkV2Out4, NTV2_CHANNEL4, kDualLinkOutNickname, 1},
|
||||
{ NTV2_XptCSC3VidYUV, NTV2_WgtCSC3, NTV2_CHANNEL3, kCSCNickname, 0},
|
||||
{ NTV2_XptCSC3KeyYUV, NTV2_WgtCSC3, NTV2_CHANNEL3, kCSCNickname, 2},
|
||||
{ NTV2_XptCSC4VidYUV, NTV2_WgtCSC4, NTV2_CHANNEL4, kCSCNickname, 0},
|
||||
{ NTV2_XptCSC4KeyYUV, NTV2_WgtCSC4, NTV2_CHANNEL4, kCSCNickname, 2},
|
||||
{ NTV2_XptDuallinkOut5, NTV2_WgtDualLinkV2Out5, NTV2_CHANNEL5, kDualLinkOutNickname, 0},
|
||||
{ NTV2_XptDuallinkOut5DS2, NTV2_WgtDualLinkV2Out5, NTV2_CHANNEL5, kDualLinkOutNickname, 1},
|
||||
{ NTV2_Xpt3DLUT1YUV, NTV2_Wgt3DLUT1, NTV2_CHANNEL1, kLUT3DNickname, 0},
|
||||
{ NTV2_XptHDMIIn1Q2, NTV2_WgtHDMIIn1v3, NTV2_CHANNEL1, kHDMINickname, 1},
|
||||
{ NTV2_XptHDMIIn1Q3, NTV2_WgtHDMIIn1v3, NTV2_CHANNEL1, kHDMINickname, 2},
|
||||
{ NTV2_XptHDMIIn1Q4, NTV2_WgtHDMIIn1v3, NTV2_CHANNEL1, kHDMINickname, 3},
|
||||
{ NTV2_Xpt4KDownConverterOut, NTV2_Wgt4KDownConverter, NTV2_CHANNEL1, k4KDownConvertNickname, 0},
|
||||
{ NTV2_XptSDIIn5, NTV2_Wgt3GSDIIn5, NTV2_CHANNEL5, kSDINickname, 0},
|
||||
{ NTV2_XptSDIIn6, NTV2_Wgt3GSDIIn6, NTV2_CHANNEL6, kSDINickname, 0},
|
||||
{ NTV2_XptSDIIn5DS2, NTV2_Wgt3GSDIIn5, NTV2_CHANNEL5, kSDINickname, 1},
|
||||
{ NTV2_XptSDIIn6DS2, NTV2_Wgt3GSDIIn6, NTV2_CHANNEL6, kSDINickname, 1},
|
||||
{ NTV2_XptSDIIn7, NTV2_Wgt3GSDIIn7, NTV2_CHANNEL7, kSDINickname, 0},
|
||||
{ NTV2_XptSDIIn8, NTV2_Wgt3GSDIIn8, NTV2_CHANNEL8, kSDINickname, 0},
|
||||
{ NTV2_XptSDIIn7DS2, NTV2_Wgt3GSDIIn7, NTV2_CHANNEL7, kSDINickname, 1},
|
||||
{ NTV2_XptSDIIn8DS2, NTV2_Wgt3GSDIIn8, NTV2_CHANNEL8, kSDINickname, 1},
|
||||
{ NTV2_XptFrameBuffer5YUV, NTV2_WgtFrameBuffer5, NTV2_CHANNEL5, kFramebufferNickname, 0},
|
||||
{ NTV2_XptFrameBuffer6YUV, NTV2_WgtFrameBuffer6, NTV2_CHANNEL6, kFramebufferNickname, 0},
|
||||
{ NTV2_XptFrameBuffer7YUV, NTV2_WgtFrameBuffer7, NTV2_CHANNEL7, kFramebufferNickname, 0},
|
||||
{ NTV2_XptFrameBuffer8YUV, NTV2_WgtFrameBuffer8, NTV2_CHANNEL8, kFramebufferNickname, 0},
|
||||
{ NTV2_XptMixer3VidYUV, NTV2_WgtMixer3, NTV2_CHANNEL3, kMixerNickname, 0},
|
||||
{ NTV2_XptMixer3KeyYUV, NTV2_WgtMixer3, NTV2_CHANNEL3, kMixerNickname, 1},
|
||||
{ NTV2_XptMixer4VidYUV, NTV2_WgtMixer4, NTV2_CHANNEL4, kMixerNickname, 0},
|
||||
{ NTV2_XptMixer4KeyYUV, NTV2_WgtMixer4, NTV2_CHANNEL4, kMixerNickname, 1},
|
||||
{ NTV2_XptCSC6VidYUV, NTV2_WgtCSC6, NTV2_CHANNEL6, kCSCNickname, 0},
|
||||
{ NTV2_XptCSC6KeyYUV, NTV2_WgtCSC6, NTV2_CHANNEL6, kCSCNickname, 1},
|
||||
{ NTV2_XptCSC7VidYUV, NTV2_WgtCSC7, NTV2_CHANNEL7, kCSCNickname, 0},
|
||||
{ NTV2_XptCSC7KeyYUV, NTV2_WgtCSC7, NTV2_CHANNEL7, kCSCNickname, 1},
|
||||
{ NTV2_XptCSC8VidYUV, NTV2_WgtCSC8, NTV2_CHANNEL8, kCSCNickname, 0},
|
||||
{ NTV2_XptCSC8KeyYUV, NTV2_WgtCSC8, NTV2_CHANNEL8, kCSCNickname, 1},
|
||||
{ NTV2_XptDuallinkOut6, NTV2_WgtDualLinkV2Out6, NTV2_CHANNEL6, kDualLinkOutNickname, 0},
|
||||
{ NTV2_XptDuallinkOut6DS2, NTV2_WgtDualLinkV2Out6, NTV2_CHANNEL6, kDualLinkOutNickname, 1},
|
||||
{ NTV2_XptDuallinkOut7, NTV2_WgtDualLinkV2Out7, NTV2_CHANNEL7, kDualLinkOutNickname, 0},
|
||||
{ NTV2_XptDuallinkOut7DS2, NTV2_WgtDualLinkV2Out7, NTV2_CHANNEL7, kDualLinkOutNickname, 1},
|
||||
{ NTV2_XptDuallinkOut8, NTV2_WgtDualLinkV2Out8, NTV2_CHANNEL8, kDualLinkOutNickname, 0},
|
||||
{ NTV2_XptDuallinkOut8DS2, NTV2_WgtDualLinkV2Out8, NTV2_CHANNEL8, kDualLinkOutNickname, 1},
|
||||
{ NTV2_Xpt425Mux1AYUV, NTV2_Wgt425Mux1, NTV2_CHANNEL1, kTSIMuxNickname, 0},
|
||||
{ NTV2_Xpt425Mux1BYUV, NTV2_Wgt425Mux1, NTV2_CHANNEL1, kTSIMuxNickname, 1},
|
||||
{ NTV2_Xpt425Mux2AYUV, NTV2_Wgt425Mux2, NTV2_CHANNEL2, kTSIMuxNickname, 0},
|
||||
{ NTV2_Xpt425Mux2BYUV, NTV2_Wgt425Mux2, NTV2_CHANNEL2, kTSIMuxNickname, 1},
|
||||
{ NTV2_Xpt425Mux3AYUV, NTV2_Wgt425Mux3, NTV2_CHANNEL3, kTSIMuxNickname, 0},
|
||||
{ NTV2_Xpt425Mux3BYUV, NTV2_Wgt425Mux3, NTV2_CHANNEL3, kTSIMuxNickname, 1},
|
||||
{ NTV2_Xpt425Mux4AYUV, NTV2_Wgt425Mux4, NTV2_CHANNEL4, kTSIMuxNickname, 0},
|
||||
{ NTV2_Xpt425Mux4BYUV, NTV2_Wgt425Mux4, NTV2_CHANNEL4, kTSIMuxNickname, 1},
|
||||
{ NTV2_XptFrameBuffer1_DS2YUV, NTV2_WgtFrameBuffer1, NTV2_CHANNEL1, kFramebufferNickname, 1},
|
||||
{ NTV2_XptFrameBuffer2_DS2YUV, NTV2_WgtFrameBuffer2, NTV2_CHANNEL2, kFramebufferNickname, 1},
|
||||
{ NTV2_XptFrameBuffer3_DS2YUV, NTV2_WgtFrameBuffer3, NTV2_CHANNEL3, kFramebufferNickname, 1},
|
||||
{ NTV2_XptFrameBuffer4_DS2YUV, NTV2_WgtFrameBuffer4, NTV2_CHANNEL4, kFramebufferNickname, 1},
|
||||
{ NTV2_XptFrameBuffer5_DS2YUV, NTV2_WgtFrameBuffer5, NTV2_CHANNEL5, kFramebufferNickname, 1},
|
||||
{ NTV2_XptFrameBuffer6_DS2YUV, NTV2_WgtFrameBuffer6, NTV2_CHANNEL6, kFramebufferNickname, 1},
|
||||
{ NTV2_XptFrameBuffer7_DS2YUV, NTV2_WgtFrameBuffer7, NTV2_CHANNEL7, kFramebufferNickname, 1},
|
||||
{ NTV2_XptFrameBuffer8_DS2YUV, NTV2_WgtFrameBuffer8, NTV2_CHANNEL8, kFramebufferNickname, 1},
|
||||
{ NTV2_XptHDMIIn2, NTV2_WgtHDMIIn2v4, NTV2_CHANNEL2, kHDMINickname, 0},
|
||||
{ NTV2_XptHDMIIn2Q2, NTV2_WgtHDMIIn2v4, NTV2_CHANNEL2, kHDMINickname, 1},
|
||||
{ NTV2_XptHDMIIn2Q3, NTV2_WgtHDMIIn2v4, NTV2_CHANNEL2, kHDMINickname, 2},
|
||||
{ NTV2_XptHDMIIn2Q4, NTV2_WgtHDMIIn2v4, NTV2_CHANNEL2, kHDMINickname, 3},
|
||||
{ NTV2_XptHDMIIn3, NTV2_WgtHDMIIn3v4, NTV2_CHANNEL3, kHDMINickname, 0},
|
||||
{ NTV2_XptHDMIIn4, NTV2_WgtHDMIIn4v4, NTV2_CHANNEL4, kHDMINickname, 0},
|
||||
{ NTV2_XptDuallinkIn1, NTV2_WgtDualLinkV2In1, NTV2_CHANNEL1, kDualLinkInNickname, 0},
|
||||
{ NTV2_XptLUT1Out, NTV2_WgtLUT1, NTV2_CHANNEL1, kLUTNickname, 0},
|
||||
{ NTV2_XptCSC1VidRGB, NTV2_WgtCSC1, NTV2_CHANNEL1, kCSCNickname, 1},
|
||||
{ NTV2_XptFrameBuffer1RGB, NTV2_WgtFrameBuffer1, NTV2_CHANNEL1, kFramebufferNickname, 2},
|
||||
{ NTV2_XptFrameSync1RGB, NTV2_WgtFrameSync1, NTV2_CHANNEL1, kFrameSyncNickname, 1},
|
||||
{ NTV2_XptFrameSync2RGB, NTV2_WgtFrameSync2, NTV2_CHANNEL2, kFrameSyncNickname, 1},
|
||||
{ NTV2_XptLUT2Out, NTV2_WgtLUT2, NTV2_CHANNEL2, kLUTNickname, 0},
|
||||
{ NTV2_XptFrameBuffer2RGB, NTV2_WgtFrameBuffer2, NTV2_CHANNEL2, kFramebufferNickname, 2},
|
||||
{ NTV2_XptCSC2VidRGB, NTV2_WgtCSC2, NTV2_CHANNEL2, kCSCNickname, 1},
|
||||
{ NTV2_XptMixer1VidRGB, NTV2_WgtMixer1, NTV2_CHANNEL1, kMixerNickname, 1},
|
||||
{ NTV2_XptHDMIIn1RGB, NTV2_WgtHDMIIn1v3, NTV2_CHANNEL1, kHDMINickname, 2},
|
||||
{ NTV2_XptFrameBuffer3RGB, NTV2_WgtFrameBuffer3, NTV2_CHANNEL3, kFramebufferNickname, 2},
|
||||
{ NTV2_XptFrameBuffer4RGB, NTV2_WgtFrameBuffer4, NTV2_CHANNEL4, kFramebufferNickname, 2},
|
||||
{ NTV2_XptDuallinkIn2, NTV2_WgtDualLinkV2In2, NTV2_CHANNEL2, kDualLinkInNickname, 0},
|
||||
{ NTV2_XptLUT3Out, NTV2_WgtLUT3, NTV2_CHANNEL3, kLUTNickname, 0},
|
||||
{ NTV2_XptLUT4Out, NTV2_WgtLUT4, NTV2_CHANNEL4, kLUTNickname, 0},
|
||||
{ NTV2_XptLUT5Out, NTV2_WgtLUT5, NTV2_CHANNEL5, kLUTNickname, 0},
|
||||
{ NTV2_XptCSC5VidRGB, NTV2_WgtCSC5, NTV2_CHANNEL5, kCSCNickname, 2},
|
||||
{ NTV2_XptDuallinkIn3, NTV2_WgtDualLinkV2In3, NTV2_CHANNEL3, kDualLinkInNickname, 0},
|
||||
{ NTV2_XptDuallinkIn4, NTV2_WgtDualLinkV2In4, NTV2_CHANNEL4, kDualLinkInNickname, 0},
|
||||
{ NTV2_XptCSC3VidRGB, NTV2_WgtCSC3, NTV2_CHANNEL3, kCSCNickname, 2},
|
||||
{ NTV2_XptCSC4VidRGB, NTV2_WgtCSC4, NTV2_CHANNEL4, kCSCNickname, 2},
|
||||
{ NTV2_Xpt3DLUT1RGB, NTV2_Wgt3DLUT1, NTV2_CHANNEL1, kLUT3DNickname, 1},
|
||||
{ NTV2_XptHDMIIn1Q2RGB, NTV2_WgtHDMIIn1v3, NTV2_CHANNEL1, kHDMINickname, 1},
|
||||
{ NTV2_XptHDMIIn1Q3RGB, NTV2_WgtHDMIIn1v3, NTV2_CHANNEL1, kHDMINickname, 2},
|
||||
{ NTV2_XptHDMIIn1Q4RGB, NTV2_WgtHDMIIn1v3, NTV2_CHANNEL1, kHDMINickname, 3},
|
||||
{ NTV2_Xpt4KDownConverterOutRGB, NTV2_Wgt4KDownConverter, NTV2_CHANNEL1, k4KDownConvertNickname, 1},
|
||||
{ NTV2_XptDuallinkIn5, NTV2_WgtDualLinkV2In5, NTV2_CHANNEL5, kDualLinkInNickname, 0},
|
||||
{ NTV2_XptDuallinkIn6, NTV2_WgtDualLinkV2In6, NTV2_CHANNEL6, kDualLinkInNickname, 0},
|
||||
{ NTV2_XptDuallinkIn7, NTV2_WgtDualLinkV2In7, NTV2_CHANNEL7, kDualLinkInNickname, 0},
|
||||
{ NTV2_XptDuallinkIn8, NTV2_WgtDualLinkV2In8, NTV2_CHANNEL8, kDualLinkInNickname, 0},
|
||||
{ NTV2_XptFrameBuffer5RGB, NTV2_WgtFrameBuffer5, NTV2_CHANNEL5, kFramebufferNickname, 2},
|
||||
{ NTV2_XptFrameBuffer6RGB, NTV2_WgtFrameBuffer6, NTV2_CHANNEL6, kFramebufferNickname, 2},
|
||||
{ NTV2_XptFrameBuffer7RGB, NTV2_WgtFrameBuffer7, NTV2_CHANNEL7, kFramebufferNickname, 2},
|
||||
{ NTV2_XptFrameBuffer8RGB, NTV2_WgtFrameBuffer8, NTV2_CHANNEL8, kFramebufferNickname, 2},
|
||||
{ NTV2_XptCSC6VidRGB, NTV2_WgtCSC6, NTV2_CHANNEL6, kCSCNickname, 1},
|
||||
{ NTV2_XptCSC7VidRGB, NTV2_WgtCSC7, NTV2_CHANNEL7, kCSCNickname, 1},
|
||||
{ NTV2_XptCSC8VidRGB, NTV2_WgtCSC8, NTV2_CHANNEL8, kCSCNickname, 1},
|
||||
{ NTV2_XptLUT6Out, NTV2_WgtLUT6, NTV2_CHANNEL6, kLUTNickname, 0},
|
||||
{ NTV2_XptLUT7Out, NTV2_WgtLUT7, NTV2_CHANNEL7, kLUTNickname, 0},
|
||||
{ NTV2_XptLUT8Out, NTV2_WgtLUT8, NTV2_CHANNEL8, kLUTNickname, 0},
|
||||
{ NTV2_Xpt425Mux1ARGB, NTV2_Wgt425Mux1, NTV2_CHANNEL1, kTSIMuxNickname, 2},
|
||||
{ NTV2_Xpt425Mux1BRGB, NTV2_Wgt425Mux1, NTV2_CHANNEL1, kTSIMuxNickname, 3},
|
||||
{ NTV2_Xpt425Mux2ARGB, NTV2_Wgt425Mux2, NTV2_CHANNEL2, kTSIMuxNickname, 2},
|
||||
{ NTV2_Xpt425Mux2BRGB, NTV2_Wgt425Mux2, NTV2_CHANNEL2, kTSIMuxNickname, 3},
|
||||
{ NTV2_Xpt425Mux3ARGB, NTV2_Wgt425Mux3, NTV2_CHANNEL3, kTSIMuxNickname, 2},
|
||||
{ NTV2_Xpt425Mux3BRGB, NTV2_Wgt425Mux3, NTV2_CHANNEL3, kTSIMuxNickname, 3},
|
||||
{ NTV2_Xpt425Mux4ARGB, NTV2_Wgt425Mux4, NTV2_CHANNEL4, kTSIMuxNickname, 2},
|
||||
{ NTV2_Xpt425Mux4BRGB, NTV2_Wgt425Mux4, NTV2_CHANNEL4, kTSIMuxNickname, 3},
|
||||
{ NTV2_XptFrameBuffer1_DS2RGB, NTV2_WgtFrameBuffer1, NTV2_CHANNEL1, kFramebufferNickname, 3},
|
||||
{ NTV2_XptFrameBuffer2_DS2RGB, NTV2_WgtFrameBuffer2, NTV2_CHANNEL2, kFramebufferNickname, 3},
|
||||
{ NTV2_XptFrameBuffer3_DS2RGB, NTV2_WgtFrameBuffer3, NTV2_CHANNEL3, kFramebufferNickname, 3},
|
||||
{ NTV2_XptFrameBuffer4_DS2RGB, NTV2_WgtFrameBuffer4, NTV2_CHANNEL4, kFramebufferNickname, 3},
|
||||
{ NTV2_XptFrameBuffer5_DS2RGB, NTV2_WgtFrameBuffer5, NTV2_CHANNEL5, kFramebufferNickname, 3},
|
||||
{ NTV2_XptFrameBuffer6_DS2RGB, NTV2_WgtFrameBuffer6, NTV2_CHANNEL6, kFramebufferNickname, 3},
|
||||
{ NTV2_XptFrameBuffer7_DS2RGB, NTV2_WgtFrameBuffer7, NTV2_CHANNEL7, kFramebufferNickname, 3},
|
||||
{ NTV2_XptFrameBuffer8_DS2RGB, NTV2_WgtFrameBuffer8, NTV2_CHANNEL8, kFramebufferNickname, 3},
|
||||
{ NTV2_XptHDMIIn2RGB, NTV2_WgtHDMIIn2v4, NTV2_CHANNEL2, kHDMINickname, 0},
|
||||
{ NTV2_XptHDMIIn2Q2RGB, NTV2_WgtHDMIIn2v4, NTV2_CHANNEL2, kHDMINickname, 1},
|
||||
{ NTV2_XptHDMIIn2Q3RGB, NTV2_WgtHDMIIn2v4, NTV2_CHANNEL2, kHDMINickname, 2},
|
||||
{ NTV2_XptHDMIIn2Q4RGB, NTV2_WgtHDMIIn2v4, NTV2_CHANNEL2, kHDMINickname, 3},
|
||||
{ NTV2_XptHDMIIn3RGB, NTV2_WgtHDMIIn3v4, NTV2_CHANNEL3, kHDMINickname, 0},
|
||||
{ NTV2_XptHDMIIn4RGB, NTV2_WgtHDMIIn4v4, NTV2_CHANNEL4, kHDMINickname, 0},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
static const size_t kNumWidgetInputSockets =
|
||||
(sizeof(kWidgetInputSockets) / sizeof(WidgetInputSocket));
|
||||
static const size_t kNumWidgetOutputSockets =
|
||||
(sizeof(kWidgetOutputSockets) / sizeof(WidgetOutputSocket));
|
||||
|
||||
bool WidgetInputSocket::Find(const std::string &name, NTV2Channel channel,
|
||||
int32_t datastream, WidgetInputSocket &inp)
|
||||
{
|
||||
for (const auto &in : kWidgetInputSockets) {
|
||||
if (name == in.name && channel == in.channel &&
|
||||
datastream == in.datastream_index) {
|
||||
inp = in;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WidgetInputSocket::GetWidgetInputSocketByXpt(InputXpt id,
|
||||
WidgetInputSocket &inp)
|
||||
{
|
||||
for (const auto &in : kWidgetInputSockets) {
|
||||
if (in.id == id) {
|
||||
inp = in;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t WidgetInputSocket::InputXptDatastreamIndex(InputXpt xpt)
|
||||
{
|
||||
int32_t datastream = 0;
|
||||
for (auto &x : kWidgetInputSockets) {
|
||||
if (x.id == xpt) {
|
||||
datastream = x.datastream_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return datastream;
|
||||
}
|
||||
|
||||
NTV2Channel WidgetInputSocket::InputXptChannel(InputXpt xpt)
|
||||
{
|
||||
NTV2Channel channel = NTV2_CHANNEL_INVALID;
|
||||
for (auto &x : kWidgetInputSockets) {
|
||||
if (x.id == xpt) {
|
||||
channel = x.channel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
const char *WidgetInputSocket::InputXptName(InputXpt xpt)
|
||||
{
|
||||
const char *name = NULL;
|
||||
for (auto &x : kWidgetInputSockets) {
|
||||
if (x.id == xpt) {
|
||||
name = x.name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
bool WidgetOutputSocket::Find(const std::string &name, NTV2Channel channel,
|
||||
int32_t datastream, WidgetOutputSocket &out)
|
||||
{
|
||||
// std::cout << "DEBUG -- WidgetOutputSocket::Find: name = " << name
|
||||
// << ", chan = " << NTV2ChannelToString(channel)
|
||||
// << ", datastream = " << datastream << std::endl;
|
||||
for (const auto &wo : kWidgetOutputSockets) {
|
||||
if (name == wo.name && channel == wo.channel &&
|
||||
datastream == wo.datastream_index) {
|
||||
out = wo;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WidgetOutputSocket::GetWidgetOutputSocketByXpt(OutputXpt id,
|
||||
WidgetOutputSocket &out)
|
||||
{
|
||||
for (const auto &wo : kWidgetOutputSockets) {
|
||||
if (wo.id == id) {
|
||||
out = wo;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t WidgetOutputSocket::OutputXptDatastreamIndex(OutputXpt xpt)
|
||||
{
|
||||
int32_t datastream = 0;
|
||||
for (auto &x : kWidgetOutputSockets) {
|
||||
if (x.id == xpt) {
|
||||
datastream = x.datastream_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return datastream;
|
||||
}
|
||||
|
||||
NTV2Channel WidgetOutputSocket::OutputXptChannel(OutputXpt xpt)
|
||||
{
|
||||
NTV2Channel channel = NTV2_CHANNEL_INVALID;
|
||||
for (auto &x : kWidgetOutputSockets) {
|
||||
if (x.id == xpt) {
|
||||
channel = x.channel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
const char *WidgetOutputSocket::OutputXptName(OutputXpt xpt)
|
||||
{
|
||||
const char *name = NULL;
|
||||
for (auto &x : kWidgetOutputSockets) {
|
||||
if (x.id == xpt) {
|
||||
name = x.name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
42
plugins/aja/aja-widget-io.hpp
Normal file
42
plugins/aja/aja-widget-io.hpp
Normal file
@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <ajantv2/includes/ntv2enums.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
using OutputXpt = NTV2OutputCrosspointID; // src
|
||||
using InputXpt = NTV2InputCrosspointID; // dest
|
||||
|
||||
// Firmware widget input socket connector
|
||||
struct WidgetInputSocket {
|
||||
InputXpt id;
|
||||
NTV2WidgetID widget_id;
|
||||
NTV2Channel channel;
|
||||
const char *name;
|
||||
int32_t datastream_index;
|
||||
|
||||
static bool Find(const std::string &route, NTV2Channel channel,
|
||||
int32_t datastream, WidgetInputSocket &inp);
|
||||
static bool GetWidgetInputSocketByXpt(InputXpt id,
|
||||
WidgetInputSocket &inp);
|
||||
static int32_t InputXptDatastreamIndex(InputXpt xpt);
|
||||
static NTV2Channel InputXptChannel(InputXpt xpt);
|
||||
static const char *InputXptName(InputXpt xpt);
|
||||
};
|
||||
|
||||
// Firmware widget output socket connector
|
||||
struct WidgetOutputSocket {
|
||||
OutputXpt id;
|
||||
NTV2WidgetID widget_id;
|
||||
NTV2Channel channel;
|
||||
const char *name;
|
||||
int32_t datastream_index;
|
||||
|
||||
static bool Find(const std::string &route, NTV2Channel channel,
|
||||
int32_t datastream, WidgetOutputSocket &out);
|
||||
static bool GetWidgetOutputSocketByXpt(OutputXpt id,
|
||||
WidgetOutputSocket &out);
|
||||
static int32_t OutputXptDatastreamIndex(OutputXpt xpt);
|
||||
static NTV2Channel OutputXptChannel(OutputXpt xpt);
|
||||
static const char *OutputXptName(OutputXpt xpt);
|
||||
};
|
0
plugins/aja/data/.keepme
Normal file
0
plugins/aja/data/.keepme
Normal file
15
plugins/aja/data/locale/en-US.ini
Normal file
15
plugins/aja/data/locale/en-US.ini
Normal file
@ -0,0 +1,15 @@
|
||||
AJACapture.Device="AJA I/O Device Capture"
|
||||
AJAOutput.Device="AJA I/O Device Output"
|
||||
Device="Device"
|
||||
Output="Output"
|
||||
Input="Input"
|
||||
Mode="Mode"
|
||||
VideoFormat="Video Format"
|
||||
PixelFormat="Pixel Format"
|
||||
AutoDetect="Auto Detect"
|
||||
Interlaced="Interlaced"
|
||||
AutoStart="Auto start on launch"
|
||||
Buffering="Use Buffering"
|
||||
DeactivateWhenNotShowing="Deactivate when not showing"
|
||||
IOSelect="Select..."
|
||||
SDI4KTransport="SDI 4K Transport"
|
34
plugins/aja/main.cpp
Normal file
34
plugins/aja/main.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
#include "aja-card-manager.hpp"
|
||||
#include <obs-module.h>
|
||||
|
||||
OBS_DECLARE_MODULE()
|
||||
OBS_MODULE_USE_DEFAULT_LOCALE("aja", "en-US")
|
||||
|
||||
MODULE_EXPORT const char *obs_module_description(void)
|
||||
{
|
||||
return "aja";
|
||||
}
|
||||
|
||||
extern struct obs_source_info create_aja_source_info();
|
||||
struct obs_source_info aja_source_info;
|
||||
|
||||
extern struct obs_output_info create_aja_output_info();
|
||||
struct obs_output_info aja_output_info;
|
||||
|
||||
bool obs_module_load(void)
|
||||
{
|
||||
aja::CardManager::Instance().EnumerateCards();
|
||||
|
||||
aja_source_info = create_aja_source_info();
|
||||
obs_register_source(&aja_source_info);
|
||||
|
||||
aja_output_info = create_aja_output_info();
|
||||
obs_register_output(&aja_output_info);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void obs_module_unload(void)
|
||||
{
|
||||
aja::CardManager::Instance().ClearCardEntries();
|
||||
}
|
22
plugins/aja/routing/hdmi_rgb_capture.h
Normal file
22
plugins/aja/routing/hdmi_rgb_capture.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "../aja-routing.hpp"
|
||||
|
||||
static const std::map<HDMIWireFormat, RoutingConfig> kHDMIRGBCaptureConfigs = {
|
||||
{HDMIWireFormat::HD_RGB_LFR,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
1,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"hdmi[{ch1}][0]->fb[{ch1}][0];",
|
||||
}}};
|
28
plugins/aja/routing/hdmi_rgb_display.h
Normal file
28
plugins/aja/routing/hdmi_rgb_display.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "../aja-routing.hpp"
|
||||
|
||||
static const std::map<HDMIWireFormat, RoutingConfig> kHDMIRGBDisplayConfigs = {
|
||||
{HDMIWireFormat::HD_RGB_LFR,
|
||||
{NTV2_MODE_DISPLAY, 1, 1, true, false, false, false, false, false,
|
||||
false, false, false, false, "fb[{ch1}][0]->hdmi[0][0];"}},
|
||||
{HDMIWireFormat::TTAP_PRO,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1,
|
||||
1,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"fb[{ch1}][2]->dlo[{ch1}][0];"
|
||||
"dlo[{ch1}][0]->sdi[{ch1}][0];"
|
||||
"dlo[{ch1}][1]->sdi[{ch1}][1];"
|
||||
"fb[{ch1}][2]->hdmi[{ch1}][0];",
|
||||
}}};
|
47
plugins/aja/routing/hdmi_ycbcr_capture.h
Normal file
47
plugins/aja/routing/hdmi_ycbcr_capture.h
Normal file
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include "../aja-routing.hpp"
|
||||
|
||||
static const std::map<HDMIWireFormat, RoutingConfig> kHDMIYCbCrCaptureConfigs = {
|
||||
{HDMIWireFormat::HD_YCBCR_LFR,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
1,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"hdmi[{ch1}][0]->fb[{ch1}][0];",
|
||||
}},
|
||||
{HDMIWireFormat::UHD_4K_YCBCR_LFR,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
1,
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"hdmi[0][0]->tsi[{ch1}][0];"
|
||||
"hdmi[0][1]->tsi[{ch1}][1];"
|
||||
"hdmi[0][2]->tsi[{ch2}][0];"
|
||||
"hdmi[0][3]->tsi[{ch2}][1];"
|
||||
"tsi[{ch1}][0]->fb[{ch1}][0];"
|
||||
"tsi[{ch1}][1]->fb[{ch1}][1];"
|
||||
"tsi[{ch2}][0]->fb[{ch2}][0];"
|
||||
"tsi[{ch2}][1]->fb[{ch2}][1];",
|
||||
}},
|
||||
};
|
64
plugins/aja/routing/hdmi_ycbcr_display.h
Normal file
64
plugins/aja/routing/hdmi_ycbcr_display.h
Normal file
@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include "../aja-routing.hpp"
|
||||
|
||||
static const std::map<HDMIWireFormat, RoutingConfig> kHDMIYCbCrDisplayConfigs = {
|
||||
{HDMIWireFormat::HD_YCBCR_LFR,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"fb[{ch1}][0]->hdmi[0][0];",
|
||||
}},
|
||||
{HDMIWireFormat::UHD_4K_YCBCR_LFR,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1,
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"fb[{ch1}][0]->tsi[{ch1}][0];"
|
||||
"fb[{ch1}][1]->tsi[{ch1}][1];"
|
||||
"fb[{ch2}][0]->tsi[{ch2}][0];"
|
||||
"fb[{ch2}][1]->tsi[{ch2}][1];"
|
||||
"tsi[{ch1}][0]->hdmi[0][0];"
|
||||
"tsi[{ch1}][1]->hdmi[0][1];"
|
||||
"tsi[{ch2}][0]->hdmi[0][2];"
|
||||
"tsi[{ch2}][1]->hdmi[0][3];",
|
||||
}},
|
||||
{HDMIWireFormat::TTAP_PRO,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"fb[{ch1}][0]->sdi[{ch1}][0];"
|
||||
"fb[{ch1}][0]->hdmi[{ch1}][0];",
|
||||
}}};
|
466
plugins/aja/routing/sdi_rgb_capture.h
Normal file
466
plugins/aja/routing/sdi_rgb_capture.h
Normal file
@ -0,0 +1,466 @@
|
||||
#pragma once
|
||||
|
||||
#include "../aja-routing.hpp"
|
||||
|
||||
static const std::map<SDIWireFormat, RoutingConfig> kSDIRGBCaptureConfigs = {
|
||||
{
|
||||
SDIWireFormat::SD_ST352,
|
||||
{
|
||||
NTV2_MODE_CAPTURE, // i/o mode
|
||||
1, // num wires
|
||||
1, // num framestores
|
||||
false, // enable 3G output?
|
||||
false, // enable 6G output?
|
||||
false, // enable 12G output?
|
||||
false, // convert 3Gb -> 3Ga input?
|
||||
false, // convert 3Ga -> 3Gb output?
|
||||
false, // convert RGB 3Ga output?
|
||||
false, // enable 3Gb output?
|
||||
false, // enable 4K Square Division?
|
||||
false, // enable 8K Square Division?
|
||||
false, // enable two-sample-interleave?
|
||||
"",
|
||||
},
|
||||
},
|
||||
{SDIWireFormat::HD_720p_ST292,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
1,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"sdi[{ch1}][0]->dli[{ch1}][0];"
|
||||
"sdi[{ch1}][1]->dli[{ch1}][1];"
|
||||
"dli[{ch1}][0]->fb[{ch1}][0];",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080_ST292,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
1,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
// Capture
|
||||
"sdi[{ch1}][0]->dli[{ch1}][0];"
|
||||
"sdi[{ch1}][1]->dli[{ch1}][1];"
|
||||
"dli[{ch1}][0]->fb[{ch1}][0];",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080_ST372_Dual,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
2,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
// Capture
|
||||
"sdi[{ch1}][0]->dli[{ch1}][0];"
|
||||
"sdi[{ch2}][0]->dli[{ch1}][1];"
|
||||
"dli[{ch1}][0]->fb[{ch1}][0];",
|
||||
}},
|
||||
{SDIWireFormat::HD_720p_ST425_3Ga,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
1,
|
||||
1,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
// Capture
|
||||
"sdi[{ch1}][0]->dli[{ch1}][0];"
|
||||
"sdi[{ch1}][1]->dli[{ch1}][1];"
|
||||
"dli[{ch1}][0]->fb[{ch1}][0];",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080p_ST425_3Ga,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
1,
|
||||
1,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
// Capture
|
||||
"sdi[{ch1}][0]->dli[{ch1}][0];"
|
||||
"sdi[{ch1}][1]->dli[{ch1}][1];"
|
||||
"dli[{ch1}][0]->fb[{ch1}][0];",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080p_ST425_3Gb_DL,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
1,
|
||||
1,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
// Capture
|
||||
"sdi[{ch1}][0]->dli[{ch1}][0];"
|
||||
"sdi[{ch1}][1]->dli[{ch1}][1];"
|
||||
"dli[{ch1}][0]->fb[{ch1}][0];",
|
||||
}},
|
||||
{SDIWireFormat::HD_720p_ST425_3Gb,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
1,
|
||||
2,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
// Capture
|
||||
"sdi[{ch1}][0]->dli[{ch1}][0];"
|
||||
"sdi[{ch1}][1]->dli[{ch1}][1];"
|
||||
"dli[{ch1}][0]->fb[{ch1}][0];",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080p_ST425_3Gb,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
1,
|
||||
2,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
// Capture
|
||||
"sdi[{ch1}][0]->dli[{ch1}][0];"
|
||||
"sdi[{ch1}][1]->dli[{ch1}][1];"
|
||||
"dli[{ch1}][0]->fb[{ch1}][0];",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080p_ST425_Dual_3Ga,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
2,
|
||||
2,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080p_ST425_Dual_3Gb,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
2,
|
||||
2,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST292_Quad_1_5_Squares,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
4,
|
||||
4,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
// Capture
|
||||
"sdi[{ch1}][0]->fb[{ch1}][0];"
|
||||
"sdi[{ch2}][0]->fb[{ch2}][0];"
|
||||
"sdi[{ch3}][0]->fb[{ch3}][0];"
|
||||
"sdi[{ch4}][0]->fb[{ch4}][0];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST425_Quad_3Ga_Squares,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
4,
|
||||
4,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
// Capture
|
||||
"sdi[{ch1}][0]->fb[{ch1}][0];"
|
||||
"sdi[{ch2}][0]->fb[{ch2}][0];"
|
||||
"sdi[{ch3}][0]->fb[{ch3}][0];"
|
||||
"sdi[{ch4}][0]->fb[{ch4}][0];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST425_Quad_3Gb_Squares,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
4,
|
||||
4,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
"sdi[{ch1}][0]->dli[{ch1}][0];"
|
||||
"sdi[{ch1}][1]->dli[{ch1}][1];"
|
||||
|
||||
"sdi[{ch2}][0]->dli[{ch2}][0];"
|
||||
"sdi[{ch2}][1]->dli[{ch2}][1];"
|
||||
|
||||
"sdi[{ch3}][0]->dli[{ch3}][0];"
|
||||
"sdi[{ch3}][1]->dli[{ch3}][1];"
|
||||
|
||||
"sdi[{ch4}][0]->dli[{ch4}][0];"
|
||||
"sdi[{ch4}][1]->dli[{ch4}][1];"
|
||||
|
||||
"dli[{ch1}][0]->fb[{ch1}][2];"
|
||||
"dli[{ch2}][0]->fb[{ch2}][2];"
|
||||
"dli[{ch3}][0]->fb[{ch3}][2];"
|
||||
"dli[{ch4}][0]->fb[{ch4}][2];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST425_Dual_3Gb_2SI,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
2,
|
||||
2,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST425_Quad_3Ga_2SI,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
4,
|
||||
4,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
// SDI 1-4 -> Dual-Link 1-4
|
||||
// -> TSI Mux 1-2 -> Framestore 1-2
|
||||
"sdi[{ch1}][0]->dli[{ch1}][0];"
|
||||
"sdi[{ch1}][1]->dli[{ch1}][1];"
|
||||
"sdi[{ch2}][0]->dli[{ch2}][0];"
|
||||
"sdi[{ch2}][1]->dli[{ch2}][1];"
|
||||
"sdi[{ch3}][0]->dli[{ch3}][0];"
|
||||
"sdi[{ch3}][1]->dli[{ch3}][1];"
|
||||
"sdi[{ch4}][0]->dli[{ch4}][0];"
|
||||
"sdi[{ch4}][1]->dli[{ch4}][1];"
|
||||
|
||||
"dli[{ch1}][0]->tsi[{ch1}][0];"
|
||||
"dli[{ch2}][0]->tsi[{ch1}][1];"
|
||||
"dli[{ch3}][0]->tsi[{ch2}][0];"
|
||||
"dli[{ch4}][0]->tsi[{ch2}][1];"
|
||||
|
||||
"tsi[{ch1}][0]->fb[{ch1}][0];"
|
||||
"tsi[{ch1}][1]->fb[{ch1}][1];"
|
||||
"tsi[{ch2}][0]->fb[{ch2}][0];"
|
||||
"tsi[{ch2}][1]->fb[{ch2}][1];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST425_Quad_3Gb_2SI,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
4,
|
||||
4,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
// SDI 1-4 -> Dual-Link 1-4
|
||||
// -> TSI Mux 1-2 -> Framestore 1-2
|
||||
"sdi[{ch1}][0]->dli[{ch1}][0];"
|
||||
"sdi[{ch1}][1]->dli[{ch1}][1];"
|
||||
"sdi[{ch2}][0]->dli[{ch2}][0];"
|
||||
"sdi[{ch2}][1]->dli[{ch2}][1];"
|
||||
"sdi[{ch3}][0]->dli[{ch3}][0];"
|
||||
"sdi[{ch3}][1]->dli[{ch3}][1];"
|
||||
"sdi[{ch4}][0]->dli[{ch4}][0];"
|
||||
"sdi[{ch4}][1]->dli[{ch4}][1];"
|
||||
|
||||
"dli[{ch1}][0]->tsi[{ch1}][0];"
|
||||
"dli[{ch2}][0]->tsi[{ch1}][1];"
|
||||
"dli[{ch3}][0]->tsi[{ch2}][0];"
|
||||
"dli[{ch4}][0]->tsi[{ch2}][1];"
|
||||
|
||||
"tsi[{ch1}][0]->fb[{ch1}][0];"
|
||||
"tsi[{ch1}][1]->fb[{ch1}][1];"
|
||||
"tsi[{ch2}][0]->fb[{ch2}][0];"
|
||||
"tsi[{ch2}][1]->fb[{ch2}][1];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST2018_6G_Squares_2SI,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
2,
|
||||
2,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST2018_12G_Squares_2SI,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
1,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"",
|
||||
}},
|
||||
{SDIWireFormat::UHD28K_ST2082_Dual_12G,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
2,
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"",
|
||||
}},
|
||||
{SDIWireFormat::UHD28K_ST2082_RGB_Dual_12G,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
2,
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"",
|
||||
}},
|
||||
{SDIWireFormat::UHD28K_ST2082_Quad_12G,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
4,
|
||||
4,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"",
|
||||
}},
|
||||
};
|
468
plugins/aja/routing/sdi_rgb_display.h
Normal file
468
plugins/aja/routing/sdi_rgb_display.h
Normal file
@ -0,0 +1,468 @@
|
||||
#pragma once
|
||||
|
||||
#include "../aja-routing.hpp"
|
||||
|
||||
static const std::map<SDIWireFormat, RoutingConfig> kSDIRGBDisplayConfigs = {
|
||||
{
|
||||
SDIWireFormat::SD_ST352,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1, // num wires
|
||||
1, // num framestores
|
||||
false, // enable 3G output?
|
||||
false, // enable 6G output?
|
||||
false, // enable 12G output?
|
||||
false, // convert 3Gb -> 3Ga input?
|
||||
false, // convert 3Ga -> 3Gb output?
|
||||
false, // convert RGB 3Ga output?
|
||||
false, // enable 3Gb output?
|
||||
false, // enable 4K Square Division?
|
||||
false, // enable 8K Square Division?
|
||||
false, // enable two-sample-interleave?
|
||||
"", // RGB Output Route
|
||||
},
|
||||
},
|
||||
{SDIWireFormat::HD_720p_ST292,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"fb[{ch1}][2]->dlo[{ch1}][0];"
|
||||
"dlo[{ch1}][0]->sdi[{ch1}[0];"
|
||||
"dlo[{ch1}][1]->sdi[{ch1}][1];",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080_ST292,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"fb[{ch1}][2]->dlo[{ch1}][0];"
|
||||
"dlo[{ch1}][0]->sdi[{ch1}[0];"
|
||||
"dlo[{ch1}][1]->sdi[{ch1}][1];",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080_ST372_Dual,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
2,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
// Playout
|
||||
"fb[{ch1}][2]->dlo[{ch1}][0];"
|
||||
"dlo[{ch1}][0]->sdi[{ch1}[0];"
|
||||
"dlo[{ch1}][1]->sdi[{ch2}][0];",
|
||||
}},
|
||||
{SDIWireFormat::HD_720p_ST425_3Ga,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1,
|
||||
1,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
// Output
|
||||
"fb[{ch1}][2]->dlo[{ch1}][0];"
|
||||
"dlo[{ch1}][0]->sdi[{ch1}][0];"
|
||||
"dlo[{ch1}][1]->sdi[{ch1}][1];",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080p_ST425_3Ga,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1,
|
||||
1,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
// Output
|
||||
"fb[{ch1}][2]->dlo[{ch1}][0];"
|
||||
"dlo[{ch1}][0]->sdi[{ch1}][0];"
|
||||
"dlo[{ch1}][1]->sdi[{ch1}][1];",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080p_ST425_3Gb_DL,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1,
|
||||
1,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
// Output
|
||||
"fb[{ch1}][2]->dlo[{ch1}][0];"
|
||||
"dlo[{ch1}][0]->sdi[{ch1}][0];"
|
||||
"dlo[{ch1}][1]->sdi[{ch1}][1];",
|
||||
}},
|
||||
{SDIWireFormat::HD_720p_ST425_3Gb,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1,
|
||||
2,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
// Output
|
||||
"fb[{ch1}][2]->dlo[{ch1}][0];"
|
||||
"dlo[{ch1}][0]->sdi[{ch1}][0];"
|
||||
"dlo[{ch1}][1]->sdi[{ch1}][1];",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080p_ST425_3Gb,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1,
|
||||
2,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
// Output
|
||||
"fb[{ch1}][2]->dlo[{ch1}][0];"
|
||||
"dlo[{ch1}][0]->sdi[{ch1}][0];"
|
||||
"dlo[{ch1}][1]->sdi[{ch1}][1];",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080p_ST425_Dual_3Ga,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
2,
|
||||
2,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080p_ST425_Dual_3Gb,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
2,
|
||||
2,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST292_Quad_1_5_Squares,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
4,
|
||||
4,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
// Playout
|
||||
"fb[{ch1}][0]->sdi[{ch1}][0];"
|
||||
"fb[{ch2}][0]->sdi[{ch2}][0];"
|
||||
"fb[{ch3}][0]->sdi[{ch3}][0];"
|
||||
"fb[{ch4}][0]->sdi[{ch4}][0];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST425_Quad_3Ga_Squares,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
4,
|
||||
4,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
// Playout
|
||||
"fb[{ch1}][0]->sdi[{ch1}][0];"
|
||||
"fb[{ch2}][0]->sdi[{ch2}][0];"
|
||||
"fb[{ch3}][0]->sdi[{ch3}][0];"
|
||||
"fb[{ch4}][0]->sdi[{ch4}][0];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST425_Quad_3Gb_Squares,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
4,
|
||||
4,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
// Framestores 1-4 -> Dual-Link 1-4 -> SDI 1-4
|
||||
"fb[{ch1}][2]->dlo[{ch1}][0];"
|
||||
"fb[{ch2}][2]->dlo[{ch2}][0];"
|
||||
"fb[{ch3}][2]->dlo[{ch3}][0];"
|
||||
"fb[{ch4}][2]->dlo[{ch4}][0];"
|
||||
|
||||
"dlo[{ch1}][0]->sdi[{ch1}][0];"
|
||||
"dlo[{ch1}][1]->sdi[{ch1}][1];"
|
||||
|
||||
"dlo[{ch2}][0]->sdi[{ch2}][0];"
|
||||
"dlo[{ch2}][1]->sdi[{ch2}][1];"
|
||||
|
||||
"dlo[{ch3}][0]->sdi[{ch3}][0];"
|
||||
"dlo[{ch3}][1]->sdi[{ch3}][1];"
|
||||
|
||||
"dlo[{ch4}][0]->sdi[{ch4}][0];"
|
||||
"dlo[{ch4}][1]->sdi[{ch4}][1];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST425_Dual_3Gb_2SI,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
2,
|
||||
2,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST425_Quad_3Ga_2SI,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
4,
|
||||
4,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
// Framestores 1-2 -> TSI Mux 1-2
|
||||
// -> Dual-Link 1-4 -> SDI 1-4
|
||||
"fb[{ch1}][2]->tsi[{ch1}][0];"
|
||||
"fb[{ch1}][3]->tsi[{ch1}][1];"
|
||||
|
||||
"fb[{ch2}][2]->tsi[{ch2}][0];"
|
||||
"fb[{ch2}][3]->tsi[{ch2}][1];"
|
||||
|
||||
"tsi[{ch1}][2]->dlo[{ch1}][0];"
|
||||
"tsi[{ch1}][3]->dlo[{ch2}][0];"
|
||||
"tsi[{ch2}][2]->dlo[{ch3}][0];"
|
||||
"tsi[{ch2}][3]->dlo[{ch4}][0];"
|
||||
|
||||
"dlo[{ch1}][0]->sdi[{ch1}][0];"
|
||||
"dlo[{ch1}][1]->sdi[{ch1}][1];"
|
||||
"dlo[{ch2}][0]->sdi[{ch2}][0];"
|
||||
"dlo[{ch2}][1]->sdi[{ch2}][1];"
|
||||
"dlo[{ch3}][0]->sdi[{ch3}][0];"
|
||||
"dlo[{ch3}][1]->sdi[{ch3}][1];"
|
||||
"dlo[{ch4}][0]->sdi[{ch4}][0];"
|
||||
"dlo[{ch4}][1]->sdi[{ch4}][1];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST425_Quad_3Gb_2SI,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
4,
|
||||
4,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
// Framestores 1-2 -> TSI Mux 1-2
|
||||
// -> Dual-Link 1-4 -> SDI 1-4
|
||||
"fb[{ch1}][2]->tsi[{ch1}][0];"
|
||||
"fb[{ch1}][3]->tsi[{ch1}][1];"
|
||||
|
||||
"fb[{ch2}][2]->tsi[{ch2}][0];"
|
||||
"fb[{ch2}][3]->tsi[{ch2}][1];"
|
||||
|
||||
"tsi[{ch1}][2]->dlo[{ch1}][0];"
|
||||
"tsi[{ch1}][3]->dlo[{ch2}][0];"
|
||||
"tsi[{ch2}][2]->dlo[{ch3}][0];"
|
||||
"tsi[{ch2}][3]->dlo[{ch4}][0];"
|
||||
|
||||
"dlo[{ch1}][0]->sdi[{ch1}][0];"
|
||||
"dlo[{ch1}][1]->sdi[{ch1}][1];"
|
||||
"dlo[{ch2}][0]->sdi[{ch2}][0];"
|
||||
"dlo[{ch2}][1]->sdi[{ch2}][1];"
|
||||
"dlo[{ch3}][0]->sdi[{ch3}][0];"
|
||||
"dlo[{ch3}][1]->sdi[{ch3}][1];"
|
||||
"dlo[{ch4}][0]->sdi[{ch4}][0];"
|
||||
"dlo[{ch4}][1]->sdi[{ch4}][1];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST2018_6G_Squares_2SI,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
2,
|
||||
2,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST2018_12G_Squares_2SI,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"",
|
||||
}},
|
||||
{SDIWireFormat::UHD28K_ST2082_Dual_12G,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
2,
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"",
|
||||
}},
|
||||
{SDIWireFormat::UHD28K_ST2082_RGB_Dual_12G,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
2,
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"",
|
||||
}},
|
||||
{SDIWireFormat::UHD28K_ST2082_Quad_12G,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
4,
|
||||
4,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"",
|
||||
}},
|
||||
};
|
466
plugins/aja/routing/sdi_ycbcr_capture.h
Normal file
466
plugins/aja/routing/sdi_ycbcr_capture.h
Normal file
@ -0,0 +1,466 @@
|
||||
#pragma once
|
||||
|
||||
#include "../aja-routing.hpp"
|
||||
|
||||
static const std::map<SDIWireFormat, RoutingConfig> kSDIYCbCrCaptureConfigs = {
|
||||
{
|
||||
SDIWireFormat::SD_ST352,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
1, // num wires
|
||||
1, // num framestores
|
||||
false, // enable 3G output?
|
||||
false, // enable 6G output?
|
||||
false, // enable 12G output?
|
||||
false, // convert 3Gb -> 3Ga input?
|
||||
false, // convert 3Ga -> 3Gb output?
|
||||
false, // convert RGB 3Ga output?
|
||||
false, // enable 3Gb output?
|
||||
false, // enable 4K Square Division?
|
||||
false, // enable 8K Square Division?
|
||||
false, // enable two-sample-interleave?
|
||||
"sdi[{ch1}][0]->fb[{ch1}][0]", // YCbCr Capture Route
|
||||
},
|
||||
},
|
||||
{SDIWireFormat::HD_720p_ST292,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
1,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"sdi[{ch1}][0]->fb[{ch1}][0]",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080_ST292,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
1,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"sdi[{ch1}][0]->fb[{ch1}][0]",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080_ST372_Dual,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
2,
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"sdi[{ch1}][0]->fb[{ch1}][0]; sdi[{ch2}][0]->fb[{ch2}][0]",
|
||||
}},
|
||||
{SDIWireFormat::HD_720p_ST425_3Ga,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
1,
|
||||
1,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"sdi[{ch1}][0]->fb[{ch1}][0]",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080p_ST425_3Ga,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
1,
|
||||
1,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"sdi[{ch1}][0]->fb[{ch1}][0]",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080p_ST425_3Gb_DL,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
1,
|
||||
1,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"sdi[{ch1}][0]->fb[{ch1}][0]",
|
||||
}},
|
||||
{SDIWireFormat::HD_720p_ST425_3Gb,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
1,
|
||||
2,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"sdi[{ch1}][0]->fb[{ch1}][0]; sdi[{ch1}][1]->fb[{ch2}][0];",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080p_ST425_3Gb,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
1,
|
||||
2,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"sdi[{ch1}][0]->fb[{ch1}][0]; sdi[{ch1}][1]->fb[{ch2}][0];",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080p_ST425_Dual_3Ga,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
2,
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"sdi[{ch1}][0]->fb[{ch1}][0];"
|
||||
"sdi[{ch2}][0]->fb[{ch2}][0];",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080p_ST425_Dual_3Gb,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
2,
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"sdi[{ch1}][0]->fb[{ch1}][0];"
|
||||
"sdi[{ch2}][0]->fb[{ch2}][0];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST292_Dual_1_5_Squares,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
2,
|
||||
4,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
"sdi[{ch1}][0]->fb[{ch1}][0];"
|
||||
"sdi[{ch1}][1]->fb[{ch2}][0];"
|
||||
"sdi[{ch2}][0]->fb[{ch3}][0];"
|
||||
"sdi[{ch2}][1]->fb[{ch4}][0];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST292_Quad_1_5_Squares,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
4,
|
||||
4,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
// Capture
|
||||
"sdi[{ch1}][0]->fb[{ch1}][0];"
|
||||
"sdi[{ch2}][0]->fb[{ch2}][0];"
|
||||
"sdi[{ch3}][0]->fb[{ch3}][0];"
|
||||
"sdi[{ch4}][0]->fb[{ch4}][0];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST425_Quad_3Ga_Squares,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
4,
|
||||
4,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
// Capture
|
||||
"sdi[{ch1}][0]->fb[{ch1}][0];"
|
||||
"sdi[{ch2}][0]->fb[{ch2}][0];"
|
||||
"sdi[{ch3}][0]->fb[{ch3}][0];"
|
||||
"sdi[{ch4}][0]->fb[{ch4}][0];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST425_Quad_3Gb_Squares,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
4,
|
||||
4,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
// Capture
|
||||
"sdi[{ch1}][0]->fb[{ch1}][0];"
|
||||
"sdi[{ch2}][0]->fb[{ch2}][0];"
|
||||
"sdi[{ch3}][0]->fb[{ch3}][0];"
|
||||
"sdi[{ch4}][0]->fb[{ch4}][0];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST425_Dual_3Gb_2SI,
|
||||
{NTV2_MODE_CAPTURE, 2, 2, true, false, false, false, false, false,
|
||||
false, false, false, true,
|
||||
"sdi[{ch1}][0]->tsi[{ch1}][0];"
|
||||
"sdi[{ch1}][1]->tsi[{ch1}][1];"
|
||||
"sdi[{ch2}][0]->tsi[{ch2}][0];"
|
||||
"sdi[{ch2}][1]->tsi[{ch2}][1];"
|
||||
"tsi[{ch1}][0]->fb[{ch1}][0];"
|
||||
"tsi[{ch1}][1]->fb[{ch1}][1];"
|
||||
"tsi[{ch2}][0]->fb[{ch2}][0];"
|
||||
"tsi[{ch2}][1]->fb[{ch2}][1];"}},
|
||||
{SDIWireFormat::UHD4K_ST425_Quad_3Ga_2SI,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
4,
|
||||
4,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"sdi[{ch1}][0]->tsi[{ch1}][0];"
|
||||
"sdi[{ch2}][0]->tsi[{ch1}][1];"
|
||||
"sdi[{ch3}][0]->tsi[{ch2}][0];"
|
||||
"sdi[{ch4}][0]->tsi[{ch2}][1];"
|
||||
"tsi[{ch1}][0]->fb[{ch1}][0];"
|
||||
"tsi[{ch1}][1]->fb[{ch1}][1];"
|
||||
"tsi[{ch2}][0]->fb[{ch2}][0];"
|
||||
"tsi[{ch2}][1]->fb[{ch2}][1];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST425_Quad_3Gb_2SI,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
4,
|
||||
4,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"sdi[{ch1}][0]->tsi[{ch1}][0];"
|
||||
"sdi[{ch2}][0]->tsi[{ch1}][1];"
|
||||
"sdi[{ch3}][0]->tsi[{ch2}][0];"
|
||||
"sdi[{ch4}][0]->tsi[{ch2}][1];"
|
||||
"tsi[{ch1}][0]->fb[{ch1}][0];"
|
||||
"tsi[{ch1}][1]->fb[{ch1}][1];"
|
||||
"tsi[{ch2}][0]->fb[{ch2}][0];"
|
||||
"tsi[{ch2}][1]->fb[{ch2}][1];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST2018_6G_Squares_2SI,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
1,
|
||||
1,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"sdi[{ch1}][0]->fb[{ch1}][0];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST2018_6G_Squares_2SI_Kona5_io4KPlus,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
2,
|
||||
2,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
// Capture
|
||||
"sdi[{ch1}][0]->tsi[{ch1}][0];"
|
||||
"sdi[{ch2}][0]->tsi[{ch1}][1];"
|
||||
"sdi[{ch3}][0]->tsi[{ch2}][0];"
|
||||
"sdi[{ch4}][0]->tsi[{ch2}][1];"
|
||||
"tsi[{ch1}][0]->fb[{ch1}][0];"
|
||||
"tsi[{ch1}][1]->fb[{ch1}][1];"
|
||||
"tsi[{ch2}][0]->fb[{ch2}][0];"
|
||||
"tsi[{ch2}][1]->fb[{ch2}][1];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST2018_12G_Squares_2SI,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
1,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"sdi[{ch1}][0]->fb[{ch1}][0];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST2018_12G_Squares_2SI_Kona5_io4KPlus,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
1,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"",
|
||||
}},
|
||||
{SDIWireFormat::UHD28K_ST2082_Dual_12G,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
2,
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"",
|
||||
}},
|
||||
{SDIWireFormat::UHD28K_ST2082_RGB_Dual_12G,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
2,
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"",
|
||||
}},
|
||||
{SDIWireFormat::UHD28K_ST2082_Quad_12G,
|
||||
{
|
||||
NTV2_MODE_CAPTURE,
|
||||
4,
|
||||
4,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"",
|
||||
}},
|
||||
};
|
486
plugins/aja/routing/sdi_ycbcr_display.h
Normal file
486
plugins/aja/routing/sdi_ycbcr_display.h
Normal file
@ -0,0 +1,486 @@
|
||||
#pragma once
|
||||
|
||||
#include "../aja-routing.hpp"
|
||||
|
||||
static const std::map<SDIWireFormat, RoutingConfig> kSDIYCbCrDisplayConfigs = {
|
||||
{
|
||||
SDIWireFormat::SD_ST352,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1, // num wires
|
||||
1, // num framestores
|
||||
false, // enable 3G output?
|
||||
false, // enable 6G output?
|
||||
false, // enable 12G output?
|
||||
false, // convert 3Gb -> 3Ga input?
|
||||
false, // convert 3Ga -> 3Gb output?
|
||||
false, // convert RGB 3Ga output?
|
||||
false, // enable 3Gb output?
|
||||
false, // enable 4K Square Division?
|
||||
false, // enable 8K Square Division?
|
||||
false, // enable two-sample-interleave?
|
||||
"fb[{ch1}][0]->sdi[{ch1}][0]", // YCbCr Output Route
|
||||
},
|
||||
},
|
||||
{SDIWireFormat::HD_720p_ST292,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"fb[{ch1}][0]->sdi[{ch1}][0]",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080_ST292,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"fb[{ch1}][0]->sdi[{ch1}][0]",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080_ST372_Dual,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
2,
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"fb[{ch1}][0]->sdi[{ch1}][0];"
|
||||
"fb[{ch2}][0]->sdi[{ch2}][0]",
|
||||
}},
|
||||
{SDIWireFormat::HD_720p_ST425_3Ga,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1,
|
||||
1,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"fb[{ch1}][0]->sdi[{ch1}][0]",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080p_ST425_3Ga,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1,
|
||||
1,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"fb[{ch1}][0]->sdi[{ch1}][0]",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080p_ST425_3Gb_DL,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1,
|
||||
1,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"fb[{ch1}][0]->sdi[{ch1}][0]",
|
||||
}},
|
||||
{SDIWireFormat::HD_720p_ST425_3Gb,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1,
|
||||
2,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"fb[{ch1}][0]->sdi[{ch1}][0]; fb[{ch2}][0]->sdi[{ch1}][1];",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080p_ST425_3Gb,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1,
|
||||
2,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"fb[{ch1}][0]->sdi[{ch1}][0]; fb[{ch2}][0]->sdi[{ch1}][1];",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080p_ST425_Dual_3Ga,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
2,
|
||||
2,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"",
|
||||
}},
|
||||
{SDIWireFormat::HD_1080p_ST425_Dual_3Gb,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
2,
|
||||
2,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST292_Dual_1_5_Squares,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
2,
|
||||
4,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
"fb[{ch1}][0]->sdi[{ch1}][0];"
|
||||
"fb[{ch2}][0]->sdi[{ch1}][1];"
|
||||
"fb[{ch3}][0]->sdi[{ch2}][0];"
|
||||
"fb[{ch4}][0]->sdi[{ch2}][1];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST292_Quad_1_5_Squares,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
4,
|
||||
4,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
// Playout
|
||||
"fb[{ch1}][0]->sdi[{ch1}][0];"
|
||||
"fb[{ch2}][0]->sdi[{ch2}][0];"
|
||||
"fb[{ch3}][0]->sdi[{ch3}][0];"
|
||||
"fb[{ch4}][0]->sdi[{ch4}][0];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST425_Quad_3Ga_Squares,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
4,
|
||||
4,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
// Playout
|
||||
"fb[{ch1}][0]->sdi[{ch1}][0];"
|
||||
"fb[{ch2}][0]->sdi[{ch2}][0];"
|
||||
"fb[{ch3}][0]->sdi[{ch3}][0];"
|
||||
"fb[{ch4}][0]->sdi[{ch4}][0];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST425_Quad_3Gb_Squares,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
4,
|
||||
4,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
// Playout
|
||||
"fb[{ch1}][0]->sdi[{ch1}][0];"
|
||||
"fb[{ch2}][0]->sdi[{ch2}][0];"
|
||||
"fb[{ch3}][0]->sdi[{ch3}][0];"
|
||||
"fb[{ch4}][0]->sdi[{ch4}][0];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST425_Dual_3Gb_2SI,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
2,
|
||||
2,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"fb[{ch1}][0]->tsi[{ch1}][0];"
|
||||
"fb[{ch1}][1]->tsi[{ch1}][1];"
|
||||
"fb[{ch2}][0]->tsi[{ch2}][0];"
|
||||
"fb[{ch2}][1]->tsi[{ch2}][1];"
|
||||
"tsi[{ch1}][0]->sdi[{ch1}][0];"
|
||||
"tsi[{ch1}][1]->sdi[{ch1}][1];"
|
||||
"tsi[{ch2}][0]->sdi[{ch2}][0];"
|
||||
"tsi[{ch2}][1]->sdi[{ch2}][1];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST425_Quad_3Ga_2SI,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
4,
|
||||
4,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"fb[{ch1}][0]->tsi[{ch1}][0];"
|
||||
"fb[{ch1}][1]->tsi[{ch1}][1];"
|
||||
"fb[{ch2}][0]->tsi[{ch2}][0];"
|
||||
"fb[{ch2}][1]->tsi[{ch2}][1];"
|
||||
"tsi[{ch1}][0]->sdi[{ch1}][0];"
|
||||
"tsi[{ch1}][1]->sdi[{ch2}][0];"
|
||||
"tsi[{ch2}][0]->sdi[{ch3}][0];"
|
||||
"tsi[{ch2}][1]->sdi[{ch4}][0];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST425_Quad_3Gb_2SI,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
4,
|
||||
4,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"fb[{ch1}][0]->tsi[{ch1}][0];"
|
||||
"fb[{ch1}][1]->tsi[{ch1}][1];"
|
||||
"fb[{ch2}][0]->tsi[{ch2}][0];"
|
||||
"fb[{ch2}][1]->tsi[{ch2}][1];"
|
||||
"tsi[{ch1}][0]->sdi[{ch1}][0];"
|
||||
"tsi[{ch1}][1]->sdi[{ch2}][0];"
|
||||
"tsi[{ch2}][0]->sdi[{ch3}][0];"
|
||||
"tsi[{ch2}][1]->sdi[{ch4}][0];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST2018_6G_Squares_2SI,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1,
|
||||
1,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"fb[{ch1}][0]->sdi[{ch1}][0];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST2018_6G_Squares_2SI_Kona5_io4KPlus,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
2,
|
||||
2,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
// Playout
|
||||
"fb[{ch3}][0]->tsi[{ch3}][0];"
|
||||
"fb[{ch3}][1]->tsi[{ch3}][1];"
|
||||
"fb[{ch4}][0]->tsi[{ch4}][0];"
|
||||
"fb[{ch4}][1]->tsi[{ch4}][1];"
|
||||
"tsi[{ch3}][0]->sdi[{ch1}][0];"
|
||||
"tsi[{ch3}][1]->sdi[{ch2}][0];"
|
||||
"tsi[{ch4}][0]->sdi[{ch3}][0];"
|
||||
"tsi[{ch4}][1]->sdi[{ch4}][0];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST2018_12G_Squares_2SI,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"fb[{ch1}][0]->sdi[{ch1}][0];",
|
||||
}},
|
||||
{SDIWireFormat::UHD4K_ST2018_12G_Squares_2SI_Kona5_io4KPlus,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
1,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
// Playout
|
||||
"fb[{ch3}][0]->tsi[{ch3}][0];"
|
||||
"fb[{ch3}][1]->tsi[{ch3}][1];"
|
||||
"fb[{ch4}][0]->tsi[{ch4}][0];"
|
||||
"fb[{ch4}][1]->tsi[{ch4}][1];"
|
||||
"tsi[{ch3}][0]->sdi[{ch1}][0];"
|
||||
"tsi[{ch3}][1]->sdi[{ch2}][0];"
|
||||
"tsi[{ch4}][0]->sdi[{ch3}][0];"
|
||||
"tsi[{ch4}][1]->sdi[{ch4}][0];",
|
||||
}},
|
||||
{SDIWireFormat::UHD28K_ST2082_Dual_12G,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
2,
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"",
|
||||
}},
|
||||
{SDIWireFormat::UHD28K_ST2082_RGB_Dual_12G,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
2,
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"",
|
||||
}},
|
||||
{SDIWireFormat::UHD28K_ST2082_Quad_12G,
|
||||
{
|
||||
NTV2_MODE_DISPLAY,
|
||||
4,
|
||||
4,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"",
|
||||
}},
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user