aja: Refactor, clean-up and fix bugs in the signal routing system, and add SDITransport UI option to assist signal routing.

This commit is contained in:
Paul Hindt
2022-01-07 18:45:08 -08:00
committed by Colin Edwards
parent d78971b4db
commit f09137a2e0
32 changed files with 2806 additions and 3297 deletions

View File

@@ -65,7 +65,6 @@ AJAOutput::AJAOutput(CNTV2Card *card, const std::string &cardID,
mOutputID{outputID},
mDeviceIndex{deviceIndex},
mDeviceID{deviceID},
mFrameTimes{},
mAudioPlayCursor{0},
mAudioWriteCursor{0},
mAudioWrapAddress{0},
@@ -140,31 +139,17 @@ void AJAOutput::Initialize(const OutputProps &props)
props.pixelFormat);
mCard->SetOutputFrame(props.Channel(), mWriteCardFrame);
mCard->WaitForOutputVerticalInterrupt(props.Channel());
const auto &cardFrameRate =
GetNTV2FrameRateFromVideoFormat(props.videoFormat);
ULWord fpsNum = 0;
ULWord fpsDen = 0;
GetFramesPerSecond(cardFrameRate, fpsNum, fpsDen);
mFrameRateNum = fpsNum;
mFrameRateDen = fpsDen;
mFrameTimes.cardFrameTime =
(1000000000ULL / (uint64_t)(fpsNum / fpsDen));
mFrameTimes.cardFps = (double)(fpsNum / fpsDen);
mFrameTimes.obsFps = obs_get_active_fps();
if (mFrameTimes.obsFps < 1.0)
mFrameTimes.obsFps = 30.0;
mFrameTimes.obsFrameTime =
(1000000000ULL / (uint64_t)mFrameTimes.obsFps);
mVideoDelay = ((int64_t)mNumCardFrames - 0) * 1000000 * mFrameRateDen /
mFrameRateNum;
mAudioRate = props.audioSampleRate;
SetOutputProps(props);
}
@@ -510,7 +495,7 @@ uint32_t AJAOutput::get_frame_count()
}
// Perform DMA of audio samples to AJA card while taking into account wrapping around the
// ends of the card's audio buffer (size set to 4MB in Routing::ConfigureOutputAudio).
// ends of the card's audio buffer (size set to 4MB in aja::Routing::ConfigureOutputAudio).
void AJAOutput::dma_audio_samples(NTV2AudioSystem audioSys, uint32_t *data,
size_t size)
{
@@ -780,28 +765,8 @@ bool aja_output_device_changed(void *data, obs_properties_t *props,
populate_output_device_list(list);
const char *cardID = obs_data_get_string(settings, kUIPropDevice.id);
size_t itemCount = obs_property_list_item_count(list);
bool itemFound = false;
for (size_t i = 0; i < itemCount; i++) {
const char *itemCardID = obs_property_list_item_string(list, i);
if (strcmp(cardID, itemCardID) == 0) {
itemFound = true;
break;
}
}
if (!itemFound) {
obs_property_list_insert_string(list, 0, cardID, cardID);
obs_property_list_item_disable(list, 0, true);
return true;
}
if (!cardID) {
blog(LOG_ERROR, "aja_output_device_changed: Card ID is null!");
if (!cardID)
return false;
}
const char *outputID =
obs_data_get_string(settings, kUIPropAJAOutputID.id);
@@ -828,8 +793,10 @@ bool aja_output_device_changed(void *data, obs_properties_t *props,
obs_properties_get(props, kUIPropVideoFormatSelect.id);
obs_property_t *pix_fmt_list =
obs_properties_get(props, kUIPropPixelFormatSelect.id);
obs_property_t *sdi_trx_list =
obs_properties_get(props, kUIPropSDITransport.id);
obs_property_t *sdi_4k_list =
obs_properties_get(props, kUIPropSDI4KTransport.id);
obs_properties_get(props, kUIPropSDITransport4K.id);
const NTV2DeviceID deviceID = cardEntry->GetDeviceID();
populate_io_selection_output_list(cardID, outputID, deviceID,
@@ -850,6 +817,11 @@ bool aja_output_device_changed(void *data, obs_properties_t *props,
obs_property_list_clear(pix_fmt_list);
populate_pixel_format_list(deviceID, pix_fmt_list);
IOSelection io_select = static_cast<IOSelection>(
obs_data_get_int(settings, kUIPropOutput.id));
obs_property_list_clear(sdi_trx_list);
populate_sdi_transport_list(sdi_trx_list, io_select);
obs_property_list_clear(sdi_4k_list);
populate_sdi_4k_transport_list(sdi_4k_list);
@@ -863,11 +835,9 @@ bool aja_output_dest_changed(obs_properties_t *props, obs_property_t *list,
blog(LOG_DEBUG, "AJA Output Dest Changed");
int dest = obs_data_get_int(settings, kUIPropOutput.id);
size_t itemCount = obs_property_list_item_count(list);
bool itemFound = false;
int dest = obs_data_get_int(settings, kUIPropOutput.id);
size_t itemCount = obs_property_list_item_count(list);
for (size_t i = 0; i < itemCount; i++) {
int itemDest = obs_property_list_item_int(list, i);
if (dest == itemDest) {
@@ -875,29 +845,12 @@ bool aja_output_dest_changed(obs_properties_t *props, obs_property_t *list,
break;
}
}
if (!itemFound) {
obs_property_list_insert_int(list, 0, "", dest);
obs_property_list_item_disable(list, 0, true);
return true;
}
const char *cardID = obs_data_get_string(settings, kUIPropDevice.id);
if (!cardID) {
blog(LOG_ERROR, "aja_output_dest_changed: Card ID is null!");
return false;
}
auto &cardManager = aja::CardManager::Instance();
cardManager.EnumerateCards();
auto cardEntry = cardManager.GetCardEntry(cardID);
if (!cardEntry) {
blog(LOG_ERROR,
"aja_output_dest_changed: Card entry not found for %s",
cardID);
return false;
}
// Revert to "Select..." if desired IOSelection is already in use
auto io_select = static_cast<IOSelection>(
obs_data_get_int(settings, kUIPropOutput.id));
@@ -916,6 +869,13 @@ bool aja_output_dest_changed(obs_properties_t *props, obs_property_t *list,
}
}
obs_property_t *sdi_trx_list =
obs_properties_get(props, kUIPropSDITransport.id);
obs_property_list_clear(sdi_trx_list);
populate_sdi_transport_list(sdi_trx_list, io_select);
obs_property_set_visible(sdi_trx_list,
aja::IsIOSelectionSDI(io_select));
return true;
}
@@ -971,7 +931,6 @@ static void *aja_output_create(obs_data_t *settings, obs_output_t *output)
}
NTV2DeviceID deviceID = card->GetDeviceID();
OutputProps outputProps(deviceID);
outputProps.ioSelect = static_cast<IOSelection>(
obs_data_get_int(settings, kUIPropOutput.id));
@@ -980,20 +939,19 @@ static void *aja_output_create(obs_data_t *settings, obs_output_t *output)
"aja_output_create: Select a valid AJA Output IOSelection!");
return nullptr;
}
outputProps.videoFormat = static_cast<NTV2VideoFormat>(
obs_data_get_int(settings, kUIPropVideoFormatSelect.id));
outputProps.pixelFormat = static_cast<NTV2PixelFormat>(
obs_data_get_int(settings, kUIPropPixelFormatSelect.id));
outputProps.sdi4kTransport = static_cast<SDI4KTransport>(
obs_data_get_int(settings, kUIPropSDI4KTransport.id));
outputProps.sdiTransport = static_cast<SDITransport>(
obs_data_get_int(settings, kUIPropSDITransport.id));
outputProps.sdi4kTransport = static_cast<SDITransport4K>(
obs_data_get_int(settings, kUIPropSDITransport4K.id));
outputProps.audioNumChannels = kDefaultAudioChannels;
outputProps.audioSampleSize = kDefaultAudioSampleSize;
outputProps.audioSampleRate = kDefaultAudioSampleRate;
if (NTV2_IS_4K_VIDEO_FORMAT(outputProps.videoFormat) &&
outputProps.sdi4kTransport == SDI4KTransport::Squares) {
outputProps.sdi4kTransport == SDITransport4K::Squares) {
if (outputProps.ioSelect == IOSelection::SDI1_2) {
outputProps.ioSelect = IOSelection::SDI1_2_Squares;
} else if (outputProps.ioSelect == IOSelection::SDI3_4) {
@@ -1025,7 +983,6 @@ static void *aja_output_create(obs_data_t *settings, obs_output_t *output)
auto ajaOutput = new AJAOutput(card, cardID, outputID,
(UWord)cardEntry->GetCardIndex(),
deviceID);
ajaOutput->Initialize(outputProps);
ajaOutput->ClearVideoQueue();
ajaOutput->ClearAudioQueue();
@@ -1066,9 +1023,7 @@ static bool aja_output_start(void *data)
const std::string &cardID = ajaOutput->mCardID;
auto &cardManager = aja::CardManager::Instance();
cardManager.EnumerateCards();
auto cardEntry = cardManager.GetCardEntry(cardID);
if (!cardEntry) {
blog(LOG_DEBUG,
@@ -1115,14 +1070,14 @@ static bool aja_output_start(void *data)
}
// Configures crosspoint routing on AJA card
if (!Routing::ConfigureOutputRoute(outputProps, NTV2_MODE_DISPLAY,
card)) {
if (!aja::Routing::ConfigureOutputRoute(outputProps, NTV2_MODE_DISPLAY,
card)) {
blog(LOG_ERROR,
"aja_output_start: Error configuring output route!");
return false;
}
Routing::ConfigureOutputAudio(outputProps, card);
aja::Routing::ConfigureOutputAudio(outputProps, card);
const auto &formatDesc = outputProps.FormatDesc();
struct video_scale_info scaler = {};
@@ -1166,26 +1121,22 @@ static void aja_output_stop(void *data, uint64_t ts)
blog(LOG_ERROR, "aja_output_stop: Plugin instance is null!");
return;
}
auto outputProps = ajaOutput->GetOutputProps();
const std::string &cardID = ajaOutput->mCardID;
auto &cardManager = aja::CardManager::Instance();
cardManager.EnumerateCards();
auto cardEntry = cardManager.GetCardEntry(cardID);
if (!cardEntry) {
blog(LOG_ERROR, "aja_output_stop: Card Entry not found for %s",
cardID.c_str());
return;
}
CNTV2Card *card = ajaOutput->GetCard();
if (!card) {
blog(LOG_ERROR, "aja_output_stop: Card instance is null!");
return;
}
auto outputProps = ajaOutput->GetOutputProps();
if (!cardEntry->ReleaseOutputSelection(outputProps.ioSelect,
card->GetDeviceID(),
ajaOutput->mOutputID)) {
@@ -1194,15 +1145,13 @@ static void aja_output_stop(void *data, uint64_t ts)
aja::IOSelectionToString(outputProps.ioSelect).c_str(),
cardID.c_str());
}
auto audioSystem = outputProps.AudioSystem();
ajaOutput->GenerateTestPattern(outputProps.videoFormat,
outputProps.pixelFormat,
NTV2_TestPatt_Black);
obs_output_end_data_capture(ajaOutput->GetOBSOutput());
auto audioSystem = outputProps.AudioSystem();
card->StopAudioOutput(audioSystem);
blog(LOG_INFO, "AJA Output stopped.");
@@ -1234,28 +1183,28 @@ static void aja_output_raw_audio(void *data, struct audio_data *frames)
static obs_properties_t *aja_output_get_properties(void *data)
{
obs_properties_t *props = obs_properties_create();
obs_property_t *device_list = obs_properties_add_list(
props, kUIPropDevice.id, obs_module_text(kUIPropDevice.text),
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
obs_property_t *output_list = obs_properties_add_list(
props, kUIPropOutput.id, obs_module_text(kUIPropOutput.text),
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
obs_property_t *vid_fmt_list = obs_properties_add_list(
props, kUIPropVideoFormatSelect.id,
obs_module_text(kUIPropVideoFormatSelect.text),
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
obs_properties_add_list(props, kUIPropPixelFormatSelect.id,
obs_module_text(kUIPropPixelFormatSelect.text),
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
obs_properties_add_list(props, kUIPropSDI4KTransport.id,
obs_module_text(kUIPropSDI4KTransport.text),
obs_properties_add_list(props, kUIPropSDITransport.id,
obs_module_text(kUIPropSDITransport.text),
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
obs_properties_add_list(props, kUIPropSDITransport.id,
obs_module_text(kUIPropSDITransport.text),
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
obs_properties_add_list(props, kUIPropSDITransport4K.id,
obs_module_text(kUIPropSDITransport4K.text),
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
obs_properties_add_bool(props, kUIPropAutoStartOutput.id,
obs_module_text(kUIPropAutoStartOutput.text));
@@ -1265,7 +1214,6 @@ static obs_properties_t *aja_output_get_properties(void *data)
aja_output_dest_changed);
obs_property_set_modified_callback2(device_list,
aja_output_device_changed, data);
return props;
}
@@ -1274,13 +1222,6 @@ static const char *aja_output_get_name(void *)
return obs_module_text(kUIPropOutputModule.text);
}
// NOTE(paulh): Drop-down defaults are set on a clean launch in aja-output-ui code.
// Otherwise we load the settings stored in the ajaOutputProps/ajaPreviewOutputProps.json configs.
void aja_output_get_defaults(obs_data_t *settings)
{
obs_data_set_default_bool(settings, kUIPropAutoStartOutput.id, false);
}
struct obs_output_info create_aja_output_info()
{
struct obs_output_info aja_output_info = {};
@@ -1295,8 +1236,6 @@ struct obs_output_info create_aja_output_info()
aja_output_info.raw_video = aja_output_raw_video;
aja_output_info.raw_audio = aja_output_raw_audio;
aja_output_info.update = aja_output_update;
aja_output_info.get_defaults = aja_output_get_defaults;
aja_output_info.get_properties = aja_output_get_properties;
return aja_output_info;
}