Files
UltraGrid/src/deltacast_common.cpp
2026-01-09 12:06:27 +01:00

1075 lines
42 KiB
C++

/**
* @file deltacast_common.cpp
* @author Martin Pulec <pulec@cesnet.cz>
*
* @sa deltacast_common.hpp for common DELTACAST information
*/
/*
* Copyright (c) 2014-2026 CESNET, zájmové sdružení právnických osob
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, is permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of CESNET nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "deltacast_common.hpp"
#include <cstddef> // for NULL
#include <cstdio> // for printf
#include <cinttypes> // for PRIu32
#include <iostream> // for basic_ostream, operator<<, cout, bas...
#include <map> // for map, operator!=, _Rb_tree_iterator
#include <unordered_map>
#include "debug.h"
#include "types.h"
#include "utils/color_out.h"
#include "video_frame.h" // for get_interlacing_suffix
#if !defined VHD_MIN_6_19
#define VHD_GetPCIeIdentificationString(BoardIndex, pIdString_c) \
snprintf_ch(pIdString_c, "UNKNOWN")
#endif
#define MOD_NAME "[DELTACAST] "
VHD_BOARDTYPE
delta_get_board_type(ULONG BoardIndex)
{
HANDLE BoardHandle = nullptr;
ULONG BoardType = 0U;
ULONG Result =
VHD_OpenBoardHandle(BoardIndex, &BoardHandle, nullptr, 0);
if (Result != VHDERR_NOERROR) {
DELTA_PRINT_ERROR(Result,
"Unable to open board %" PRIu_ULONG ".",
BoardIndex);
return NB_VHD_BOARDTYPES;
}
Result = VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_BOARD_TYPE,
&BoardType);
VHD_CloseBoardHandle(BoardHandle);
if (Result != VHDERR_NOERROR) {
DELTA_PRINT_ERROR(Result,
"Unable to get board %" PRIu_ULONG " type.",
BoardIndex);
return NB_VHD_BOARDTYPES;
}
return (VHD_BOARDTYPE) BoardType;
}
const char *
delta_get_model_name(ULONG BoardIndex)
{
thread_local char buf[128];
#if !defined VHD_MIN_6_00
#define VHD_GetBoardModel(BoardIndex) \
delta_get_board_type_name(delta_get_board_type(BoardIndex))
#endif
snprintf_ch(buf, "%s #%" PRIu_ULONG,
VHD_GetBoardModel(BoardIndex),
BoardIndex);
#ifdef VHD_GetBoardModel
#undef VHD_GetBoardModel
#endif
return buf;
}
const char *
delta_get_error_description(ULONG CodeError)
{
#ifdef HAVE_VHD_STRING
return VHD_ERRORCODE_ToPrettyString((VHD_ERRORCODE) CodeError);
#else // compat VHD < 6.30
switch (CodeError) {
case VHDERR_NOERROR:
return "No error";
case VHDERR_FATALERROR:
return "Fatal error occurred (should re-install)";
case VHDERR_OPERATIONFAILED:
return "Operation failed (undefined error)";
case VHDERR_NOTENOUGHRESOURCE:
return "Not enough resource to complete the operation";
case VHDERR_NOTIMPLEMENTED:
return "Not implemented yet";
case VHDERR_NOTFOUND:
return "Required element was not found";
case VHDERR_BADARG:
return "Bad argument value";
case VHDERR_INVALIDPOINTER:
return "Invalid pointer";
case VHDERR_INVALIDHANDLE:
return "Invalid handle";
case VHDERR_INVALIDPROPERTY:
return "Invalid property index";
case VHDERR_INVALIDSTREAM:
return "Invalid stream or invalid stream type";
case VHDERR_RESOURCELOCKED:
return "Resource is currently locked";
case VHDERR_BOARDNOTPRESENT:
return "Board is not available";
case VHDERR_INCOHERENTBOARDSTATE:
return "Incoherent board state or register value";
case VHDERR_INCOHERENTDRIVERSTATE:
return "Incoherent driver state";
case VHDERR_INCOHERENTLIBSTATE:
return "Incoherent library state";
case VHDERR_SETUPLOCKED:
return "Configuration is locked";
case VHDERR_CHANNELUSED:
return "Requested channel is already used or doesn't exist";
case VHDERR_STREAMUSED:
return "Requested stream is already used";
case VHDERR_READONLYPROPERTY:
return "Property is read-only";
case VHDERR_OFFLINEPROPERTY:
return "Property is off-line only";
case VHDERR_TXPROPERTY:
return "Property is of TX streams";
case VHDERR_TIMEOUT:
return "Time-out occurred";
case VHDERR_STREAMNOTRUNNING:
return "Stream is not running";
case VHDERR_BADINPUTSIGNAL:
return "Bad input signal, or unsupported standard";
case VHDERR_BADREFERENCESIGNAL:
return "Bad genlock signal or unsupported standard";
case VHDERR_FRAMELOCKED:
return "Frame already locked";
case VHDERR_FRAMEUNLOCKED:
return "Frame already unlocked";
case VHDERR_INCOMPATIBLESYSTEM:
return "Selected video standard is incompatible with running "
"clock system";
case VHDERR_ANCLINEISEMPTY:
return "ANC line is empty";
case VHDERR_ANCLINEISFULL:
return "ANC line is full";
case VHDERR_BUFFERTOOSMALL:
return "Buffer too small";
case VHDERR_BADANC:
return "Received ANC aren't standard";
case VHDERR_BADCONFIG:
return "Invalid configuration";
case VHDERR_FIRMWAREMISMATCH:
return "The loaded firmware is not compatible with the "
"installed driver";
case VHDERR_LIBRARYMISMATCH:
return "The loaded VideomasterHD library is not compatible "
"with the installed driver";
case VHDERR_FAILSAFE:
return "The fail safe firmware is loaded. You need to upgrade "
"your firmware";
case VHDERR_RXPROPERTY:
return "Property is of RX streams";
case VHDERR_ALREADYINITIALIZED:
return "Already initialized";
case VHDERR_NOTINITIALIZED:
return "Not initialized";
case VHDERR_CROSSTHREAD:
return "Cross-thread";
case VHDERR_INCOHERENTDATA:
return "Incoherent data";
case VHDERR_BADSIZE:
return "Bad size";
case VHDERR_WAKEUP:
return "Wake up";
case VHDERR_DEVICE_REMOVED:
return "Device removed";
case VHDERR_LTCSOURCEUNLOCKED:
return "LTC source unlocked";
#ifdef VHD_MIN_6_00
case VHDERR_INVALIDACCESSRIGHT:
return "Invalid access right";
#endif // defined VHD_MIN_6_00
#ifdef VHD_MIN_6_19
case VHDERR_INVALIDCAPABILITY:
return "Invalid capability index";
#endif // defined VHD_MIN_6_19
#ifdef DELTA_DVI_DEPRECATED
case VHDERR_DEPRECATED:
return "Symbol is deprecated";
#endif
default:
return "Unknown code error";
}
#endif
}
auto
delta_format_version(uint32_t version, bool long_out) -> std::string
{
using namespace std::string_literals;
std::string out = std::to_string(version >> 24U) + "."s +
std::to_string((version >> 16U) & 0xFFU);
if (long_out) {
out += std::to_string((version >> 8U) & 0xFFU) + "."s +
std::to_string(version & 0xFFU);
}
return out;
}
// PrintChnType in SDK
static void
print_avail_channels(HANDLE BoardHandle)
{
printf("\t - available channels:");
#ifdef HAVE_VHD_STRING
printf(" ");
// PrintChnType in SDK
ULONG ChnType, NbOfChn;
VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_NB_RXCHANNELS, &NbOfChn);
for (ULONG i = 0; i < NbOfChn; i++) {
VHD_GetChannelProperty(BoardHandle, VHD_RX_CHANNEL, i,
VHD_CORE_CP_TYPE, &ChnType);
printf("RX%" PRIu_ULONG "=%s / ", i,
VHD_CHANNELTYPE_ToPrettyString(
((VHD_CHANNELTYPE) ChnType)));
}
VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_NB_TXCHANNELS, &NbOfChn);
for (ULONG i = 0; i < NbOfChn; i++) {
VHD_GetChannelProperty(BoardHandle, VHD_TX_CHANNEL, i,
VHD_CORE_CP_TYPE, &ChnType);
printf("TX%" PRIu_ULONG "=%s / ", i,
VHD_CHANNELTYPE_ToPrettyString(
((VHD_CHANNELTYPE) ChnType)));
}
printf("\b\b\b \n");
#else // compat
ULONG avail_channs = 0;
ULONG Result = VHD_GetBoardProperty(
BoardHandle, VHD_CORE_BP_CHN_AVAILABILITY, &avail_channs);
if (Result != VHDERR_NOERROR) {
LOG(LOG_LEVEL_ERROR)
<< "[DELTACAST] Unable to available channels: "
<< delta_get_error_description(Result) << "\n";
return;
}
// RXx
// bit0 = RX0, bit1 = RX1, bit2 = RX2, bit3 = RX3
for (int i = 0; i < 4; ++i) {
if (((avail_channs >> i) & 0x1) != 0U) {
printf(" RX%d", i);
}
}
// bit8 = RX4, bit9 = RX5, bit10 = RX6, bit11 = RX7
for (int i = 4; i < 8; ++i) {
if (((avail_channs >> (i + 4)) & 0x1) != 0U) {
printf(" RX%d", i);
}
}
// TXx
// bit4 = TX0, bit5 = TX1, bit6 = TX2, bit7 = TX3
for (int i = 0; i < 4; ++i) {
if (((avail_channs >> (i + 4)) & 0x1) != 0U) {
printf(" TX%d", i);
}
}
// bit12 = TX4, bit13 = TX5, bit14 = TX6, bit15 = TX7
for (int i = 4; i < 8; ++i) {
if (((avail_channs >> (i + 8)) & 0x1) != 0U) {
printf(" TX%d", i);
}
}
printf("\n");
#endif
}
// PrintBoardInfo in SDK
static void
print_board_info(int BoardIndex, ULONG DllVersion, bool full)
{
color_printf("\tBoard " TBOLD("%d") ": " TBOLD("%s") "\n", BoardIndex,
delta_get_model_name(BoardIndex));
ULONG DriverVersion = 0U;
HANDLE BoardHandle = nullptr;
ULONG Result =
VHD_OpenBoardHandle(BoardIndex, &BoardHandle, nullptr, 0);
if (Result != VHDERR_NOERROR) {
DELTA_PRINT_ERROR(Result, "Unable to open board %d.",
BoardIndex);
return;
}
Result = VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_DRIVER_VERSION,
&DriverVersion);
if (Result != VHDERR_NOERROR) {
DELTA_PRINT_ERROR(Result, "Unable to get board %d version.",
BoardIndex);
}
if ((DllVersion >> 16U) != (DriverVersion >> 16U)) {
MSG(WARNING, "API and driver version mismatch: %s vs %s\n",
delta_format_version(DllVersion, true).c_str(),
delta_format_version(DriverVersion, true).c_str());
}
if (!full) {
VHD_CloseBoardHandle(BoardHandle);
return;
}
ULONG BoardType = 0U;
ULONG SerialNumber_UL[4] = {};
ULONG NbOfLane, BusType, FirmwareVersion, Firmware3Version, LowProfile,
NbRxChannels, NbTxChannels, ProductVersion = 0;
char pIdString_c[64];
Result = VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_BOARD_TYPE,
&BoardType);
if (Result != VHDERR_NOERROR) {
DELTA_PRINT_ERROR(Result, "Unable to get board %d type.",
BoardIndex);
}
VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_FIRMWARE_VERSION,
&FirmwareVersion);
VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_BOARD_TYPE, &BoardType);
#if defined VHD_MIN_6_21 // (not tested exactly)
VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_SERIALNUMBER_PART1_LSW,
&SerialNumber_UL[0]);
VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_SERIALNUMBER_PART2,
&SerialNumber_UL[1]);
VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_SERIALNUMBER_PART3,
&SerialNumber_UL[2]);
VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_SERIALNUMBER_PART4_MSW,
&SerialNumber_UL[3]);
#endif
VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_NBOF_LANE, &NbOfLane);
VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_LOWPROFILE, &LowProfile);
VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_NB_RXCHANNELS,
&NbRxChannels);
VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_NB_TXCHANNELS,
&NbTxChannels);
#if defined VHD_MIN_6_19
VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_PRODUCT_VERSION,
&ProductVersion);
#endif
VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_BUS_TYPE, &BusType);
VHD_GetPCIeIdentificationString(BoardIndex, pIdString_c);
printf("\t - PCIe Id string : %s\n", pIdString_c);
printf("\t - Driver %s\n",
delta_format_version(DriverVersion, false).c_str());
printf("\t - Board fpga firmware v%02" PRIX_ULONG " (%02" PRIX_ULONG
"-%02" PRIX_ULONG "-%02" PRIX_ULONG ")\n",
FirmwareVersion & 0xFF, (FirmwareVersion >> 24) & 0xFF,
(FirmwareVersion >> 16) & 0xFF, (FirmwareVersion >> 8) & 0xFF);
if (BoardType == VHD_BOARDTYPE_3G || BoardType == VHD_BOARDTYPE_3GKEY ||
(BoardType == VHD_BOARDTYPE_HD && NbTxChannels == 4)) {
VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_FIRMWARE3_VERSION,
&Firmware3Version);
printf("\t - Board micro-controller firmware v%02" PRIX_ULONG
" (%02" PRIX_ULONG "-%02" PRIX_ULONG "-%02" PRIX_ULONG
")\n",
Firmware3Version & 0xFF, (Firmware3Version >> 24) & 0xFF,
(Firmware3Version >> 16) & 0xFF,
(Firmware3Version >> 8) & 0xFF);
}
#if defined VHD_MIN_6_00
ULONG Firmware4Version = 0;
if (BoardType == VHD_BOARDTYPE_IP) {
VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_FIRMWARE4_VERSION,
&Firmware4Version);
printf("\t - Board microcode firmware v%02" PRIX_ULONG
" (%02" PRIX_ULONG "-%02" PRIX_ULONG "-%02" PRIX_ULONG
")\n",
Firmware4Version & 0xFF, (Firmware4Version >> 24) & 0xFF,
(Firmware4Version >> 16) & 0xFF,
(Firmware4Version >> 8) & 0xFF);
}
#endif
printf("\t - Board serial# : 0x%08" PRIX_ULONG "%08" PRIX_ULONG
"%08" PRIX_ULONG "%08" PRIX_ULONG "\n",
SerialNumber_UL[3], SerialNumber_UL[2], SerialNumber_UL[1],
SerialNumber_UL[0]);
if (ProductVersion != 0) {
printf("\t - Board product v%04" PRIX_ULONG "\n",
ProductVersion);
}
#ifdef HAVE_VHD_STRING
#define bus_type_to_str(x) VHD_BUSTYPE_ToPrettyString((VHD_BUSTYPE) x)
#else
#define bus_type_to_str(x) "unknown bus"
#endif
printf("\t - %s on %s", delta_get_board_type_name(BoardType),
bus_type_to_str(BusType));
#undef bus_type_to_str
if (NbOfLane)
printf(" (%" PRIu_ULONG " lane%s)\n", NbOfLane,
(NbOfLane > 1) ? "s" : "");
else
printf("\n");
printf("\t - %s\n", LowProfile ? "Low profile" : "Full height");
printf("\t - %" PRIu_ULONG " In / %" PRIu_ULONG " Out\n", NbRxChannels,
NbTxChannels);
print_avail_channels(BoardHandle);
const char *bidir_status = "ERROR";
ULONG IsBiDir = 0;
if (VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_IS_BIDIR, &IsBiDir) ==
VHDERR_NOERROR) {
bidir_status = IsBiDir == true ? "supported" : "not supported";
}
printf("\t - bidirectional (switchable) channels: "
"%s\n",
bidir_status);
VHD_CloseBoardHandle(BoardHandle);
}
void
print_available_delta_boards(bool full)
{
ULONG Result, DllVersion, NbBoards;
Result = VHD_GetApiInfo(&DllVersion, &NbBoards);
if (Result != VHDERR_NOERROR) {
log_msg(LOG_LEVEL_ERROR,
"[DELTACAST] ERROR : Cannot query VideoMasterHD"
" information. Result = 0x%08" PRIX_ULONG "\n",
Result);
return;
}
std::cout << "\nAPI version: "
<< delta_format_version(DllVersion, false) << "\n";
std::cout << "\nAvailable cards:\n";
if (NbBoards == 0) {
log_msg(LOG_LEVEL_ERROR,
"[DELTACAST] No DELTA board detected, exiting...\n");
return;
}
/* Query DELTA boards information */
for (ULONG i = 0; i < NbBoards; i++) {
print_board_info(i, DllVersion, full);
}
std::cout << "\n";
}
void
delta_print_ch_layout_help(bool full)
{
color_printf(
"\t" TBOLD("ch_layout") " - configure bidirectional channels%s\n",
full ? ":" : ", see \":fullhelp\" for details");
if (full) {
color_printf("\t\tSet the layout with a number in format RxTx, "
"examples:\n"
"\t\t\t- 80 - 8 Rx and 0 TX\n"
"\t\t\t- 44 - 4 Rx and 4 TX\n"
"\t\t\t- 13 - 1 Rx and 3 TX (4 channel device)\n"
"\t\tIt is user responsibility to enter the valid "
"number (not exceeding device channels).\n");
}
}
/// from SDK SetNbChannels()
bool
delta_set_nb_channels(ULONG BrdId, HANDLE BoardHandle, ULONG RequestedRx,
ULONG RequestedTx)
{
ULONG Result;
ULONG NbRxOnBoard = 0;
ULONG NbTxOnBoard = 0;
ULONG NbChanOnBoard;
BOOL32 IsBiDir = false;
Result = VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_NB_RXCHANNELS,
&NbRxOnBoard);
if (Result != VHDERR_NOERROR) {
log_msg(LOG_LEVEL_ERROR,
"[DELTACAST] ERROR: Cannot get number of RX channels. "
"Result = 0x%08" PRIX_ULONG "\n",
Result);
return false;
}
Result = VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_NB_TXCHANNELS,
&NbTxOnBoard);
if (Result != VHDERR_NOERROR) {
log_msg(LOG_LEVEL_ERROR,
"[DELTACAST] ERROR: Cannot get number of TX channels. "
"Result = 0x%08" PRIX_ULONG "\n",
Result);
return false;
}
if (NbRxOnBoard >= RequestedRx && NbTxOnBoard >= RequestedTx) {
return true;
}
Result = VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_IS_BIDIR,
(ULONG *) &IsBiDir);
if (Result != VHDERR_NOERROR) {
log_msg(LOG_LEVEL_ERROR,
"[DELTACAST] ERROR: Cannot check whether board "
"channels are bidirectional. Result = 0x%08" PRIX_ULONG
"\n",
Result);
return false;
}
NbChanOnBoard = NbRxOnBoard + NbTxOnBoard;
if (!IsBiDir || NbChanOnBoard < (RequestedRx + RequestedTx)) {
MSG(ERROR,
"ERROR: Insufficient number of channels - "
"requested %" PRIu_ULONG " RX + %" PRIu_ULONG
" TX, got %" PRIu_ULONG " RX + %" PRIu_ULONG
" TX. %s\n",
RequestedRx, RequestedTx, NbRxOnBoard, NbTxOnBoard,
IsBiDir ? "Bidirectional" : "Non-bidirectional");
return false;
}
// key - (NbChanOnBoard, RequestedRX), value - member of
// VHD_BIDIRCFG_2C, VHD_BIDIRCFG_4C or VHD_BIDIRCFG_8C
std::map<std::pair<ULONG, ULONG>, ULONG> mapping = {
//{{2, 0}, VHD_BIDIR_02},
//{{2, 1}, VHD_BIDIR_11},
//{{2, 2}, VHD_BIDIR_20},
{ { 4, 0 }, VHD_BIDIR_04 },
{ { 4, 1 }, VHD_BIDIR_13 },
{ { 4, 2 }, VHD_BIDIR_22 },
{ { 4, 3 }, VHD_BIDIR_31 },
{ { 4, 4 }, VHD_BIDIR_40 },
{ { 8, 0 }, VHD_BIDIR_08 },
{ { 8, 1 }, VHD_BIDIR_17 },
{ { 8, 2 }, VHD_BIDIR_26 },
{ { 8, 3 }, VHD_BIDIR_35 },
{ { 8, 4 }, VHD_BIDIR_44 },
{ { 8, 5 }, VHD_BIDIR_53 },
{ { 8, 6 }, VHD_BIDIR_62 },
{ { 8, 7 }, VHD_BIDIR_71 },
{ { 8, 8 }, VHD_BIDIR_80 }
};
auto it = mapping.find({ NbChanOnBoard, RequestedRx });
if (it == mapping.end()) {
MSG(ERROR, "Sufficient number of channels and board is "
"switchable but cannot find a mapping! Please "
"report...\n");
return false;
}
Result = VHD_SetBiDirCfg(BrdId, it->second);
if (Result == VHDERR_NOERROR) {
MSG(INFO,
"Set bidirectional channel configuration %" PRIu_ULONG
" In / %" PRIu_ULONG " Out\n",
RequestedRx, NbChanOnBoard - RequestedRx);
return true;
}
MSG(ERROR,
"ERROR: Cannot set bidirectional channels. Result = "
"0x%08" PRIX_ULONG "\n",
Result);
MSG(WARNING, "See also option \":ch_layout\" to switch "
"bidirectional channels layout.\n");
return false;
}
/// @returns stream type corresponding channel ID or NB_VHD_STREAMTYPES if too
/// high
VHD_STREAMTYPE
delta_rx_ch_to_stream_t(unsigned channel)
{
switch (channel) {
case 0:
return VHD_ST_RX0;
case 1:
return VHD_ST_RX1;
case 2:
return VHD_ST_RX2;
case 3:
return VHD_ST_RX3;
case 4:
return VHD_ST_RX4;
case 5:
return VHD_ST_RX5;
case 6:
return VHD_ST_RX6;
case 7:
return VHD_ST_RX7;
}
log_msg(LOG_LEVEL_ERROR, "[DELTACAST] Channel index %u out of bound!\n",
channel);
return NB_VHD_STREAMTYPES;
}
/// @copydoc delta_rx_ch_to_stream_t
VHD_STREAMTYPE
delta_tx_ch_to_stream_t(unsigned channel)
{
switch (channel) {
case 0:
return VHD_ST_TX0;
case 1:
return VHD_ST_TX1;
case 2:
return VHD_ST_TX2;
case 3:
return VHD_ST_TX3;
case 4:
return VHD_ST_TX4;
case 5:
return VHD_ST_TX5;
case 6:
return VHD_ST_TX6;
case 7:
return VHD_ST_TX7;
}
log_msg(LOG_LEVEL_ERROR, "[DELTACAST] Channel index %u out of bound!\n",
channel);
return NB_VHD_STREAMTYPES;
}
/// @see SDK Is4KInterface() + Is8KInterface
bool
delta_is_quad_channel_interface(ULONG Interface)
{
switch (Interface) {
// Is4KInterface
case VHD_INTERFACE_4XHD_QUADRANT:
case VHD_INTERFACE_4X3G_A_QUADRANT:
case VHD_INTERFACE_4X3G_B_DL_QUADRANT:
case VHD_INTERFACE_2X3G_B_DS_425_3:
case VHD_INTERFACE_4X3G_A_425_5:
case VHD_INTERFACE_4X3G_B_DL_425_5:
#if defined VHD_MIN_6_00
case VHD_INTERFACE_2X3G_B_DS_425_3_DUAL:
case VHD_INTERFACE_4XHD_QUADRANT_DUAL:
case VHD_INTERFACE_4X3G_A_QUADRANT_DUAL:
case VHD_INTERFACE_4X3G_A_425_5_DUAL:
case VHD_INTERFACE_4X3G_B_DL_QUADRANT_DUAL:
case VHD_INTERFACE_4X3G_B_DL_425_5_DUAL:
#endif
// Is8KInterface
#if defined VHD_MIN_6_19
case VHD_INTERFACE_4X6G_2081_10_QUADRANT:
case VHD_INTERFACE_4X12G_2082_10_QUADRANT:
case VHD_INTERFACE_4X6G_2081_12:
case VHD_INTERFACE_4X12G_2082_12:
#endif
return true;
default:
return false;
}
}
/// equally named fn in SDK
static VHD_CORE_BOARDPROPERTY GetPassiveLoopbackProperty(int ChannelIdx)
{
switch (ChannelIdx)
{
case 0: return VHD_CORE_BP_BYPASS_RELAY_0;
case 1: return VHD_CORE_BP_BYPASS_RELAY_1;
case 2: return VHD_CORE_BP_BYPASS_RELAY_2;
case 3: return VHD_CORE_BP_BYPASS_RELAY_3;
default: return NB_VHD_CORE_BOARDPROPERTIES;
}
}
/// @sa SDK SetLoopbackState() since VideoMaster 6.21 but simplified to the extent
/// of features supported by the prior versions (passive loopback only)
void
delta_set_loopback_state(HANDLE BoardHandle, int ChannelIndex, BOOL32 State)
{
VHD_CORE_BOARDPROPERTY prop = GetPassiveLoopbackProperty(ChannelIndex);
if (prop == NB_VHD_CORE_BOARDPROPERTIES) {
return;
}
ULONG err = VHD_SetBoardProperty(BoardHandle, prop, State);
if (err != VHDERR_NOERROR) {
MSG(VERBOSE, "Cannot set passive loopback for channel %d!\n",
ChannelIndex);
}
}
#if !defined VHD_MIN_6_13
struct deltacast_mode_info {
unsigned int width;
unsigned int height;
int fps;
enum interlacing_t interlacing;
};
static struct deltacast_mode_info
deltacast_get_frame_mode(unsigned mode)
{
switch (mode) {
// clang-format off
case VHD_VIDEOSTD_S274M_1080p_25Hz: return { 1920, 1080, 25, PROGRESSIVE };
case VHD_VIDEOSTD_S274M_1080p_30Hz: return { 1920, 1080, 30, PROGRESSIVE };
case VHD_VIDEOSTD_S274M_1080i_50Hz: return { 1920, 1080, 25, UPPER_FIELD_FIRST };
case VHD_VIDEOSTD_S274M_1080i_60Hz: return { 1920, 1080, 30, UPPER_FIELD_FIRST };
case VHD_VIDEOSTD_S296M_720p_50Hz: return { 1280, 720, 50, PROGRESSIVE };
case VHD_VIDEOSTD_S296M_720p_60Hz: return { 1280, 720, 60, PROGRESSIVE };
case VHD_VIDEOSTD_S259M_PAL: return { 720, 576, 25, UPPER_FIELD_FIRST };
case VHD_VIDEOSTD_S259M_NTSC: return { 720, 487, 30, UPPER_FIELD_FIRST };
case VHD_VIDEOSTD_S274M_1080p_24Hz: return { 1920, 1080, 24, PROGRESSIVE };
case VHD_VIDEOSTD_S274M_1080p_60Hz: return { 1920, 1080, 60, PROGRESSIVE };
case VHD_VIDEOSTD_S274M_1080p_50Hz: return { 1920, 1080, 50, PROGRESSIVE };
case VHD_VIDEOSTD_S274M_1080psf_24Hz: return { 1920, 1080, 24, SEGMENTED_FRAME };
case VHD_VIDEOSTD_S274M_1080psf_25Hz: return { 1920, 1080, 25, SEGMENTED_FRAME };
case VHD_VIDEOSTD_S274M_1080psf_30Hz: return { 1920, 1080, 30, SEGMENTED_FRAME };
// UHD modes
case VHD_VIDEOSTD_3840x2160p_24Hz: return { 3840, 2160, 24, PROGRESSIVE };
case VHD_VIDEOSTD_3840x2160p_25Hz: return { 3840, 2160, 25, PROGRESSIVE };
case VHD_VIDEOSTD_3840x2160p_30Hz: return { 3840, 2160, 30, PROGRESSIVE };
case VHD_VIDEOSTD_3840x2160p_50Hz: return { 3840, 2160, 50, PROGRESSIVE };
case VHD_VIDEOSTD_3840x2160p_60Hz: return { 3840, 2160, 60, PROGRESSIVE };
case VHD_VIDEOSTD_4096x2160p_24Hz: return { 4096, 2160, 24, PROGRESSIVE };
case VHD_VIDEOSTD_4096x2160p_25Hz: return { 4096, 2160, 25, PROGRESSIVE };
case VHD_VIDEOSTD_4096x2160p_48Hz: return { 4096, 2160, 48, PROGRESSIVE };
case VHD_VIDEOSTD_4096x2160p_50Hz: return { 4096, 2160, 50, PROGRESSIVE };
case VHD_VIDEOSTD_4096x2160p_60Hz: return { 4096, 2160, 60, PROGRESSIVE };
// clang-format on
default:
return {};
};
}
// compat with old SDK
static ULONG
VHD_GetVideoCharacteristics(unsigned mode, ULONG *Width, ULONG *Height,
BOOL32 *Interlaced, ULONG *Framerate)
{
const struct deltacast_mode_info info = deltacast_get_frame_mode(mode);
if (info.width == 0) {
return VHDERR_NOTFOUND;
}
*Width = info.width;
*Height = info.height;
*Interlaced = info.interlacing == UPPER_FIELD_FIRST ? true : false;
*Framerate = info.fps;
return VHDERR_NOERROR;
}
#endif // not defined VHD_MIN_6_00
/**
* @brief returns DELTACAST mode metadata
* @param mode valid VHD_VIDEOSTANDARD item
* @param want_1001 American clock system to be used
* @return the mode information, {} if mode not found (can happen eg. if mode
* not handled by deltacast_get_frame_mode() asked, eg. 8K modes)
*/
struct deltacast_frame_mode_t
deltacast_get_mode_info(unsigned mode, bool want_1001)
{
// do not return "European" NTSC
if (mode == VHD_VIDEOSTD_S259M_NTSC && !want_1001) {
return {};
}
ULONG Width = 0;
ULONG Height = 0;
BOOL32 Interlaced = false;
ULONG Framerate = 0;
ULONG Result = VHD_GetVideoCharacteristics(
(VHD_VIDEOSTANDARD) mode, &Width, &Height, &Interlaced, &Framerate);
if (Result != VHDERR_NOERROR) {
return {};
}
if (want_1001 && (Framerate == 25 || Framerate == 50)) {
return {};
}
double fps = Framerate;
if (want_1001) {
fps = fps * 1000. / 1001.;
}
return { .width = Width,
.height = Height,
.fps = fps,
.interlacing = Interlaced ? UPPER_FIELD_FIRST : PROGRESSIVE };
}
/**
* @brief returns DELTACAST mode name
* @copydetails deltacast_get_mode_info
* @sa VHD_VIDEOSTANDARD_ToPrettyString (but doesn't alter for 1001 modes)
*/
const char *
deltacast_get_mode_name(unsigned mode, bool want_1001)
{
struct deltacast_frame_mode_t info =
deltacast_get_mode_info(mode, want_1001);
if (info.width == 0) {
return nullptr;
}
if (mode == VHD_VIDEOSTD_S259M_PAL) {
return "SMPTE 259M PAL";
}
if (mode == VHD_VIDEOSTD_S259M_NTSC) {
return "SMPTE 259M NTSC";
}
if (info.interlacing == UPPER_FIELD_FIRST) {
info.interlacing = INTERLACED_MERGED;
info.fps *= 2;
}
thread_local char buf[128];
if (info.height <= 1080) {
int std = 274;
if (info.width == 2048) {
std = 2048;
} else if (info.height == 720) {
std = 296;
}
snprintf_ch(buf, "SMPTE %dM %ux%u%s %.4g Hz", std, info.width,
info.height,
get_interlacing_suffix(info.interlacing), info.fps);
} else {
snprintf_ch(buf, "%ux%u %.4g Hz", info.width, info.height,
info.fps);
}
return buf;
}
// PrintVideoStandardInfo
void
delta_print_intefrace_info(ULONG Interface)
{
#ifdef HAVE_VHD_STRING
printf("\nIncoming interface : %s\n",
VHD_INTERFACE_ToString((VHD_INTERFACE) Interface));
#else
(void) Interface;
#endif
}
// SingleToQuadLinksInterface
void
delta_single_to_quad_links_interface(ULONG RXStatus, ULONG *pInterface,
ULONG *pVideoStandard)
{
switch (*pVideoStandard)
{
case VHD_VIDEOSTD_S274M_1080p_24Hz: *pVideoStandard = VHD_VIDEOSTD_3840x2160p_24Hz;
*pInterface = VHD_INTERFACE_4XHD_QUADRANT; break;
case VHD_VIDEOSTD_S274M_1080p_25Hz: *pVideoStandard = VHD_VIDEOSTD_3840x2160p_25Hz;
*pInterface = VHD_INTERFACE_4XHD_QUADRANT; break;
case VHD_VIDEOSTD_S274M_1080p_30Hz: *pVideoStandard = VHD_VIDEOSTD_3840x2160p_30Hz;
*pInterface = VHD_INTERFACE_4XHD_QUADRANT; break;
#if defined VHD_MIN_6_19
case VHD_VIDEOSTD_S274M_1080psf_24Hz: *pVideoStandard = VHD_VIDEOSTD_3840x2160psf_24Hz;
*pInterface = VHD_INTERFACE_4XHD_QUADRANT; break;
case VHD_VIDEOSTD_S274M_1080psf_25Hz: *pVideoStandard = VHD_VIDEOSTD_3840x2160psf_25Hz;
*pInterface = VHD_INTERFACE_4XHD_QUADRANT; break;
case VHD_VIDEOSTD_S274M_1080psf_30Hz: *pVideoStandard = VHD_VIDEOSTD_3840x2160psf_30Hz;
*pInterface = VHD_INTERFACE_4XHD_QUADRANT; break;
#endif
case VHD_VIDEOSTD_S274M_1080p_50Hz: *pVideoStandard = VHD_VIDEOSTD_3840x2160p_50Hz;
if (RXStatus&VHD_SDI_RXSTS_LEVELB_3G)
*pInterface = VHD_INTERFACE_4X3G_B_DL_QUADRANT;
else
*pInterface = VHD_INTERFACE_4X3G_A_QUADRANT;
break;
case VHD_VIDEOSTD_S274M_1080p_60Hz: *pVideoStandard = VHD_VIDEOSTD_3840x2160p_60Hz;
if (RXStatus&VHD_SDI_RXSTS_LEVELB_3G)
*pInterface = VHD_INTERFACE_4X3G_B_DL_QUADRANT;
else
*pInterface = VHD_INTERFACE_4X3G_A_QUADRANT;
break;
case VHD_VIDEOSTD_S2048M_2048p_24Hz: *pVideoStandard = VHD_VIDEOSTD_4096x2160p_24Hz;
*pInterface = VHD_INTERFACE_4XHD_QUADRANT; break;
case VHD_VIDEOSTD_S2048M_2048p_25Hz: *pVideoStandard = VHD_VIDEOSTD_4096x2160p_25Hz;
*pInterface = VHD_INTERFACE_4XHD_QUADRANT; break;
case VHD_VIDEOSTD_S2048M_2048p_30Hz: *pVideoStandard = VHD_VIDEOSTD_4096x2160p_30Hz;
*pInterface = VHD_INTERFACE_4XHD_QUADRANT; break;
#if defined VHD_MIN_6_19
case VHD_VIDEOSTD_S2048M_2048psf_24Hz: *pVideoStandard = VHD_VIDEOSTD_4096x2160psf_24Hz;
*pInterface = VHD_INTERFACE_4XHD_QUADRANT; break;
case VHD_VIDEOSTD_S2048M_2048psf_25Hz: *pVideoStandard = VHD_VIDEOSTD_4096x2160psf_25Hz;
*pInterface = VHD_INTERFACE_4XHD_QUADRANT; break;
case VHD_VIDEOSTD_S2048M_2048psf_30Hz: *pVideoStandard = VHD_VIDEOSTD_4096x2160psf_30Hz;
*pInterface = VHD_INTERFACE_4XHD_QUADRANT; break;
#endif
case VHD_VIDEOSTD_S2048M_2048p_48Hz: *pVideoStandard = VHD_VIDEOSTD_4096x2160p_48Hz;
if (RXStatus&VHD_SDI_RXSTS_LEVELB_3G)
*pInterface = VHD_INTERFACE_4X3G_B_DL_QUADRANT;
else
*pInterface = VHD_INTERFACE_4X3G_A_QUADRANT;
break;
case VHD_VIDEOSTD_S2048M_2048p_50Hz: *pVideoStandard = VHD_VIDEOSTD_4096x2160p_50Hz;
if (RXStatus&VHD_SDI_RXSTS_LEVELB_3G)
*pInterface = VHD_INTERFACE_4X3G_B_DL_QUADRANT;
else
*pInterface = VHD_INTERFACE_4X3G_A_QUADRANT;
break;
case VHD_VIDEOSTD_S2048M_2048p_60Hz: *pVideoStandard = VHD_VIDEOSTD_4096x2160p_60Hz;
if (RXStatus&VHD_SDI_RXSTS_LEVELB_3G)
*pInterface = VHD_INTERFACE_4X3G_B_DL_QUADRANT;
else
*pInterface = VHD_INTERFACE_4X3G_A_QUADRANT;
break;
#if defined VHD_MIN_6_19
case VHD_VIDEOSTD_3840x2160p_24Hz:
*pVideoStandard = VHD_VIDEOSTD_7680x4320p_24Hz;
*pInterface = VHD_INTERFACE_4X6G_2081_10_QUADRANT;
break;
case VHD_VIDEOSTD_3840x2160p_25Hz:
*pVideoStandard = VHD_VIDEOSTD_7680x4320p_25Hz;
*pInterface = VHD_INTERFACE_4X6G_2081_10_QUADRANT;
break;
case VHD_VIDEOSTD_3840x2160p_30Hz:
*pVideoStandard = VHD_VIDEOSTD_7680x4320p_30Hz;
*pInterface = VHD_INTERFACE_4X6G_2081_10_QUADRANT;
break;
case VHD_VIDEOSTD_3840x2160p_50Hz:
*pVideoStandard = VHD_VIDEOSTD_7680x4320p_50Hz;
*pInterface = VHD_INTERFACE_4X12G_2082_10_QUADRANT;
break;
case VHD_VIDEOSTD_3840x2160p_60Hz:
*pVideoStandard = VHD_VIDEOSTD_7680x4320p_60Hz;
*pInterface = VHD_INTERFACE_4X12G_2082_10_QUADRANT;
break;
case VHD_VIDEOSTD_4096x2160p_24Hz:
*pVideoStandard = VHD_VIDEOSTD_8192x4320p_24Hz;
*pInterface = VHD_INTERFACE_4X6G_2081_10_QUADRANT;
break;
case VHD_VIDEOSTD_4096x2160p_25Hz:
*pVideoStandard = VHD_VIDEOSTD_8192x4320p_25Hz;
*pInterface = VHD_INTERFACE_4X6G_2081_10_QUADRANT;
break;
case VHD_VIDEOSTD_4096x2160p_30Hz:
*pVideoStandard = VHD_VIDEOSTD_8192x4320p_30Hz;
*pInterface = VHD_INTERFACE_4X6G_2081_10_QUADRANT;
break;
case VHD_VIDEOSTD_4096x2160p_48Hz:
*pVideoStandard = VHD_VIDEOSTD_8192x4320p_48Hz;
*pInterface = VHD_INTERFACE_4X12G_2082_10_QUADRANT;
break;
case VHD_VIDEOSTD_4096x2160p_50Hz:
*pVideoStandard = VHD_VIDEOSTD_8192x4320p_50Hz;
*pInterface = VHD_INTERFACE_4X12G_2082_10_QUADRANT;
break;
case VHD_VIDEOSTD_4096x2160p_60Hz:
*pVideoStandard = VHD_VIDEOSTD_8192x4320p_60Hz;
*pInterface = VHD_INTERFACE_4X12G_2082_10_QUADRANT;
break;
#endif
}
}
const char *
delta_get_board_type_name(ULONG BoardType)
{
#ifdef HAVE_VHD_STRING
thread_local char buf[128];
snprintf_ch(buf, "%s type",
VHD_BOARDTYPE_ToPrettyString((VHD_BOARDTYPE) BoardType));
return buf;
#else
static const std::unordered_map<ULONG, std::string> board_type_map = {
{ VHD_BOARDTYPE_HD, "HD board type" },
{ VHD_BOARDTYPE_HDKEY, "HD key board type" },
{ VHD_BOARDTYPE_SD, "SD board type" },
{ VHD_BOARDTYPE_SDKEY, "SD key board type" },
{ VHD_BOARDTYPE_DVI, "DVI board type" },
{ VHD_BOARDTYPE_CODEC, "CODEC board type" },
{ VHD_BOARDTYPE_3G, "3G board type" },
{ VHD_BOARDTYPE_3GKEY, "3G key board type" },
{ VHD_BOARDTYPE_HDMI, "HDMI board type" },
};
auto it = board_type_map.find(BoardType);
if (it != board_type_map.end()) {
return it->second.c_str();
}
return "Unknown DELTACAST type";
#endif
}
bool
delta_chn_type_is_sdi(ULONG ChnType)
{
return ChnType == VHD_CHNTYPE_HDSDI || ChnType == VHD_CHNTYPE_3GSDI ||
ChnType == VHD_CHNTYPE_3GSDI_ASI ||
ChnType == VHD_CHNTYPE_12GSDI ||
ChnType == VHD_CHNTYPE_12GSDI_ASI;
}
/**
* @param action "sent" or "received"
* @param is_final_summary print always even if no change in dropped frames
* since last time
*/
void
delta_print_slot_stats(HANDLE StreamHandle, ULONG *SlotsDroppedLast,
const char *action)
{
ULONG SlotsCount = 0, SlotsDropped = 0;
/* Print some statistics */
VHD_GetStreamProperty(StreamHandle, VHD_CORE_SP_SLOTS_DROPPED,
&SlotsDropped);
if (SlotsDropped == *SlotsDroppedLast) {
return;
}
VHD_GetStreamProperty(StreamHandle, VHD_CORE_SP_SLOTS_COUNT,
&SlotsCount);
MSG(WARNING, "%" PRIu_ULONG " frames %s (%" PRIu_ULONG " dropped)\n",
SlotsCount, action, SlotsDropped);
*SlotsDroppedLast = SlotsDropped;
}
bool
delta_board_type_is_dv(VHD_BOARDTYPE BoardType,
[[maybe_unused]] bool include_mixed)
{
switch (BoardType) {
case VHD_BOARDTYPE_HD: return false;
case VHD_BOARDTYPE_HDKEY: return false;
case VHD_BOARDTYPE_SD: return false;
case VHD_BOARDTYPE_SDKEY: return false;
case VHD_BOARDTYPE_DVI: return true;
case VHD_BOARDTYPE_CODEC: return include_mixed; // _MIXEDINTERFACE
case VHD_BOARDTYPE_3G: return false;
case VHD_BOARDTYPE_3GKEY: return false;
case VHD_BOARDTYPE_HDMI: return true;
case VHD_BOARDTYPE_FLEX: return include_mixed;
case VHD_BOARDTYPE_ASI: return false;
#if defined VHD_MIN_6_00
case VHD_BOARDTYPE_IP: return false;
#endif
#if defined VHD_MIN_6_13
case VHD_BOARDTYPE_HDMI20: return true;
case VHD_BOARDTYPE_FLEX_DP: return include_mixed;
case VHD_BOARDTYPE_FLEX_SDI: return false;
case VHD_BOARDTYPE_12G: return false;
case VHD_BOARDTYPE_FLEX_HMI: return include_mixed;
#endif
case NB_VHD_BOARDTYPES: return false;
}
return false;
}