48#pragma GCC diagnostic push
49#pragma GCC diagnostic ignored "-Wcast-qual"
50#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
51#pragma GCC diagnostic ignored "-Woverloaded-virtual"
53#include <ntv2devicescanner.h>
54#include <ajantv2/includes/ntv2utils.h>
55#include <ajabase/system/debug.h>
57#pragma GCC diagnostic pop
66#include <condition_variable>
80#include <unordered_map>
93#define DEFAULT_MAX_FRAME_QUEUE_LEN 1
94#define MOD_NAME "[AJA display] "
103#define SAMPLE_RATE 48000
106#define LINK_SPEC extern "C" __declspec(dllexport)
108#define LINK_SPEC static
112using std::chrono::duration;
113using std::chrono::steady_clock;
114using std::condition_variable;
118using std::lock_guard;
121using std::ostringstream;
123using std::runtime_error;
126using std::unique_lock;
127using std::unique_ptr;
128using std::unordered_map;
131#define CHECK_EX(cmd, msg, action_failed) do { bool ret = cmd; if (!ret) {\
132 LOG(LOG_LEVEL_WARNING) << MOD_NAME << (msg) << "\n";\
136#define NOOP ((void)0)
137#define CHECK(cmd) CHECK_EX(cmd, #cmd " failed", NOOP)
167 static const ULWord
app = AJA_FOURCC (
'U',
'L',
'G',
'R');
193 steady_clock::time_point
mT0 = steady_clock::now();
211 : mConf(conf), max_frame_queue_len(conf.bufLen), mOutputChannel(conf.outputChannel)
213 if (!CNTV2DeviceScanner::GetFirstDeviceFromArgument(conf.
deviceId,
mDevice)) {
214 throw runtime_error(string(
"Device '") + conf.deviceId +
"' not found!");
217 if (!
mDevice.IsDeviceReady(
false)) {
218 throw runtime_error(string(
"Device '") + conf.deviceId +
"' not ready!");
222 CHECK(mDevice.ClearRouting());
227 bool canDoMultiChannel = NTV2DeviceCanDoMultiFormat(
mDeviceID);
228 if (!canDoMultiChannel) {
237 if (!
mDevice.AcquireStreamForApplication(
app,
static_cast <uint32_t
> (getpid()))) {
238 throw runtime_error(
"Device busy!\n");
247 }
else if (::NTV2DeviceCanDoMultiFormat(
mDeviceID)) {
254 "probably channel 1. Consider passing \"channel\" option (see help).\n";
266 oss <<
"## ERROR: Cannot use channel '" <<
mOutputChannel+1 <<
"' -- device only supports channel 1"
267 << (::NTV2DeviceGetNumFrameStores (
mDeviceID) > 1 ? string (
" thru ") + string (1, uint8_t (::NTV2DeviceGetNumFrameStores (
mDeviceID)+
'0')) :
"");
268 throw runtime_error(oss.str());
279 CHECK(
mDevice.ReleaseStreamForApplication (
app,
static_cast <uint32_t
>(getpid())));
290 if (AJA_FAILURE(
status)) {
292 oss <<
"Unable to initialize video: " << ::AJAStatusToString(
status);
293 throw runtime_error(oss.str());
299 for (
unsigned int i = 0; i <
desc.tile_count; ++i) {
300 NTV2Channel chan = (NTV2Channel)((
unsigned int)
mOutputChannel + i);
312 return AJA_STATUS_SUCCESS;
316 cerr <<
"## ERROR: This device cannot handle '" << ::NTV2VideoFormatToString (
mVideoFormat) <<
"'" << endl;
317 return AJA_STATUS_UNSUPPORTED;
321 for (
unsigned int i = 0; i <
desc.tile_count; ++i) {
322 NTV2Channel chan = (NTV2Channel)((
unsigned int)
mOutputChannel + i);
326 <<
" for output " << chan <<
"\n";
327 return AJA_STATUS_FAIL;
333 for (
unsigned int i = 0; i <
desc.tile_count; ++i) {
337 "overriding output color spec preference.\n";
355 return AJA_STATUS_FAIL;
358 for (
unsigned int i = 0; i <
desc.tile_count; ++i) {
359 NTV2Channel chan = (NTV2Channel)((
unsigned int)
mOutputChannel + i);
364 return AJA_STATUS_FAIL;
367 if(
mDeviceID == DEVICE_ID_KONAIP_1RX_1TX_2110 ||
373 for (
unsigned int i = 0; i <
desc.tile_count; ++i) {
374 NTV2Channel chan = (NTV2Channel)((
unsigned int)
mOutputChannel + i);
375 if (!
mDevice.EnableChannel(chan)) {
378 return AJA_STATUS_FAIL;
397 NTV2LutType lutType = NTV2_LUTRGBRangeFull_SMPTE;
401 NTV2DoubleArray table(1024);
415 "HDMI Sample Struct",
NOOP);
418 "HDMI Color Space",
NOOP);
423 for (
unsigned int i = 0; i <
desc.tile_count; ++i) {
424 NTV2Channel chan = (NTV2Channel)((
unsigned int)
mOutputChannel + i);
434 return AJA_STATUS_SUCCESS;
441 return AJA_STATUS_SUCCESS;
461 CHECK(
mDevice.SetHDMIOutAudioChannels(NTV2_HDMIAudio8Channels));
462 CHECK_EX(
mDevice.SetHDMIOutAudioSource8Channel(NTV2_AudioChannel1_8,
mAudioSystem),
"Unable to set HDMI output audio system!",
return AJA_STATUS_FAIL);
484 return AJA_STATUS_SUCCESS;
488const unordered_map<NTV2Channel, NTV2OutputCrosspointID, hash<int>>
chanToLutSrc = {
489 {NTV2_CHANNEL1, NTV2_XptLUT1RGB},
490 {NTV2_CHANNEL2, NTV2_XptLUT2RGB},
491 {NTV2_CHANNEL3, NTV2_XptLUT3Out},
492 {NTV2_CHANNEL4, NTV2_XptLUT4Out},
493 {NTV2_CHANNEL5, NTV2_XptLUT5Out},
494 {NTV2_CHANNEL6, NTV2_XptLUT6Out},
495 {NTV2_CHANNEL7, NTV2_XptLUT7Out},
496 {NTV2_CHANNEL8, NTV2_XptLUT8Out}
505 const NTV2Standard outputStandard (::GetNTV2StandardFromVideoFormat (
mVideoFormat));
506 const UWord numSDIOutputs (::NTV2DeviceGetNumVideoOutputs (
mDeviceID));
511 for (
unsigned int i = 0; i <
desc.tile_count; ++i) {
513 NTV2Channel chan = (NTV2Channel)((
unsigned int)
mOutputChannel + i);
514 NTV2OutputCrosspointID cscVidOutXpt (::GetCSCOutputXptFromChannel (chan,
false, !fbIsRGB));
515 NTV2OutputCrosspointID fsVidOutXpt (::GetFrameBufferOutputXptFromChannel (chan, fbIsRGB,
false));
517 CHECK_EX(
mDevice.Connect(::GetCSCInputXptFromChannel (chan,
false), fsVidOutXpt),
518 "Connnect to CSC",
NOOP);
522 if (::NTV2DeviceHasBiDirectionalSDI (
mDeviceID)) {
526 CHECK(
mDevice.SetSDIOutputStandard(chan, outputStandard));
529 CHECK(
mDevice.Connect (::GetDLOutInputXptFromChannel(chan), fbIsRGB ? ::GetFrameBufferOutputXptFromChannel(chan, NTV2_IS_FBF_RGB(
mPixelFormat)) : cscVidOutXpt));
534 "Connect to CSC",
NOOP);
540 auto lutInXpt = (NTV2InputCrosspointID) ((
unsigned int) NTV2_XptLUT1Input + (
unsigned int) chan);
543 "Connect from LUT",
NOOP);
549 "Connect from CSC",
NOOP);
554 "Routing for %s may be incorrect. ",
555 NTV2OutputDestinationToString(
559 "Connect from CSC or frame store",
NOOP);
564 const NTV2OutputCrosspointID cscVidOutXpt(::GetCSCOutputXptFromChannel (
mOutputChannel,
false, !fbIsRGB));
565 const NTV2OutputCrosspointID fsVidOutXpt(::GetFrameBufferOutputXptFromChannel (
mOutputChannel, fbIsRGB,
false));
566 const UWord numFrameStores(::NTV2DeviceGetNumFrameStores(
mDeviceID));
571 for (NTV2Channel chan(NTV2_CHANNEL1); ULWord(chan) < numSDIOutputs; chan = NTV2Channel(chan+1))
575 if (::NTV2DeviceHasBiDirectionalSDI (
mDeviceID))
578 CHECK(
mDevice.Connect (::GetSDIOutputInputXpt (chan,
false), fbIsRGB ? cscVidOutXpt : fsVidOutXpt));
579 CHECK(
mDevice.SetSDIOutputStandard (chan, outputStandard));
583 if (::NTV2DeviceCanDoWidget (
mDeviceID, NTV2_WgtAnalogOut1))
584 CHECK(
mDevice.Connect (::GetOutputDestInputXpt (NTV2_OUTPUTDESTINATION_ANALOG), fbIsRGB ? cscVidOutXpt : fsVidOutXpt));
587 if (::NTV2DeviceCanDoWidget (
mDeviceID, NTV2_WgtHDMIOut1)
588 || ::NTV2DeviceCanDoWidget (
mDeviceID, NTV2_WgtHDMIOut1v2)
589 || ::NTV2DeviceCanDoWidget (
mDeviceID, NTV2_WgtHDMIOut1v3)
590 || ::NTV2DeviceCanDoWidget (
mDeviceID, NTV2_WgtHDMIOut1v4))
591 CHECK(
mDevice.Connect (::GetOutputDestInputXpt (NTV2_OUTPUTDESTINATION_HDMI), fbIsRGB ? fsVidOutXpt : cscVidOutXpt));
594 for (
unsigned int i = 1; i <
desc.tile_count; ++i) {
596 const NTV2OutputCrosspointID fsVidOutXpt(::GetFrameBufferOutputXptFromChannel (chan, fbIsRGB,
false));
598 CHECK(
mDevice.Connect (::GetCSCInputXptFromChannel (chan,
false), fsVidOutXpt));
599 CHECK(
mDevice.Connect (::GetSDIOutputInputXpt (chan,
false), fbIsRGB ? cscVidOutXpt : fsVidOutXpt));
615 for (
unsigned int i = 0; i <
desc.tile_count; ++i) {
616 NTV2Channel chan = (NTV2Channel)((
unsigned int)
mOutputChannel + i);
640 uint32_t readBackOut;
666 if (latency_ms > 135) {
693 if (
frame ==
nullptr) {
720 auto now = steady_clock::now();
724 << seconds <<
" seconds = " << mFrames / seconds <<
" FPS\n";
734 <<
"[[:buffers=<b>][:channel=<ch>][:clear-routing][:"
735 "connection=<c>][:device=<d>][:[no-]multi-channel][:"
736 "novsync][:no-setup[-route]][:RGB|:YUV][:{smpte|"
737 "full}-range]|:help] [-r embedded]")
742 "\t\tbut may also increase latency (when VBlank is enabled)\n";
744 col() <<
SBOLD(
"\tclear-routing\n") <<
745 "\t\tremove all existing signal paths for device\n";
749 NTV2OutputDestination dest = NTV2OutputDestination();
750 while (dest != NTV2_OUTPUTDESTINATION_INVALID) {
754 col() << NTV2OutputDestinationToString(dest,
true);
757 dest = (NTV2OutputDestination) ((
int) dest + 1);
762 "\t\tchannel number to use (indexed from 1). Doesn't need to be set for SDI, useful for HDMI (capture and display should have different channel numbers if both used, also other than 1 if SDI1 is in use, see \"-t aja:help\" to see number of available channels).\n";
765 "\t\tdevice identifier (number or name)\n";
767 col() <<
SBOLD(
"\t[no-]multi-channel\n") <<
768 "\t\tdo (not) treat the device as a multi-channel\n";
770 col() <<
SBOLD(
"\tno-setup[-route]\n") <<
771 "\t\tdo not setup anything/routing (user must set it eg. in Cables app)\n";
774 "\t\tdisable sync on VBlank (may improve latency at the expense of tearing)\n";
777 "\t\tforce SDI output to be RGB or YCbCr, otherwise UG keeps the colorspace\n";
779 col() <<
SBOLD(
"\tsmpte-|full-range\n") <<
780 "\t\tuse either SMPTE (default) or full RGB range\n";
782 col() <<
SBOLD(
"\t-r embedded\n") <<
783 "\t\treceive also audio and embed it to SDI\n";
786 col() <<
"Available devices:\n";
788 CNTV2DeviceScanner deviceScanner;
789 for (
unsigned int i = 0; i < deviceScanner.GetNumDevices (); i++) {
791 if (!CNTV2DeviceScanner::GetDeviceAtIndex(i, device)) {
792 MSG(WARNING,
"Cannot get device at index #%u!\n", i);
797 <<
SBOLD(device.GetDisplayName());
801 if (deviceScanner.GetNumDevices() == 0) {
802 col() <<
SRED(
"\tno devices found\n");
809 NTV2FrameRate result = NTV2_FRAMERATE_UNKNOWN;
810 double best = 1000.0;
812 for(
int i = 0; i < NTV2_NUM_FRAMERATES; i++)
814 auto candidate =
static_cast<NTV2FrameRate
>(i);
815 double distance = fabs(GetFramesPerSecond(candidate) - fps);
834 CNTV2DeviceScanner deviceScanner;
835 *
count = deviceScanner.GetNumDevices();
836 *available_cards =
static_cast<struct
device_info *
>(calloc(deviceScanner.GetNumDevices(),
sizeof(
struct device_info)));
837 for (
unsigned int i = 0; i < deviceScanner.GetNumDevices (); i++) {
838 snprintf((*available_cards)[i].dev,
sizeof (*available_cards)[i].dev,
":device=%d", i);
839 snprintf((*available_cards)[i].extra,
sizeof (*available_cards)[i].extra,
"\"embeddedAudioAvailable\":\"t\"");
841 if (!CNTV2DeviceScanner::GetDeviceAtIndex(i, device)) {
842 MSG(WARNING,
"Cannot get device at index #%u!\n", i);
845 strncpy((*available_cards)[i].
name,
846 device.GetDisplayName().c_str(),
847 sizeof(*available_cards)[0].name - 1);
848 (*available_cards)[i].repeatable =
false;
856 if ((
fmt >= NTV2_FORMAT_FIRST_4K_DEF_FORMAT &&
fmt < NTV2_FORMAT_END_4K_DEF_FORMATS)
857 || (
fmt >= NTV2_FORMAT_FIRST_4K_DEF_FORMAT2 &&
fmt < NTV2_FORMAT_END_4K_DEF_FORMATS2)
858 || (
fmt >= NTV2_FORMAT_FIRST_UHD2_DEF_FORMAT &&
fmt < NTV2_FORMAT_END_UHD2_DEF_FORMATS)
859 || (
fmt >= NTV2_FORMAT_FIRST_UHD2_FULL_DEF_FORMAT &&
fmt < NTV2_FORMAT_END_UHD2_FULL_DEF_FORMATS)) {
870 for (NTV2VideoFormat
fmt(NTV2_FORMAT_FIRST_HIGH_DEF_FORMAT);
fmt < NTV2_MAX_NUM_VIDEO_FORMATS;
fmt = NTV2VideoFormat(
fmt + 1)) {
874 if (inFrameRate == ::GetNTV2FrameRateFromVideoFormat(
fmt))
875 if (inHeightLines == ::GetDisplayHeight(
fmt))
876 if (inWidthPixels == ::GetDisplayWidth(
fmt))
877 if (inIsInterlaced == !::IsProgressiveTransport(
fmt) && !IsPSF(
fmt))
878 if (NTV2_VIDEO_FORMAT_IS_B(
fmt) == inIsLevelB)
881 return NTV2_FORMAT_UNKNOWN;
903 if (s->mVideoFormat == NTV2_FORMAT_UNKNOWN || !::NTV2DeviceCanDoVideoFormat (s->mDeviceID, s->mVideoFormat)) {
907 if (s->mVideoFormat == NTV2_FORMAT_UNKNOWN) {
915 s->mPixelFormat = NTV2_FBF_INVALID;
918 s->mPixelFormat = c.first;
921 assert(s->mPixelFormat != NTV2_FBF_INVALID);
927 }
catch (runtime_error &e) {
933 s->worker = thread(&ugaja::display::process_frames, s);
940 struct ugaja::display::configuration conf;
941 auto tmp =
static_cast<char *
>(alloca(strlen(
fmt) + 1));
945 while ((
item = strtok(tmp,
":")) !=
nullptr) {
946 if (strcmp(
"help",
item) == 0) {
947 ugaja::display::show_help();
949 }
else if (strstr(
item,
"buffers=") ==
item) {
950 conf.bufLen = atoi(
item + strlen(
"buffers="));
951 if (conf.bufLen <= 0) {
955 }
else if (strcmp(
"clear-routing",
item) == 0) {
956 conf.clearRouting =
true;
957 }
else if (strstr(
item,
"connection=") !=
nullptr) {
958 string connection =
item + strlen(
"connection=");
959 NTV2OutputDestination dest = NTV2OutputDestination();
960 while (dest != NTV2_OUTPUTDESTINATION_INVALID) {
961 if (NTV2OutputDestinationToString(dest,
true) == connection) {
962 conf.outputDestination = dest;
967 dest = (NTV2OutputDestination) ((
int) dest + 1);
969 if (dest == NTV2_OUTPUTDESTINATION_INVALID) {
973 }
else if (strstr(
item,
"channel=") !=
nullptr) {
974 conf.outputChannel = (NTV2Channel) (atoi(
item + strlen(
"channel=")) - 1);
975 }
else if (strstr(
item,
"device=") !=
nullptr) {
976 conf.deviceId =
item + strlen(
"device=");
977 }
else if (strstr(
item,
"multi-channel") !=
nullptr) {
978 conf.doMultiChannel = strstr(
item,
"no-") ==
nullptr;
979 }
else if (strstr(
item,
"no-setup") ==
item) {
980 conf.setupRoute =
false;
981 if (strcmp(
item,
"no-setup") == 0) {
982 conf.setupAll =
false;
984 }
else if (strstr(
item,
"novsync") ==
item) {
986 }
else if (strcasecmp(
item,
"RGB") == 0 || strcasecmp(
item,
"YUV") == 0) {
987 conf.forceOutputColorSpace = strcasecmp(
item,
"RGB") == 0 ?
RGB :
UYVY;
988 }
else if (strcasecmp(
item,
"smpte-range") == 0 || strcasecmp(
item,
"full-range") == 0) {
989 conf.smpteRange = strcasecmp(
item,
"smpte-range") == 0;
1001 }
catch (runtime_error &e) {
1049 if (::NTV2DeviceCanDoFrameBufferFormat(s->mDeviceID, c.first)) {
1068 desc->ch_count = ::NTV2DeviceGetMaxAudioChannels (s->mDeviceID);
1075 if (*
len >=
sizeof(
int)) {
1092 lock_guard<mutex> lk(s->mAudioLock);
1099 memcpy(s->mAudioBuffer.get() + s->mAudioLen,
frame->data,
len);
1100 s->mAudioLen +=
len;
1109 AJAStatus
status = s->SetUpAudio();
1110 if (AJA_FAILURE(
status)) {
1112 oss <<
"Unable to initialize audio: " << AJAStatusToString(
status);
void print_aja_device_details(CNTV2Card *device)
void vc_copylineR12LtoR12A(unsigned char *__restrict dst, const unsigned char *__restrict src, int dstlen, int rshift, int gshift, int bshift)
#define NTV2_AUDIOSIZE_MAX
struct video_frame long long nonblock
struct video_frame * frame
const char unsigned int flags
int void(** deleter)(void *))
void vf_free(struct video_frame *buf)
Frees video_frame structure.
struct video_frame * vf_alloc_desc_data(struct video_desc desc)
This file contains definition of some common audio types.
void bug_msg(int level, const char *format,...)
prints the error with the advice to report the bug
#define LOG_LEVEL_NOTICE
information that may be interesting
#define LOG_LEVEL_INFO
normal reporting
#define LOG_LEVEL_ERROR
general errors
#define LOG_LEVEL_WARNING
less severe errors
steady_clock::duration duration
#define VIDEO_DISPLAY_ABI_VERSION
#define DISPLAY_FLAG_AUDIO_ANY
#define DISPLAY_NO_GENERIC_FPS_INDICATOR
@ DISPLAY_PROPERTY_VIDEO_MODE
requested video mode - int (one of display_prop_vid_mode)
@ DISPLAY_PROPERTY_CODECS
list of natively supported codecs - codec_t[]
@ DISPLAY_PROPERTY_AUDIO_FORMAT
@ DISPLAY_PROPERTY_VIDEO_SEPARATE_TILES
framebuffer consists of separate tiles
@ LIBRARY_CLASS_VIDEO_DISPLAY
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
const unordered_map< NTV2Channel, NTV2OutputCrosspointID, hash< int > > chanToLutSrc
static const std::map< NTV2FrameBufferFormat, codec_t > codec_map
struct used to comunicate with audio capture/playback device
char * data
Raw tile data. Pointer must be at least 4B aligned.
unsigned int data_len
length of the data
NTV2OutputDestination outputDestination
NTV2Channel outputChannel
if invalid, select according to output destination
codec_t forceOutputColorSpace
force output color space for SDI - RGB or UYVY, VIDEO_CODEC_NONE for default
unique_ptr< char[]> mAudioBuffer
NTV2AudioSystem mAudioSystem
unsigned int max_frame_queue_len
static NTV2FrameRate getFrameRate(double fps)
const struct ultragrid::aja::display::configuration mConf
uint32_t mAudioOutWrapAddress
uint32_t mCurrentOutFrame
queue< struct video_frame * > frames
NTV2VideoFormat mVideoFormat
bool Putf(struct video_frame *frame, long long flags)
display(struct configuration &configuration)
steady_clock::time_point mT0
uint32_t mFramesProcessed
NTV2Channel mOutputChannel
NTV2EveryFrameTaskMode mSavedTaskMode
Used to restore the prior task mode.
NTV2FrameBufferFormat mPixelFormat
condition_variable frame_ready
uint32_t mFramesDiscarded
uint32_t mAudioOutLastAddress
bool mDoMultiChannel
Use multi-format.
enum interlacing_t interlacing
unsigned int width
width of every tile (!)
unsigned int height
height of every tile (!)
Struct video_frame represents a video frame and contains video description.
struct tile tiles[FLEXIBLE_ARRAY_MEMBER]
@ INTERLACED_MERGED
Columngs of both fields are interlaced together.
@ RGB
RGB 8-bit (packed into 24-bit word)
@ UYVY
YCbCr 422 8-bit - Cb Y0 Cr Y1.
@ VIDEO_CODEC_NONE
dummy color spec
@ R12L
RGB 12-bit packed, little-endian.
This is an umbrella header for video functions.
LINK_SPEC bool display_aja_reconfigure(void *state, struct video_desc desc)
static bool display_aja_is_quad_format(NTV2VideoFormat fmt)
LINK_SPEC bool display_aja_putf(void *state, struct video_frame *frame, long long nonblock)
LINK_SPEC void display_aja_done(void *state)
#define DEFAULT_MAX_FRAME_QUEUE_LEN
static NTV2VideoFormat display_aja_get_first_matching_video_format(const NTV2FrameRate inFrameRate, const UWord inHeightLines, const UWord inWidthPixels, const bool inIsInterlaced, const bool inIsLevelB, bool skipQuadFormats)
LINK_SPEC bool display_aja_reconfigure_audio(void *state, int quant_samples, int channels, int sample_rate)
static const struct video_display_info display_aja_info
LINK_SPEC void display_aja_put_audio_frame(void *state, const struct audio_frame *frame)
LINK_SPEC void * display_aja_init(struct module *, const char *fmt, unsigned int flags)
LINK_SPEC struct video_frame * display_aja_getf(void *state)
LINK_SPEC bool display_aja_get_property(void *state, int property, void *val, size_t *len)
#define CHECK_EX(cmd, msg, action_failed)
LINK_SPEC void display_aja_probe(struct device_info **available_cards, int *count, void(**deleter)(void *))