UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
aja.cpp
Go to the documentation of this file.
1
7/*
8 * Copyright (c) 2015-2024 CESNET
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, is permitted provided that the following conditions
13 * are met:
14 *
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 *
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 *
22 * 3. Neither the name of CESNET nor the names of its contributors may be
23 * used to endorse or promote products derived from this software without
24 * specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
28 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
29 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
30 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
31 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
36 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
37 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
45#ifdef HAVE_CONFIG_H
46#include "config.h"
47#endif
48#include "config_msvc.h"
49// aligned_alloc/aligned_free
50#include "config_unix.h"
51#include "config_win32.h"
52
53#include "debug.h"
54#undef UNUSED // collides with same-named macro defined by libajantv2
55
56#include <algorithm>
57#include <chrono>
58#include <condition_variable>
59#include <cstdint> // for uint32_t, uint8_t
60#include <cstdio> // for snprintf, printf
61#include <cstdlib> // for free, atoi, malloc, realloc
62#include <cstring> // for strchr, memcpy, memset, strcmp
63#include <iostream> // for operator<<, basic_ostream, cout
64#include <memory> // for shared_ptr, __shared_ptr_access
65#include <mutex>
66#include <sstream> // for basic_ostringstream
67#include <stdexcept> // for runtime_error
68#include <string>
69#include <thread>
70#include <unordered_map> // for unordered_map, operator!=, _Node...
71#include <utility>
72#ifdef _MSC_VER
73#include <winsock2.h>
74#endif
75
76#include "audio/types.h"
77#include "audio/utils.h"
78#include "host.h"
79#include "lib_common.h"
81#include "video.h"
82#include "video_capture.h"
83
84#include "ajatypes.h"
85#include <ajantv2/includes/ntv2rp188.h>
86#ifdef __GNUC__
87#pragma GCC diagnostic push
88#pragma GCC diagnostic ignored "-Wcast-qual"
89#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
90#pragma GCC diagnostic ignored "-Woverloaded-virtual"
91#endif // defined __GNUC__
92#include "ntv2devicescanner.h"
93#ifdef __GNUC__
94#pragma GCC diagnostic pop
95#endif // defined __GNUC__
96#include "ajabase/common/types.h"
97#include "ajabase/common/videotypes.h"
98#include "ajabase/common/circularbuffer.h"
99#include "ajabase/system/process.h"
100#include "ajabase/system/systemtime.h"
101#include "ajabase/system/thread.h"
102
103#include "ntv2utils.h"
104#include "ntv2devicefeatures.h"
105
106#define NTV2_AUDIOSIZE_MAX (401 * 1024)
107
108#include "aja_common.hpp"
109
110#define MOD_NAME "[AJA cap.] "
111
112namespace ugaja = ultragrid::aja;
113
114#ifdef _MSC_VER
115extern "C" __declspec(dllexport) unsigned int *aja_audio_capture_channels = NULL;
116volatile int log_level = 5;
117#else
119#endif
120
121#ifdef _MSC_VER
122#define LINK_SPEC extern "C" __declspec(dllexport)
123#else
124#define LINK_SPEC static
125#endif
126
127#define CHECK_OK(cmd, msg, action_failed) do { bool ret = cmd; if (!ret) {\
128 LOG(LOG_LEVEL_WARNING) << MOD_NAME << (msg) << "\n";\
129 action_failed;\
130}\
131} while(0)
132#define NOOP ((void)0)
133
134#define CHECK_RET_FAIL(cmd) CHECK_OK(cmd, #cmd " failed", return AJA_STATUS_FAIL)
135#define CHECK(cmd) CHECK_OK(cmd, #cmd " failed", NOOP)
136
137namespace chrono = std::chrono;
138using std::condition_variable;
139using std::min;
140using std::mutex;
141using std::ostringstream;
142using std::runtime_error;
143using std::string;
144using std::shared_ptr;
145using std::thread;
146using std::to_string;
147using std::unique_lock;
148using std::unordered_map;
149
151 void *allocate(size_t size) override {
152 return aligned_malloc(size, AJA_PAGE_SIZE);
153 }
154 void deallocate(void *ptr) override {
155 aligned_free(ptr);
156 }
158 return new aligned_data_allocator(*this);
159 }
160};
161
162static const ULWord app = AJA_FOURCC ('U','L','G','R');
163
165 public:
166 vidcap_state_aja(unordered_map<string, string> const & parameters, int audioFlags);
167 void Init();
168 virtual ~vidcap_state_aja();
169 struct video_frame *grab(struct audio_frame **audio);
170 AJAStatus Run();
171 void Quit();
172 private:
173 unsigned int mDeviceIndex{0};
174 CNTV2Card mDevice;
175 NTV2DeviceID mDeviceID{};
176 NTV2EveryFrameTaskMode mSavedTaskMode{};
177 NTV2Channel mInputChannel{NTV2_CHANNEL_INVALID};
178 NTV2VideoFormat mVideoFormat{NTV2_FORMAT_UNKNOWN};
179 NTV2FrameBufferFormat mPixelFormat{NTV2_FBF_INVALID};
180 NTV2VANCMode mVancMode{};
181 bool mWideVanc{};
182 NTV2InputSource mInputSource{NTV2_INPUTSOURCE_SDI1};
183 NTV2AudioSystem mAudioSystem{NTV2_AUDIOSYSTEM_1};
184 uint32_t mVideoBufferSize{};
185 uint32_t mAudioBufferSize{};
188 shared_ptr<video_frame> mOutputFrame;
189 shared_ptr<uint32_t> mOutputAudioFrame;
192 condition_variable mOutputFrameReady;
193 bool mProgressive{false};
194 chrono::system_clock::time_point mT0{chrono::system_clock::now()};
195 int mFrames{0};
198 NTV2AudioSource mAudioSource{};
199 NTV2TCIndex mTimeCodeSource{};
200 bool mbFixedReference{false};
201 bool mCheckFor4K{false};
203 bool mClearRouting{false};
205 bool mGlobalQuit{false};
206
207 AJAStatus SetupHDMI();
208 AJAStatus SetupVideo();
209 AJAStatus SetupAudio();
210 void SetupHostBuffers();
211 NTV2VideoFormat GetVideoFormatFromInputSource();
212 void EnableInput(NTV2InputSource source);
213 bool IsInput3Gb(const NTV2InputSource inputSource);
214
218 virtual void StartProducerThread (void);
219 virtual void CaptureFrames (void);
220 static void ProducerThreadStatic (vidcap_state_aja * pContext);
221};
222
223vidcap_state_aja::vidcap_state_aja(unordered_map<string, string> const & parameters, int audioFlags)
224{
225 bool colorSpaceSet = false; // explicitly set input color spec
226
227 for (auto const &it : parameters) {
228 if (it.first == "progressive") {
229 mProgressive = true;
230 } else if (it.first == "4K" || it.first == "4k") {
231 mCheckFor4K = true;
232 } else if (it.first == "clear-routing") {
233 mClearRouting = true;
234 } else if (it.first == "device") {
235 mDeviceIndex = stol(it.second, nullptr, 10);
236 } else if (it.first == "channel") {
237 mInputChannel = (NTV2Channel) (atoi(it.second.c_str()) - 1);
238 } else if (it.first == "codec") {
239 if (get_codec_from_name(it.second.c_str()) == VIDEO_CODEC_NONE) {
240 throw string("Unknown codec " + it.second + "!");
241 }
242 mPixelFormat = NTV2_FBF_INVALID;
243 for (auto const &c : ugaja::codec_map) {
244 if (c.second == get_codec_from_name(it.second.c_str())) {
245 mPixelFormat = c.first;
246 break;
247 }
248 }
249 if (mPixelFormat == NTV2_FBF_INVALID) {
250 throw string("Cannoc map " + it.second + " to AJA codec!");
251 }
252 } else if (it.first == "connection") {
253 NTV2InputSource source = NTV2InputSource();
254 while (source != NTV2_INPUTSOURCE_INVALID) {
255 if (NTV2InputSourceToString(source, true) == it.second) {
257 break;
258 }
259 // should be this, but GetNTV2InputSourceForIndex knows only SDIs
260 //source = ::GetNTV2InputSourceForIndex(::GetIndexForNTV2InputSource(source) + 1);
261 source = (NTV2InputSource) ((int) source + 1);
262 }
263 if (source == NTV2_INPUTSOURCE_INVALID) {
264 throw string("Unknown source " + it.second + "!");
265 }
266 } else if (it.first == "format") {
267 NTV2VideoFormat format = NTV2VideoFormat();
268 while (format != NTV2_MAX_NUM_VIDEO_FORMATS) {
269 if (NTV2VideoFormatToString(format) == it.second) {
270 mVideoFormat = format;
271 break;
272 }
273 format = (NTV2VideoFormat) ((int) format + 1);
274 }
275 if (format == NTV2_MAX_NUM_VIDEO_FORMATS) {
276 throw string("Unknown format " + it.second + "!");
277 }
278 } else if (it.first == "RGB") {
279 if (colorSpaceSet && mInputIsRGB != true) {
280 throw string("Both RGB and YUV input specified!") + it.first;
281 }
282 colorSpaceSet = true;
283 mInputIsRGB = true;
284 } else if (it.first == "YUV") {
285 if (colorSpaceSet && mInputIsRGB != false) {
286 throw string("Both RGB and YUV input specified!") + it.first;
287 }
288 colorSpaceSet = true;
289 mInputIsRGB = false;
290 } else {
291 throw string("Unknown option: ") + it.first;
292 }
293 }
294
295 if (!NTV2_INPUT_SOURCE_IS_SDI(mInputSource) && mInputChannel == NTV2_CHANNEL_INVALID) {
296 LOG(LOG_LEVEL_NOTICE) << MOD_NAME "Non-SDI source detected - we will use "
297 "probably channel 1. Consider passing \"channel\" option (see help).\n";
298 }
299
300 if (mPixelFormat == NTV2_FBF_INVALID) {
301 if (colorSpaceSet && mInputIsRGB) {
302 mPixelFormat = NTV2_FBF_ABGR;
303 } else {
304 mPixelFormat = NTV2_FBF_8BIT_YCBCR;
305 }
306 }
307
308 if (!colorSpaceSet) {
309 mInputIsRGB = IsRGBFormat(mPixelFormat);
310 }
311
312 if (audioFlags & VIDCAP_FLAG_AUDIO_EMBEDDED) {
313 // this maps according to mInputSource - EMBEDDED (SDI), HDMI or ANALOG
314 mAudioSource = NTV2InputSourceToAudioSource(mInputSource);
315 } else if (audioFlags & VIDCAP_FLAG_AUDIO_AESEBU) {
316 mAudioSource = NTV2_AUDIO_AES;
317 } else if (audioFlags & VIDCAP_FLAG_AUDIO_ANALOG) {
318 mAudioSource = NTV2_AUDIO_ANALOG;
319 } else {
320 mAudioSource = NTV2_AUDIO_SOURCE_INVALID;
321 }
322
323 Init();
324}
325
326#ifndef _MSC_VER
327ADD_TO_PARAM("aja-fourcc", "* aja-fourcc\n"
328 " Specifies application FourCC for AJA.\n");
329#endif
331{
332 AJAStatus status (AJA_STATUS_SUCCESS);
333
334 // Open the device...
335 CNTV2DeviceScanner deviceScanner;
336
337 // Any AJA devices out there?
338 if (deviceScanner.GetNumDevices () == 0)
339 throw string("No devices found!");
340
341 if (mDeviceIndex >= deviceScanner.GetNumDevices ()) {
342 throw string("Device index exceeds number of available devices!");
343 }
344
345 // Using a reference to the discovered device list,
346 // and the index number of the device of interest (mDeviceIndex),
347 // get information about that particular device...
348 if (!mDevice.Open (mDeviceIndex))
349 throw string("Unable to open device.");
350
351 if (mClearRouting) {
352 CHECK_OK(mDevice.ClearRouting(), "ClearRouting", NOOP);
353 }
354
355 ULWord fourcc = app;
356#ifndef _MSC_VER
357 if (get_commandline_param("aja-fourcc")) {
358 const char *fcc_req = get_commandline_param("aja-fourcc");
359 char fcc_s[4] = "";
360 // trim or pad with spaces
361 memcpy(fcc_s, fcc_req, min<int>(strlen(fcc_req), 4));
362 fourcc = AJA_FOURCC(fcc_s[0], fcc_s[1], fcc_s[2], fcc_s[3]);
363 }
364
365 if (!mDevice.AcquireStreamForApplication (fourcc, static_cast <uint32_t> (getpid())))
366 throw string("Cannot aquire stream.");
367#endif
368
369 CHECK(mDevice.GetEveryFrameServices (mSavedTaskMode)); // Save the current state before we change it
370 CHECK(mDevice.SetEveryFrameServices (NTV2_OEM_TASKS));
371
372 // Keep the device ID handy as it will be used frequently...
373 mDeviceID = mDevice.GetDeviceID ();
374
375 // Set up the video and audio...
376 status = SetupVideo ();
377 if (AJA_FAILURE (status))
378 throw string("Cannot setup video. Err = ") + to_string(status);
379
380 status = SetupAudio ();
381 if (AJA_FAILURE (status))
382 throw string("Cannot setup audio. Err = ") + to_string(status);
383
384 // Set up the circular buffers, the device signal routing, and both playout and capture Auto Circulate...
386
387 // This is for the timecode that we will burn onto the image...
388 //NTV2FormatDescriptor fd = GetFormatDescriptor (GetNTV2StandardFromVideoFormat (mVideoFormat), mPixelFormat, mVancEnabled, Is2KFormat (mVideoFormat), mWideVanc);
389}
390
392 // Unsubscribe from input vertical event...
393 CHECK(mDevice.UnsubscribeInputVerticalEvent (mInputChannel));
394
395 CHECK(mDevice.SetEveryFrameServices (mSavedTaskMode)); // Restore previous service level
396#ifndef _MSC_VER
397 CHECK(mDevice.ReleaseStreamForApplication (app, static_cast <uint32_t> (getpid()))); // Release the device
398#endif
399
400 mOutputFrame = NULL;
401 free(mAudio.data);
402}
403
405{
406 NTV2VideoFormat videoFormat (NTV2_FORMAT_UNKNOWN);
407
408 switch (mInputSource)
409 {
410 case NTV2_INPUTSOURCE_SDI1:
411 case NTV2_INPUTSOURCE_SDI5:
412 {
413 NTV2InputSource source;
414 const ULWord ndx (::GetIndexForNTV2InputSource (mInputSource));
415 if (::NTV2DeviceCanDoMultiFormat (mDeviceID))
416 CHECK(mDevice.SetMultiFormatMode (true));
417 source = ::GetNTV2InputSourceForIndex (ndx + 0);
419 videoFormat = mDevice.GetInputVideoFormat(source, mProgressive);
420 NTV2Standard videoStandard (::GetNTV2StandardFromVideoFormat (videoFormat));
421 if (mCheckFor4K && (videoStandard == NTV2_STANDARD_1080p))
422 {
423 if (::NTV2DeviceCanDoMultiFormat (mDeviceID))
424 CHECK(mDevice.SetMultiFormatMode (false));
425 int i;
426 bool allSDISameInput = true;
427 NTV2VideoFormat videoFormatNext;
428 for (i = 1; i < 4; i++) {
429 source = ::GetNTV2InputSourceForIndex (ndx + i);
431 videoFormatNext = mDevice.GetInputVideoFormat (source, mProgressive);
432 if (videoFormatNext != videoFormat) {
433 allSDISameInput = false;
434 break;
435 }
436 }
437 if (allSDISameInput)
438 videoFormat = GetQuadSizedVideoFormat(videoFormat);
439 else
440 LOG(LOG_LEVEL_WARNING) << "Input " << NTV2InputSourceToString(::GetNTV2InputSourceForIndex(ndx + i), true) << " has input format " << NTV2VideoFormatToString(videoFormatNext) << " which differs from " << NTV2VideoFormatToString(videoFormat) << " on " << NTV2InputSourceToString(::GetNTV2InputSourceForIndex(ndx), true) << "!\n";
441 }
442 if (mCheckFor4K && (videoStandard != NTV2_STANDARD_1080p)) {
443 log_msg(LOG_LEVEL_WARNING, "Video format on first SDI is not 1080p!\n");
444 }
445 break;
446 }
447
448 case NTV2_NUM_INPUTSOURCES:
449 break; // indicates no source is currently selected
450
451 default:
452 videoFormat = mDevice.GetInputVideoFormat (mInputSource, mProgressive);
453 break;
454 }
455
456 return videoFormat;
457} // GetVideoFormatFromInputSource
458
460{
461 // Bi-directional SDI connectors need to be set to capture...
462 if (NTV2DeviceHasBiDirectionalSDI (mDeviceID) && NTV2_INPUT_SOURCE_IS_SDI (source))
463 {
464 NTV2Channel channel = ::NTV2InputSourceToChannel(source);
465 CHECK(mDevice.SetSDITransmitEnable (channel, false)); // Disable transmit mode...
466 for (unsigned ndx (0); ndx < 10; ndx++)
467 CHECK(mDevice.WaitForInputVerticalInterrupt (channel)); // ...and give the device some time to lock to a signal
468 }
469
470}
471
472/*
473 * see NTV2FrameGrabber::SetupInput()
474 */
476{
477 NTV2LHIHDMIColorSpace hdmiColor (NTV2_LHIHDMIColorSpaceRGB);
478 CHECK_RET_FAIL(mDevice.GetHDMIInputColor (hdmiColor, mInputChannel));
479 if (!mbFixedReference)
480 CHECK_RET_FAIL(mDevice.SetReference (::NTV2InputSourceToReferenceSource(mInputSource)));
481
482 // configure hdmi with 2.0 support
483 if (NTV2_IS_4K_VIDEO_FORMAT (mVideoFormat) && !mDevice.features().CanDoHDMIQuadRasterConversion()) {
484 // Set two sample interleave
485 CHECK_RET_FAIL(mDevice.SetTsiFrameEnable(true, mInputChannel));
486
487 for (unsigned offset = 0; offset < 2; ++offset) {
488 NTV2Channel channel = (NTV2Channel) ((int) mInputChannel + offset);
489 CHECK_RET_FAIL(mDevice.EnableChannel (channel));
490 CHECK_RET_FAIL(mDevice.SetMode (channel, NTV2_MODE_CAPTURE));
491 CHECK_RET_FAIL(mDevice.SetFrameBufferFormat (channel, mPixelFormat));
492 }
493
494 for (unsigned offset = 0; offset < 4; ++offset) {
495 if ((hdmiColor == NTV2_LHIHDMIColorSpaceYCbCr && IsRGBFormat(mPixelFormat))
496 || (hdmiColor == NTV2_LHIHDMIColorSpaceRGB && !IsRGBFormat(mPixelFormat))) {
497 NTV2Channel channel = (NTV2Channel) ((int) mInputChannel + offset);
498 CHECK_RET_FAIL(mDevice.Connect (::GetCSCInputXptFromChannel (channel),
499 ::GetInputSourceOutputXpt (mInputSource, false/*isSDI_DS2*/,
500 hdmiColor == NTV2_LHIHDMIColorSpaceRGB /*isHDMI_RGB*/, offset/*hdmiQuadrant*/)));
501 CHECK_RET_FAIL(mDevice.Connect (GetTSIMuxInputXptFromChannel((NTV2Channel) offset, false/* inLinkB */), GetCSCOutputXptFromChannel (channel, false/* inIsKey */, hdmiColor != NTV2_LHIHDMIColorSpaceRGB /* inIsRGB */)));
502 } else {
503 CHECK_RET_FAIL(mDevice.Connect (GetTSIMuxInputXptFromChannel((NTV2Channel) offset, false/* inLinkB */), ::GetInputSourceOutputXpt (mInputSource, false/*isSDI_DS2*/, hdmiColor == NTV2_LHIHDMIColorSpaceRGB /*isHDMI_RGB*/, offset /*hdmiQuadrant*/)));
504 }
505 }
506
507 for (unsigned offset = 0; offset < 4; ++offset) {
508 CHECK_RET_FAIL(mDevice.Connect(GetFrameBufferInputXptFromChannel((NTV2Channel) (offset / 2) /* inChannel */, offset % 2 == 1 /* inIsBInput */),
509 GetTSIMuxOutputXptFromChannel((NTV2Channel) (offset / 2), offset % 2 == 1 /* inLinkB */)));
510 }
511 } else if (NTV2_IS_4K_VIDEO_FORMAT (mVideoFormat) && mDevice.features().CanDoHDMIQuadRasterConversion()) {
512 CHECK_RET_FAIL(mDevice.SetTsiFrameEnable(false, mInputChannel));
513 for (unsigned offset = 0; offset < 4; ++offset) {
514 NTV2Channel channel = (NTV2Channel) ((int) mInputChannel + offset);
515 CHECK_RET_FAIL(mDevice.EnableChannel (channel));
516 CHECK_RET_FAIL(mDevice.SetMode (channel, NTV2_MODE_CAPTURE));
517 CHECK_RET_FAIL(mDevice.SetFrameBufferFormat (channel, mPixelFormat));
518 if ((hdmiColor == NTV2_LHIHDMIColorSpaceYCbCr && IsRGBFormat(mPixelFormat))
519 || (hdmiColor == NTV2_LHIHDMIColorSpaceRGB && !IsRGBFormat(mPixelFormat))) {
520 CHECK_RET_FAIL(mDevice.Connect (::GetCSCInputXptFromChannel (channel),
521 ::GetInputSourceOutputXpt (mInputSource, false/*isSDI_DS2*/, hdmiColor == NTV2_LHIHDMIColorSpaceRGB /*isHDMI_RGB*/, channel /*hdmiQuadrant*/)));
522 CHECK_RET_FAIL(mDevice.Connect (::GetFrameBufferInputXptFromChannel (channel),
523 ::GetCSCOutputXptFromChannel (channel, false/*isKey*/, hdmiColor != NTV2_LHIHDMIColorSpaceRGB /*isRGB*/)));
524 } else {
525 CHECK_RET_FAIL(mDevice.Connect (::GetFrameBufferInputXptFromChannel (channel),
526 ::GetInputSourceOutputXpt (mInputSource, false/*isSDI_DS2*/, hdmiColor == NTV2_LHIHDMIColorSpaceRGB /*isHDMI_RGB*/, channel /*hdmiQuadrant*/)));
527 }
528 } // loop once for each channel (4 times for 4K/UHD)
529 } else {
530 CHECK_RET_FAIL(mDevice.EnableChannel (mInputChannel));
531 CHECK_RET_FAIL(mDevice.SetMode (mInputChannel, NTV2_MODE_CAPTURE));
532 CHECK_RET_FAIL(mDevice.SetFrameBufferFormat (mInputChannel, mPixelFormat));
533 if ((hdmiColor == NTV2_LHIHDMIColorSpaceYCbCr && IsRGBFormat(mPixelFormat))
534 || (hdmiColor == NTV2_LHIHDMIColorSpaceRGB && !IsRGBFormat(mPixelFormat))) {
535 CHECK_RET_FAIL(mDevice.Connect (::GetCSCInputXptFromChannel (mInputChannel),
536 ::GetInputSourceOutputXpt (mInputSource, false/*isSDI_DS2*/, hdmiColor == NTV2_LHIHDMIColorSpaceRGB /*isHDMI_RGB*/, 0/*hdmiQuadrant*/)));
537 CHECK_RET_FAIL(mDevice.Connect (::GetFrameBufferInputXptFromChannel (mInputChannel),
538 ::GetCSCOutputXptFromChannel (mInputChannel, false/*isKey*/, hdmiColor != NTV2_LHIHDMIColorSpaceRGB /*isRGB*/)));
539 } else {
540 CHECK_RET_FAIL(mDevice.Connect (::GetFrameBufferInputXptFromChannel (mInputChannel),
541 ::GetInputSourceOutputXpt (mInputSource, false/*isSDI_DS2*/, hdmiColor == NTV2_LHIHDMIColorSpaceRGB /*isHDMI_RGB*/, 0/*hdmiQuadrant*/)));
542 }
543 }
544
545 // configure the qrc if present
546 if (NTV2DeviceGetHDMIVersion(mDeviceID) == 2) {
547 if (NTV2_IS_4K_VIDEO_FORMAT (mVideoFormat)) {
548 CHECK_RET_FAIL(mDevice.SetHDMIV2Mode (NTV2_HDMI_V2_4K_CAPTURE));
549 } else {
550 CHECK_RET_FAIL(mDevice.SetHDMIV2Mode (NTV2_HDMI_V2_HDSD_BIDIRECTIONAL));
551 }
552 }
553
554 return AJA_STATUS_SUCCESS;
555}
556
558{
559 // Set the video format to match the incomming video format.
560 // Does the device support the desired input source?
561 if (!::NTV2DeviceCanDoInputSource (mDeviceID, mInputSource))
562 return AJA_STATUS_BAD_PARAM; // Nope
563
564 if (mInputChannel == NTV2_CHANNEL_INVALID) {
565 mInputChannel = ::NTV2InputSourceToChannel (mInputSource);
566 }
567
568 if (UWord (mInputChannel) >= ::NTV2DeviceGetNumFrameStores (mDeviceID)) {
569 ostringstream oss;
570 oss << "## ERROR: Cannot use channel '" << mInputChannel+1 << "' -- device only supports channel 1"
571 << (::NTV2DeviceGetNumFrameStores (mDeviceID) > 1 ? string (" thru ") + string (1, uint8_t (::NTV2DeviceGetNumFrameStores (mDeviceID)+'0')) : "");
572 throw runtime_error(oss.str());
573 }
574
575 // Sometimes other applications disable some or all of the frame buffers, so turn on ours now..
576 CHECK_OK(mDevice.EnableChannel (mInputChannel), "Cannot enable channel", NOOP);
577
578 // Enable and subscribe to the interrupts for the channel to be used...
579 CHECK_OK(mDevice.EnableInputInterrupt (mInputChannel), "EnableInputInterrupt failed", NOOP);
580 CHECK_OK(mDevice.SubscribeInputVerticalEvent (mInputChannel), "SubscribeInputVerticalEvent failed", NOOP);
581
582 // Set the video format to match the incomming video format.
583 // Does the device support the desired input source?
584
585 // If the device supports bi-directional SDI and the
586 // requested input is SDI, ensure the SDI direction
587 // is configured for input...
588 if (::NTV2DeviceHasBiDirectionalSDI (mDeviceID) && NTV2_INPUT_SOURCE_IS_SDI (mInputSource))
589 {
590 CHECK_OK(mDevice.SetSDITransmitEnable (mInputChannel, false), "Cannot disable SDI transmit", NOOP);
591
592 // Give the input circuit some time (~10 frames) to lock onto the input signal...
593 CHECK_OK(mDevice.WaitForInputVerticalInterrupt (mInputChannel, 10), "Cannot wait for VBI", NOOP);
594 }
595
596 if (NTV2_INPUT_SOURCE_IS_SDI (mInputSource))
597 mTimeCodeSource = ::NTV2InputSourceToTimecodeIndex(mInputSource);
598 else if (NTV2_INPUT_SOURCE_IS_ANALOG (mInputSource))
599 mTimeCodeSource = NTV2_TCINDEX_LTC1;
600 else
601 mTimeCodeSource = NTV2_TCINDEX_DEFAULT;
602
603 // Determine the input video signal format...
604 if (mVideoFormat == NTV2_FORMAT_UNKNOWN) {
606 if (mVideoFormat == NTV2_FORMAT_UNKNOWN)
607 {
608 LOG(LOG_LEVEL_ERROR) << "## ERROR: No input signal or unknown format\n";
609 return AJA_STATUS_NOINPUT; // Sorry, can't handle this format
610 }
611 LOG(LOG_LEVEL_INFO) << MOD_NAME "Detected input signal: " << NTV2VideoFormatToString(mVideoFormat) << "\n";
612 }
613
614 mAudioSystem = ::NTV2ChannelToAudioSystem(mInputChannel);
615
616 // Set the device video format to whatever we detected at the input...
617 CHECK(mDevice.SetVideoFormat (mVideoFormat, false, false, mInputChannel));
618
619 // Set the frame buffer pixel format for all the channels on the device
620 // (assuming it supports that pixel format -- otherwise default to 8-bit YCbCr)...
621 if (!::NTV2DeviceCanDoFrameBufferFormat (mDeviceID, mPixelFormat))
622 mPixelFormat = NTV2_FBF_8BIT_YCBCR;
623
624 // Set the pixel format for both device frame buffers...
625 CHECK(mDevice.SetFrameBufferFormat (mInputChannel, mPixelFormat));
626
627 // Enable and subscribe to the interrupts for the channel to be used...
628 CHECK(mDevice.EnableInputInterrupt (mInputChannel));
629 CHECK(mDevice.SubscribeInputVerticalEvent (mInputChannel));
630
631 // Tell the hardware which buffers to use until the main worker thread runs
632 CHECK(mDevice.SetInputFrame (mInputChannel, 0));
633
634 // Set the Frame Store modes
635 CHECK(mDevice.SetMode (mInputChannel, NTV2_MODE_CAPTURE));
636
637 CHECK(mDevice.SetReference (NTV2_REFERENCE_FREERUN));
638
639 if (NTV2_INPUT_SOURCE_IS_SDI (mInputSource)) {
640 for (unsigned offset (0); offset < 4; offset++) {
641 NTV2Channel SDIChannel = (NTV2Channel) ((int) NTV2InputSourceToChannel (mInputSource) + offset);
642 NTV2Channel channel = (NTV2Channel) ((int) mInputChannel + offset);
643
644 if (mInputIsRGB) {
645 CHECK(mDevice.Connect (::GetDLInInputXptFromChannel(channel, false), ::GetSDIInputOutputXptFromChannel (SDIChannel, false))); // SDIIn ==> DLIn
646 CHECK(mDevice.Connect (::GetDLInInputXptFromChannel(channel, true), ::GetSDIInputOutputXptFromChannel (SDIChannel, true))); // SDIIn ==> DLIn
647 }
648
649 if (IsRGBFormat(mPixelFormat) && !mInputIsRGB) { // convert YUV->RGB
650 CHECK(mDevice.Connect (::GetCSCInputXptFromChannel (channel), ::GetSDIInputOutputXptFromChannel (SDIChannel)));
651 CHECK(mDevice.Connect (::GetFrameBufferInputXptFromChannel (channel), ::GetCSCOutputXptFromChannel (SDIChannel, false/*isKey*/, true/*isRGB*/)));
652 } else if (IsRGBFormat(mPixelFormat) && mInputIsRGB) { // RGB->RGB
653 CHECK(mDevice.Connect (::GetFrameBufferInputXptFromChannel (channel), ::GetDLInOutputXptFromChannel(SDIChannel))); // DLOut ==> FBRGB
654 } else if (!IsRGBFormat(mPixelFormat) && !mInputIsRGB) { // YUV->YUV
655 CHECK(mDevice.Connect (::GetFrameBufferInputXptFromChannel (channel), ::GetSDIInputOutputXptFromChannel (SDIChannel)));
656 } else { // RGB->YUV
657 CHECK(mDevice.Connect (::GetCSCInputXptFromChannel (channel), ::GetDLInOutputXptFromChannel(SDIChannel)));
658 CHECK(mDevice.Connect (::GetFrameBufferInputXptFromChannel (channel), ::GetCSCOutputXptFromChannel (SDIChannel, false/*isKey*/, true/*isRGB*/)));
659 }
660
661 CHECK(mDevice.SetFrameBufferFormat (channel, mPixelFormat));
662 CHECK(mDevice.EnableChannel (channel));
663 CHECK(mDevice.SetSDIInLevelBtoLevelAConversion (SDIChannel, IsInput3Gb (mInputSource) && !mInputIsRGB ? true : false));
664 if (!NTV2_IS_4K_VIDEO_FORMAT (mVideoFormat))
665 break;
666 CHECK(mDevice.Set4kSquaresEnable(true, mInputChannel));
667 }
668 } else if (NTV2_INPUT_SOURCE_IS_ANALOG(mInputSource)) {
669 //mDevice.SetTsiFrameEnable(false, NTV2_CHANNEL1);
670
671 if (IsRGBFormat(mPixelFormat)) {
672 CHECK(mDevice.Connect (::GetCSCInputXptFromChannel (mInputChannel), NTV2_XptAnalogIn));
673 CHECK(mDevice.Connect (::GetFrameBufferInputXptFromChannel (mInputChannel), ::GetCSCOutputXptFromChannel (mInputChannel, false/*isKey*/, true/*isRGB*/)));
674 } else {
675 CHECK(mDevice.Connect (::GetFrameBufferInputXptFromChannel (NTV2Channel (mInputChannel)), NTV2_XptAnalogIn));
676 }
677 CHECK(mDevice.SetFrameBufferFormat (mInputChannel, mPixelFormat));
678 if (!mbFixedReference)
679 CHECK(mDevice.SetReference (NTV2_REFERENCE_ANALOG_INPUT));
680 } else if (NTV2_IS_VALID_VIDEO_FORMAT (mVideoFormat)) {
681 SetupHDMI();
682 } else {
683 LOG(LOG_LEVEL_WARNING) << "## DEBUG: NTV2FrameGrabber::SetupInput: Bad mInputSource switch value " << ::NTV2InputSourceToChannelSpec (mInputSource);
684 }
685
686 // Enable and subscribe to the interrupts for the channel to be used...
687 CHECK(mDevice.EnableInputInterrupt (mInputChannel));
688 CHECK(mDevice.SubscribeInputVerticalEvent (mInputChannel));
689
691 MSG(ERROR, "Cannot find valid mapping from AJA pixel format to "
692 "UltraGrid\n");
693 return AJA_STATUS_NOINPUT;
694 }
695
696 return AJA_STATUS_SUCCESS;
697}
698
700{
701 if (mAudioSource == NTV2_AUDIO_SOURCE_INVALID) {
702 LOG(LOG_LEVEL_INFO) << MOD_NAME "Audio capture disabled.\n";
703 return AJA_STATUS_SUCCESS;
704 }
705 // Have the audio system capture audio from the designated device input...
706 CHECK_OK(mDevice.SetAudioSystemInputSource(mAudioSystem, mAudioSource, ::NTV2InputSourceToEmbeddedAudioInput(mInputSource)), string("Cannot set audio input source: ") + NTV2AudioSourceToString(mAudioSource), NOOP);
707
708 mMaxAudioChannels = ::NTV2DeviceGetMaxAudioChannels (mDeviceID);
710 if (mMaxAudioChannels < mAudio.ch_count) {
711 LOG(LOG_LEVEL_ERROR) << MOD_NAME "Invalid number of capture channels requested. Requested " <<
712 mAudio.ch_count << ", maximum " << mMaxAudioChannels << "\n";
713 return AJA_STATUS_FAIL;
714 }
715 if (!mDevice.SetNumberAudioChannels (mMaxAudioChannels, NTV2InputSourceToAudioSystem(mInputSource))) {
716 LOG(LOG_LEVEL_ERROR) << MOD_NAME "Unable to set channel count!\n";
717 return AJA_STATUS_FAIL;
718 }
719 CHECK(mDevice.SetAudioRate (NTV2_AUDIO_48K, NTV2InputSourceToAudioSystem(mInputSource)));
720
721 // How big should the on-device audio buffer be? 1MB? 2MB? 4MB? 8MB?
722 // For this demo, 4MB will work best across all platforms (Windows, Mac & Linux)...
723 CHECK(mDevice.SetAudioBufferSize (NTV2_AUDIO_BUFFER_BIG, NTV2InputSourceToAudioSystem(mInputSource)));
724
725 //
726 // Loopback mode plays whatever audio appears in the input signal when it's
727 // connected directly to an output (i.e., "end-to-end" mode). If loopback is
728 // left enabled, the video will lag the audio as video frames get briefly delayed
729 // in our ring buffer. Audio, therefore, needs to come out of the (buffered) fram
730 // data being played, so loopback must be turned off...
731 //
732 CHECK(mDevice.SetAudioLoopBack (NTV2_AUDIO_LOOPBACK_OFF, mAudioSystem));
733
734 // Reset both the input and output sides of the audio system so that the buffer
735 // pointers are reset to zero and inhibited from advancing.
736 CHECK(mDevice.StopAudioInput(mAudioSystem));
737
738 // Ensure that the audio system will capture samples when the reset is removed
739 CHECK(mDevice.SetAudioCaptureEnable (mAudioSystem, true));
740
741 mAudio.bps = 4;
742 mAudio.sample_rate = 48000;
743 mAudio.data = (char *) malloc(NTV2_AUDIOSIZE_MAX);
744 mAudio.max_size = NTV2_AUDIOSIZE_MAX;
745
746#ifndef _MSC_VER
747 LOG(LOG_LEVEL_NOTICE) << "AJA audio capture initialized successfully: " << audio_desc_from_frame(&mAudio) << "\n";
748#endif
749
750 return AJA_STATUS_SUCCESS;
751} // SetupAudio
752
754{
755 mVancMode = NTV2_VANCMODE_OFF;
756 mWideVanc = false;
757 CHECK(mDevice.GetVANCMode (mVancMode));
758 mVideoBufferSize = GetVideoWriteSize (mVideoFormat, mPixelFormat, mVancMode);
760
761 interlacing_t interlacing = NTV2_VIDEO_FORMAT_HAS_PROGRESSIVE_PICTURE(mVideoFormat) ? PROGRESSIVE : INTERLACED_MERGED;
762 video_desc desc{GetDisplayWidth(mVideoFormat), GetDisplayHeight(mVideoFormat),
764 GetFramesPerSecond(GetNTV2FrameRateFromVideoFormat(mVideoFormat)),
765 interlacing,
766 1};
768
769#ifndef _MSC_VER
770 LOG(LOG_LEVEL_NOTICE) << "Detected input video mode: " << desc << "\n";
771#endif
772} // SetupHostBuffers
773
775{
776 // Check that there's a valid input to capture
777 if (mDevice.GetInputVideoFormat(mInputSource, mProgressive) ==
778 NTV2_FORMAT_UNKNOWN) {
779 MSG(WARNING, "## WARNING: No video signal present on the "
780 "input connector\n");
781 }
782
783 // Start the playout and capture threads...
785
786 return AJA_STATUS_SUCCESS;
787}
788
790{
791 // Set the global 'quit' flag, and wait for the threads to go inactive...
792 mGlobalQuit = true;
793
794 mProducerThread.join();
795
796 // Don't leave the audio system active after we exit
797 CHECK(mDevice.StopAudioInput(mAudioSystem));
798} // Quit
799
801
802// This is where we start the capture thread
804{
805 // Create and start the capture thread..
807} // StartProducerThread
808
809
810// The capture thread function
812{
813 pContext->CaptureFrames ();
814
815} // ProducerThreadStatic
816
818{
819 uint32_t currentInFrame = mInputChannel * 2; // Will ping-pong between 0 and 1
820
821 uint32_t currentAudioInAddress = 0;
822 uint32_t audioReadOffset = 0;
823 uint32_t audioInWrapAddress = 0;
824 uint32_t audioOutWrapAddress = 0;
825 uint32_t audioBytesCaptured = 0;
826 NTV2FieldID fieldID = NTV2_VIDEO_FORMAT_HAS_PROGRESSIVE_PICTURE(mVideoFormat) ? NTV2_FIELD0 : NTV2_FIELD1;
827
828 CHECK(mDevice.GetAudioReadOffset (audioReadOffset, mAudioSystem));
829 CHECK(mDevice.GetAudioWrapAddress (audioOutWrapAddress, mAudioSystem));
830
831 // Wait to make sure the next two SDK calls will be made during the same frame...
832 CHECK(mDevice.WaitForInputFieldID (fieldID, mInputChannel));
833
834 currentInFrame ^= 1;
835 CHECK(mDevice.SetInputFrame (mInputChannel, currentInFrame));
836
837 // Wait until the hardware starts filling the new buffers, and then start audio
838 // capture as soon as possible to match the video...
839 CHECK(mDevice.WaitForInputFieldID (fieldID, mInputChannel));
840 CHECK(mDevice.StartAudioInput(mAudioSystem));
841
842 mAudioInLastAddress = audioReadOffset;
843 audioInWrapAddress = audioOutWrapAddress + audioReadOffset;
844
845 currentInFrame ^= 1;
846 CHECK(mDevice.SetInputFrame (mInputChannel, currentInFrame));
847
848 while (!mGlobalQuit) {
849 uint32_t *pHostAudioBuffer = NULL;
850 // Wait until the input has completed capturing a frame...
851 CHECK(mDevice.WaitForInputFieldID (fieldID, mInputChannel));
852
853 if (mAudioSource != NTV2_AUDIO_SOURCE_INVALID) {
854 pHostAudioBuffer = reinterpret_cast <uint32_t *> (aligned_malloc(NTV2_AUDIOSIZE_MAX, AJA_PAGE_SIZE));
855 // Read the audio position registers as close to the interrupt as possible...
856 CHECK(mDevice.ReadAudioLastIn (currentAudioInAddress, NTV2AudioSystem(mInputChannel)));
857 currentAudioInAddress &= ~0x7f; // Force 128 B alignment (originally there was 4 bytes)
858 currentAudioInAddress += audioReadOffset;
859
860 if (((currentAudioInAddress + (audioInWrapAddress - audioReadOffset) - mAudioInLastAddress)
861 % (audioInWrapAddress - audioReadOffset)) > NTV2_AUDIOSIZE_MAX) {
862 LOG(LOG_LEVEL_WARNING) << MOD_NAME "Discarding audio samples!\n";
863 mAudioInLastAddress = (currentAudioInAddress + audioInWrapAddress - NTV2_AUDIOSIZE_MAX) % audioInWrapAddress;
864 }
865
866 if (currentAudioInAddress < mAudioInLastAddress) {
867 // Audio address has wrapped around the end of the buffer.
868 // Do the calculations and transfer from the last address to the end of the buffer...
869 audioBytesCaptured = audioInWrapAddress - mAudioInLastAddress;
870
871 CHECK(mDevice.DMAReadAudio (mAudioSystem, pHostAudioBuffer, mAudioInLastAddress, audioBytesCaptured));
872
873 // Transfer the new samples from the start of the buffer to the current address...
874 CHECK(mDevice.DMAReadAudio (mAudioSystem, &pHostAudioBuffer [audioBytesCaptured / sizeof (uint32_t)],
875 audioReadOffset, currentAudioInAddress - audioReadOffset));
876
877 audioBytesCaptured += currentAudioInAddress - audioReadOffset;
878 } else {
879 audioBytesCaptured = currentAudioInAddress - mAudioInLastAddress;
880
881 // No wrap, so just perform a linear DMA from the buffer...
882 CHECK(mDevice.DMAReadAudio (mAudioSystem, pHostAudioBuffer, mAudioInLastAddress, audioBytesCaptured));
883 }
884
885 mAudioInLastAddress = currentAudioInAddress;
886 }
887
888 // Flip sense of the buffers again to refer to the buffers that the hardware isn't using (i.e. the off-screen buffers)...
889 currentInFrame ^= 1;
890
891 shared_ptr<video_frame> out = mPool.get_frame();
892 // DMA the new frame to system memory...
893 CHECK(mDevice.DMAReadFrame (currentInFrame, reinterpret_cast<uint32_t *>(out->tiles[0].data), mVideoBufferSize));
894 if (out->color_spec == R12L) {
895 shared_ptr<video_frame> converted = mPool.get_frame();
896 vc_copylineR12AtoR12L((unsigned char *) converted->tiles[0].data, (unsigned char *) out->tiles[0].data, out->tiles[0].data_len, 0, 0, 0);
897 out = std::move(converted);
898 }
899
900 if (log_level >= LOG_LEVEL_DEBUG) {
901 NTV2_RP188 timecodeValue;
902 string timeCodeString;
903 // Use the embedded input time code...
904 CHECK(mDevice.GetRP188Data (mInputChannel, timecodeValue));
905 CRP188 inputRP188Info (timecodeValue);
906 CHECK(inputRP188Info.GetRP188Str (timeCodeString));
907 LOG(LOG_LEVEL_DEBUG) << "AJA: Captured frame with timecode: " << timeCodeString << '\n';
908 }
909
910 // Check for dropped frames by ensuring the hardware has not started to process
911 // the buffers that were just filled....
912 uint32_t readBackIn;
913 CHECK(mDevice.GetInputFrame (mInputChannel, readBackIn));
914
915 if (readBackIn == currentInFrame) {
917 << MOD_NAME
918 << "## WARNING: Drop detected: current in "
919 << currentInFrame << ", readback in " << readBackIn
920 << "\n";
921 }
922
923 // Tell the hardware which buffers to start using at the beginning of the next frame...
924 CHECK(mDevice.SetInputFrame (mInputChannel, currentInFrame));
925
926 unique_lock<mutex> lk(mOutputFrameLock);
927 mOutputFrame = std::move(out);
928 mOutputAudioFrame = shared_ptr<uint32_t>(pHostAudioBuffer, aligned_free);
929 mOutputAudioFrameSize = audioBytesCaptured;
930 lk.unlock();
931 mOutputFrameReady.notify_one();
932 } // loop til quit signaled
933} // CaptureFrames
934
936{
937 if (mGlobalQuit) {
938 return NULL;
939 }
940
941 struct video_frame *ret;
942
943 unique_lock<mutex> lk(mOutputFrameLock);
944 if (mOutputFrameReady.wait_for(lk, chrono::milliseconds(100), [this]{return mOutputFrame != NULL;}) == false) {
945 return NULL;
946 }
947
948 ret = mOutputFrame.get();
949 ret->callbacks.dispose_udata = new shared_ptr<video_frame>(mOutputFrame);
950 static auto dispose = [](video_frame *f) { delete static_cast<shared_ptr<video_frame> *>(f->callbacks.dispose_udata); };
951 ret->callbacks.dispose = dispose;
952
953 mOutputFrame = NULL;
954
955 if (mOutputAudioFrame) {
956 for (int i = 0; i < mAudio.ch_count; ++i) {
958 }
960 mOutputAudioFrame = NULL;
961 *audio = &mAudio;
962 } else {
963 *audio = NULL;
964 }
965
966 lk.unlock();
967
968 mFrames += 1;
969
970 chrono::system_clock::time_point now = chrono::system_clock::now();
971 double seconds = chrono::duration_cast<chrono::microseconds>(now - mT0).count() / 1000000.0;
972
973 if (seconds >= 5) {
974 LOG(LOG_LEVEL_INFO) << MOD_NAME "" << mFrames << " frames in "
975 << seconds << " seconds = " << mFrames / seconds << " FPS\n";
976 mT0 = now;
977 mFrames = 0;
978 }
979
980 return ret;
981}
982
983bool vidcap_state_aja::IsInput3Gb(const NTV2InputSource inputSource)
984{
985 bool is3Gb (false);
986
987 CHECK(mDevice.GetSDIInput3GbPresent (is3Gb, ::NTV2InputSourceToChannel (inputSource)));
988
989 return is3Gb;
990}
991
992static void show_help() {
993 col() << "Usage:\n";
994 col() << SBOLD(SRED("\t-t aja") << "[[:4K][:clear-routing][:channel=<ch>][:codec=<pixfmt>][:connection=<c>][:device=<idx>][:format=<fmt>][:progressive][:RGB|:YUV]|:help] -r [embedded|AESEBU|analog]")"\n";
995 col() << "where\n";
996
997 col() << SBOLD("\t4K") << "\n";
998 col() << "\t\tVideo input is 4K.\n";
999
1000 col() << SBOLD("\tclear-routing") "\n" <<
1001 "\t\tremove all existing signal paths for device\n";
1002
1003 col() << SBOLD("\tchannel") "\n";
1004 col() << "\t\tChannel number to use (indexed from 1). Doesn't need to "
1005 "be set for SDI, useful for HDMI (capture and display should "
1006 "have different channel numbers if both used, also other than "
1007 "1 if SDI1 is in use).\n";
1008
1009 col() << SBOLD("\tconnection") "\n" <<
1010 "\t\tConnection can be one of: ";
1011 NTV2InputSource source = NTV2InputSource();
1012 while (source != NTV2_INPUTSOURCE_INVALID) {
1013 if (source > 0) {
1014 col() << ", ";
1015 }
1016 col() << NTV2InputSourceToString(source, true);
1017 // should be this, but GetNTV2InputSourceForIndex knows only SDIs
1018 //source = ::GetNTV2InputSourceForIndex(::GetIndexForNTV2InputSource(source) + 1);
1019 source = (NTV2InputSource) ((int) source + 1);
1020 }
1021 col() << "\n";
1022
1023 col() << SBOLD("\tprogressive") "\n";
1024 col() << "\t\tVideo input is progressive.\n";
1025
1026 col() << SBOLD("\tRGB|YUV") "\n";
1027 col() << "\t\tSet SDI video input to RGB or YCbCr explicitly. If "
1028 "capture pixel format is set, expect the same encoding on "
1029 "input. You can override this setting by specifying the "
1030 "signal color space explicitly by this option (see "
1031 "https://github.com/CESNET/UltraGrid/wiki/"
1032 "Device-Settings#RGB_over_SDI for details).\n";
1033 col() << "\n";
1034
1035 printf("Available devices:\n");
1036 CNTV2DeviceScanner deviceScanner;
1037 for (unsigned int i = 0; i < deviceScanner.GetNumDevices (); i++) {
1038 CNTV2Card device;
1039 if (!CNTV2DeviceScanner::GetDeviceAtIndex(i, device)) {
1040 MSG(WARNING, "Cannot get device at index #%u!\n", i);
1041 continue;
1042 }
1043
1044 col() << "\t" << SBOLD(i) << ") "
1045 << SBOLD(device.GetDisplayName());
1046 print_aja_device_details(&device);
1047 const NTV2DeviceID deviceID = device.GetBaseDeviceID();
1048 NTV2VideoFormatSet fmt_set;
1049 if (NTV2DeviceGetSupportedVideoFormats(deviceID, fmt_set)) {
1050 col() << SUNDERLINE("\tAvailable formats:");
1051 for (auto fmt : fmt_set) {
1052 if (fmt != *fmt_set.begin()) {
1053 col()<< ",";
1054 }
1055 col() << " \"" << NTV2VideoFormatToString(fmt)
1056 << "\"";
1057 }
1058 col() << "\n";
1059 }
1060 NTV2FrameBufferFormatSet pix_fmts;
1061 if (NTV2DeviceGetSupportedPixelFormats(deviceID, pix_fmts)) {
1062 if (pix_fmts.count(NTV2_FBF_10BIT_YCBCR) == 0 && NTV2DeviceCanDoFrameBufferFormat(deviceID, NTV2_FBF_10BIT_YCBCR)) {
1063 pix_fmts.insert(NTV2_FBF_10BIT_YCBCR); // workaround NTV2 bug
1064 }
1065 col() << SUNDERLINE("\tAvailable pixel formats:");
1066 for (auto fmt : pix_fmts) {
1067 if (fmt != *pix_fmts.begin()) {
1068 col() << ",";
1069 }
1070 if (ugaja::codec_map.find(fmt) ==
1071 ugaja::codec_map.end()) {
1072 col() << " "
1073 << NTV2FrameBufferFormatToString(
1074 fmt)
1075 << " (unsupported)";
1076 } else {
1077 col() << " "
1078 << get_codec_name(
1079 ugaja::codec_map.at(fmt));
1080 }
1081 }
1082 col() << "\n";
1083 }
1084 col() << SUNDERLINE("\tNumber of frame stores: ") << NTV2DeviceGetNumFrameStores (deviceID) << "\n";
1085 }
1086 if (deviceScanner.GetNumDevices() == 0) {
1087 col() << SUNDERLINE("\tno devices found") "\n";
1088 }
1089 col() << "\n";
1090}
1091
1093{
1094 unordered_map<string, string> parameters_map;
1095 char *tmp = strdup(vidcap_params_get_fmt(params));
1096 if (strcmp(tmp, "help") == 0) {
1097 show_help();
1098 free(tmp);
1099 return VIDCAP_INIT_NOERR;
1100 }
1101 char *item, *cfg = tmp;
1102 while ((item = strtok(cfg, ":"))) {
1103 char *key_cstr = item;
1104 if (strchr(item, '=')) {
1105 char *val_cstr = strchr(item, '=') + 1;
1106 *strchr(item, '=') = '\0';
1107 parameters_map[key_cstr] = val_cstr;
1108 } else {
1109 parameters_map[key_cstr] = string();
1110 }
1111 cfg = NULL;
1112 }
1113 free(tmp);
1114
1115 vidcap_state_aja *ret = nullptr;
1116 try {
1118 ret->Run();
1119 } catch (string const & s) {
1120 LOG(LOG_LEVEL_ERROR) << "[AJA cap.] " << s << "\n";
1121 delete ret;
1122 return VIDCAP_INIT_FAIL;
1123 } catch (...) {
1124 delete ret;
1125 return VIDCAP_INIT_FAIL;
1126 }
1127 *state = ret;
1128 return VIDCAP_INIT_OK;
1129}
1130
1132{
1133 auto s = static_cast<vidcap_state_aja *>(state);
1134 s->Quit();
1135 delete s;
1136}
1137
1139{
1140 return ((vidcap_state_aja *) state)->grab(audio);
1141}
1142
1143LINK_SPEC void vidcap_aja_probe(device_info **available_cards, int *count, void (**deleter)(void *))
1144{
1145 *deleter = free;
1146
1147 device_info *cards = nullptr;
1148 int card_count = 0;
1149
1150 CNTV2DeviceScanner deviceScanner;
1151 for (unsigned int i = 0; i < deviceScanner.GetNumDevices (); i++) {
1152 CNTV2Card device;
1153 if (!CNTV2DeviceScanner::GetDeviceAtIndex(i, device)) {
1154 MSG(WARNING, "Cannot get device at index #%u!\n", i);
1155 continue;
1156 }
1157 card_count += 1;
1158 cards = (struct device_info *)
1159 realloc(cards, card_count * sizeof(struct device_info));
1160 memset(&cards[card_count - 1], 0, sizeof(struct device_info));
1161 snprintf(cards[card_count - 1].dev, sizeof cards[card_count - 1].dev,
1162 ":device=%d", i);
1163 snprintf(cards[card_count - 1].name, sizeof cards[card_count - 1].name,
1164 "AJA %s", device.GetDisplayName().c_str());
1165 snprintf(cards[card_count - 1].extra, sizeof cards[card_count - 1].extra,
1166 "\"embeddedAudioAvailable\":\"t\"");
1167 }
1168
1169 *available_cards = cards;
1170 *count = card_count;
1171}
1172
1173#ifndef _MSC_VER
1181
1183#endif
1184
void print_aja_device_details(CNTV2Card *device)
void vc_copylineR12AtoR12L(unsigned char *__restrict dst, const unsigned char *__restrict src, int dstlen, int rshift, int gshift, int bshift)
void ** state
struct video_desc desc
const char * fmt
struct audio_frame ** audio
int void(** deleter)(void *))
__declspec(dllimport) int vidcap_aja_init(struct vidcap_params *params
const char * get_codec_name(codec_t codec)
codec_t get_codec_from_name(const char *name)
This file contains definition of some common audio types.
struct video_frame * grab(struct audio_frame **audio)
Definition aja.cpp:935
bool mProgressive
Definition aja.cpp:193
NTV2EveryFrameTaskMode mSavedTaskMode
My device identifier.
Definition aja.cpp:176
bool mCheckFor4K
Definition aja.cpp:201
NTV2InputSource mInputSource
Wide VANC?
Definition aja.cpp:182
AJAStatus SetupHDMI()
Definition aja.cpp:475
bool mClearRouting
Definition aja.cpp:203
NTV2Channel mInputChannel
Used to restore prior every-frame task mode.
Definition aja.cpp:177
AJAStatus SetupAudio()
Definition aja.cpp:699
uint32_t mAudioBufferSize
My video buffer size, in bytes.
Definition aja.cpp:185
chrono::system_clock::time_point mT0
Definition aja.cpp:194
int mMaxAudioChannels
Definition aja.cpp:197
NTV2AudioSource mAudioSource
Definition aja.cpp:198
unsigned int mDeviceIndex
Definition aja.cpp:173
NTV2TCIndex mTimeCodeSource
Time code source.
Definition aja.cpp:199
size_t mOutputAudioFrameSize
Definition aja.cpp:190
NTV2VideoFormat GetVideoFormatFromInputSource()
Definition aja.cpp:404
void EnableInput(NTV2InputSource source)
Definition aja.cpp:459
bool mbFixedReference
Definition aja.cpp:200
NTV2VideoFormat mVideoFormat
My input channel.
Definition aja.cpp:178
shared_ptr< uint32_t > mOutputAudioFrame
Definition aja.cpp:189
thread mProducerThread
My audio buffer size, in bytes.
Definition aja.cpp:186
virtual void StartProducerThread(void)
Starts my frame consumer thread.
Definition aja.cpp:803
AJAStatus SetupVideo()
Definition aja.cpp:557
bool IsInput3Gb(const NTV2InputSource inputSource)
Definition aja.cpp:983
uint32_t mVideoBufferSize
The audio system I'm using.
Definition aja.cpp:184
mutex mOutputFrameLock
Definition aja.cpp:191
NTV2AudioSystem mAudioSystem
The input source I'm using.
Definition aja.cpp:183
void Quit()
Definition aja.cpp:789
video_frame_pool mPool
My producer thread object – does the frame capturing.
Definition aja.cpp:187
vidcap_state_aja(unordered_map< string, string > const &parameters, int audioFlags)
Definition aja.cpp:223
bool mWideVanc
VANC enabled?
Definition aja.cpp:181
static void ProducerThreadStatic(vidcap_state_aja *pContext)
Definition aja.cpp:811
bool mInputIsRGB
SDI Input is in RGB otherwise YCbCr.
Definition aja.cpp:204
CNTV2Card mDevice
Definition aja.cpp:174
condition_variable mOutputFrameReady
Definition aja.cpp:192
void Init()
Definition aja.cpp:330
uint32_t mAudioInLastAddress
My record of the location of the last audio sample captured.
Definition aja.cpp:202
NTV2VANCMode mVancMode
My pixel format.
Definition aja.cpp:180
NTV2FrameBufferFormat mPixelFormat
My video format.
Definition aja.cpp:179
void SetupHostBuffers()
Definition aja.cpp:753
shared_ptr< video_frame > mOutputFrame
Definition aja.cpp:188
AJAStatus Run()
Definition aja.cpp:774
NTV2DeviceID mDeviceID
Definition aja.cpp:175
virtual ~vidcap_state_aja()
Definition aja.cpp:391
bool mGlobalQuit
Definition aja.cpp:205
virtual void CaptureFrames(void)
Definition aja.cpp:817
#define SRED(x)
Definition color_out.h:85
#define SBOLD(x)
Definition color_out.h:80
#define SUNDERLINE(x)
Definition color_out.h:81
static __device__ const float offset
Definition cuda_dxt.cu:139
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_NOTICE
information that may be interesting
Definition debug.h:61
#define LOG_LEVEL_INFO
normal reporting
Definition debug.h:62
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define MSG(l, fmt,...)
Definition debug.h:96
#define LOG(level)
Definition debug.h:313
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
#define VIDCAP_INIT_NOERR
state not initialized, other action performed (typically help)
#define VIDCAP_INIT_OK
initialization successful
#define VIDCAP_NO_GENERIC_FPS_INDICATOR
#define VIDEO_CAPTURE_ABI_VERSION
#define VIDCAP_INIT_FAIL
error ocured
int count
Definition hd-rum.c:44
const char * get_commandline_param(const char *key)
Definition host.cpp:894
unsigned int audio_capture_channels
Definition host.cpp:163
#define DEFAULT_AUDIO_CAPTURE_CHANNELS
Definition host.h:89
#define ADD_TO_PARAM(param, doc)
Definition host.h:203
#define LOG_LEVEL_DEBUG
Definition kernels.cu:48
volatile int log_level
Definition debug.cpp:65
static void * aligned_malloc(size_t size, size_t alignment)
#define aligned_free
@ LIBRARY_CLASS_VIDEO_CAPTURE
Definition lib_common.h:78
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
Definition lib_common.h:144
static const std::map< NTV2FrameBufferFormat, codec_t > codec_map
struct _source source
const char * name
Definition settings.cpp:193
Definition rtp.c:206
video_frame_pool_allocator * clone() const override
Definition aja.cpp:157
void * allocate(size_t size) override
Definition aja.cpp:151
void deallocate(void *ptr) override
Definition aja.cpp:154
struct used to comunicate with audio capture/playback device
Definition types.h:99
char extra[1024]
Definition types.h:389
char dev[1024]
Definition types.h:383
Definition hd-rum.c:27
void(* dispose)(struct video_frame *)
Definition types.h:245
std::shared_ptr< video_frame > get_frame()
void reconfigure(struct video_desc new_desc, size_t new_size=SIZE_MAX)
Struct video_frame represents a video frame and contains video description.
Definition types.h:305
struct video_frame_callbacks callbacks
Definition types.h:324
interlacing_t
Definition types.h:163
@ PROGRESSIVE
progressive frame
Definition types.h:164
@ INTERLACED_MERGED
Columngs of both fields are interlaced together.
Definition types.h:167
@ VIDEO_CODEC_NONE
dummy color spec
Definition types.h:66
@ R12L
RGB 12-bit packed, little-endian.
Definition types.h:74
struct audio_desc audio_desc_from_frame(const struct audio_frame *frame)
Definition utils.cpp:196
void remux_channel(char *out, const char *in, int bps, int in_len, int in_stream_channels, int out_stream_channels, int pos_in_stream, int pos_out_stream)
Definition utils.cpp:352
This is an umbrella header for video functions.
#define CHECK_OK(cmd, msg, action_failed)
Definition aja.cpp:127
#define CHECK(cmd)
Definition aja.cpp:135
#define CHECK_RET_FAIL(cmd)
Definition aja.cpp:134
LINK_SPEC int vidcap_aja_init(struct vidcap_params *params, void **state)
Definition aja.cpp:1092
static void show_help()
Definition aja.cpp:992
static const struct video_capture_info vidcap_aja_info
Definition aja.cpp:1174
#define LINK_SPEC
Definition aja.cpp:124
#define NOOP
Definition aja.cpp:132
LINK_SPEC struct video_frame * vidcap_aja_grab(void *state, struct audio_frame **audio)
Definition aja.cpp:1138
#define MOD_NAME
Definition aja.cpp:110
LINK_SPEC void vidcap_aja_probe(device_info **available_cards, int *count, void(**deleter)(void *))
Definition aja.cpp:1143
#define NTV2_AUDIOSIZE_MAX
Definition aja.cpp:106
LINK_SPEC void vidcap_aja_done(void *state)
Definition aja.cpp:1131
static const ULWord app
Definition aja.cpp:162
unsigned int * aja_audio_capture_channels
Definition aja.cpp:118
const char * vidcap_params_get_fmt(const struct vidcap_params *params)
unsigned int vidcap_params_get_flags(const struct vidcap_params *params)
#define VIDCAP_FLAG_AUDIO_AESEBU
AES/EBU audio.
#define VIDCAP_FLAG_AUDIO_ANALOG
(balanced) analog audio
#define VIDCAP_FLAG_AUDIO_ANY
#define VIDCAP_FLAG_AUDIO_EMBEDDED
HD-SDI embedded audio.