UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
decklink.cpp
Go to the documentation of this file.
1/*
2 * FILE: video_capture/decklink.cpp
3 * AUTHORS: Martin Benes <martinbenesh@gmail.com>
4 * Lukas Hejtmanek <xhejtman@ics.muni.cz>
5 * Petr Holub <hopet@ics.muni.cz>
6 * Milos Liska <xliska@fi.muni.cz>
7 * Martin Pulec <martin.pulec@cesnet.cz>
8 * Jiri Matela <matela@ics.muni.cz>
9 * Dalibor Matura <255899@mail.muni.cz>
10 * Ian Wesley-Smith <iwsmith@cct.lsu.edu>
11 *
12 * Copyright (c) 2005-2024 CESNET z.s.p.o.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, is permitted provided that the following conditions
16 * are met:
17 *
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 *
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
24 *
25 * 3. All advertising materials mentioning features or use of this software
26 * must display the following acknowledgement:
27 *
28 * This product includes software developed by CESNET z.s.p.o.
29 *
30 * 4. Neither the name of the CESNET nor the names of its contributors may be
31 * used to endorse or promote products derived from this software without
32 * specific prior written permission.
33 *
34 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
35 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
36 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
37 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
38 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
39 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
40 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
41 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
42 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
43 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
44 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
45 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
46 *
47 */
48
49#include <algorithm>
50#include <cassert>
51#include <cctype> // for isdigit
52#include <chrono>
53#include <climits>
54#include <condition_variable>
55#include <cstdint> // for int64_t, uint32_t, int32_t
56#include <cstdio> // for snprintf, printf
57#include <cstdlib> // for free, abort, atoi, malloc, realloc
58#include <cstring>
59#include <exception> // for exception
60#include <iomanip>
61#include <iostream>
62#include <list>
63#include <map> // for map, operator!=, _Rb_tree_const_it...
64#include <memory>
65#include <mutex>
66#include <queue>
67#include <string>
68#include <tuple> // for tuple, get
69#include <unordered_map> // for unordered_map
70#include <utility> // for pair, operator!=
71#include <vector>
72
73#include "blackmagic_common.hpp"
74#include "audio/types.h"
75#include "audio/utils.h"
76#include "compat/htonl.h"
77#include "compat/strings.h"
78#include "debug.h"
79#include "host.h"
80#include "lib_common.h"
81#include "types.h"
82#include "utils/color_out.h"
83#include "utils/macros.h"
84#include "utils/math.h"
85#include "utils/windows.h"
86#include "video.h"
87#include "video_capture.h"
88
89constexpr const int DEFAULT_AUDIO_BPS = 4;
90constexpr const size_t MAX_AUDIO_PACKETS = 10;
91#define MOD_NAME "[DeckLink capture] "
92
93#ifndef _WIN32
94#define STDMETHODCALLTYPE
95#endif
96
97#define RELEASE_IF_NOT_NULL(x) if (x != nullptr) { x->Release(); x = nullptr; }
98
99using namespace std::string_literals;
100using std::chrono::duration_cast;
101using std::chrono::microseconds;
102using std::chrono::milliseconds;
103using std::chrono::steady_clock;
104using std::condition_variable;
105using std::cv_status;
106using std::cout;
107using std::exception;
108using std::get;
109using std::internal;
110using std::left;
111using std::list;
112using std::make_unique;
113using std::map;
114using std::max;
115using std::min;
116using std::mutex;
117using std::queue;
118using std::right;
119using std::setw;
120using std::string;
121using std::tuple;
122using std::unique_lock;
123using std::unique_ptr;
124using std::unordered_map;
125using std::vector;
126
127// static int device = 0; // use first BlackMagic device
128// static int mode = 5; // for Intensity
129// static int mode = 6; // for Decklink 6) HD 1080i 59.94; 1920 x 1080; 29.97 FPS 7) HD 1080i 60; 1920 x 1080; 30 FPS
130//static int connection = 0; // the choice of BMDVideoConnection // It should be 0 .... bmdVideoConnectionSDI
131
132// performs command, if failed, displays error and jumps to error label
133#define EXIT_IF_FAILED(cmd, name) \
134 do {\
135 HRESULT result = cmd;\
136 if (FAILED(result)) {;\
137 LOG(LOG_LEVEL_ERROR) << MOD_NAME << name << ": " << bmd_hresult_to_string(result) << "\n";\
138 goto error;\
139 }\
140 } while (0)
141
142// similar as above, but only displays warning
143#define CALL_AND_CHECK_2(cmd, name) \
144 do {\
145 HRESULT result = cmd;\
146 if (FAILED(result)) {;\
147 LOG(LOG_LEVEL_WARNING) << MOD_NAME << name << ": " << bmd_hresult_to_string(result) << "\n";\
148 }\
149 } while (0)
150
151#define CALL_AND_CHECK_3(cmd, name, msg) \
152 do {\
153 HRESULT result = cmd;\
154 if (FAILED(result)) {;\
155 LOG(LOG_LEVEL_WARNING) << MOD_NAME << name << ": " << bmd_hresult_to_string(result) << "\n";\
156 } else {\
157 LOG(LOG_LEVEL_INFO) << MOD_NAME << name << ": " << msg << "\n";\
158 }\
159 } while (0)
160
161#define GET_3TH_ARG(arg1, arg2, arg3, ...) arg3
162#define CALL_AND_CHECK_CHOOSER(...) \
163 GET_3TH_ARG(__VA_ARGS__, CALL_AND_CHECK_3, CALL_AND_CHECK_2, )
164
165#define CALL_AND_CHECK(cmd, ...) CALL_AND_CHECK_CHOOSER(__VA_ARGS__)(cmd, __VA_ARGS__)
166
167class VideoDelegate;
168
170 IDeckLink *deckLink = nullptr;
171 IDeckLinkInput *deckLinkInput = nullptr;
172 unique_ptr<VideoDelegate> delegate;
173 IDeckLinkProfileAttributes *deckLinkAttributes = nullptr;
174 IDeckLinkConfiguration *deckLinkConfiguration = nullptr;
175 string device_id = "0"; // either numeric value or device name
176 bool audio = false; /* wheather we process audio or not */
177 struct tile *tile = nullptr;
178 bool init(struct vidcap_decklink_state *s, struct tile *tile, BMDAudioConnection audioConnection);
180};
181
183 bool com_initialized = false;
184 vector <struct device_state> state{vector <struct device_state>(1)};
185 int devices_cnt = 1;
186 string mode;
187 unsigned int next_frame_time = 0; // avarege time between frames
188 struct video_frame *frame{nullptr};
190 queue<IDeckLinkAudioInputPacket *> audioPackets;
192 BMDVideoInputFlags enable_flags = bmdVideoInputFlagDefault;
193 BMDSupportedVideoModeFlags supported_flags = bmdSupportedVideoModeDefault;
194
195 mutex lock;
196 condition_variable boss_cv;
197
198 int frames = 0;
199 bool stereo{false}; /* for eg. DeckLink HD Extreme, Quad doesn't set this !!! */
200 bool sync_timecode{false}; /* use timecode when grabbing from multiple inputs */
201 map<BMDDeckLinkConfigurationID, bmd_option> device_options = {
202 { bmdDeckLinkConfigCapturePassThroughMode, bmd_option((int64_t) bmdDeckLinkCapturePassthroughModeDisabled, false) },
203 };
204 bool detect_format = false;
205 unsigned int requested_bit_depth = 0; // 0, bmdDetectedVideoInput8BitDepth, bmdDetectedVideoInput10BitDepth or bmdDetectedVideoInput12BitDepth
206 bool p_not_i = false;
207
209 bool nosig_send = false;
211
212 BMDTimeScale frameRateScale = 0;
213
214 void set_codec(codec_t c);
215
218 throw 1;
219 }
220 }
224};
225
226static HRESULT set_display_mode_properties(struct vidcap_decklink_state *s, struct tile *tile, IDeckLinkDisplayMode* displayMode, /* out */ BMDPixelFormat *pf);
227static void cleanup_common(struct vidcap_decklink_state *s);
228static list<tuple<int, string, string, string>> get_input_modes (IDeckLink* deckLink);
229static void print_input_modes (IDeckLink* deckLink);
230
231class VideoDelegate : public IDeckLinkInputCallback {
232private:
233 int32_t mRefCount{};
234 static constexpr BMDDetectedVideoInputFormatFlags csMask{bmdDetectedVideoInputYCbCr422 | bmdDetectedVideoInputRGB444};
235 static constexpr BMDDetectedVideoInputFormatFlags bitDepthMask{bmdDetectedVideoInput8BitDepth | bmdDetectedVideoInput10BitDepth | bmdDetectedVideoInput12BitDepth};
236 BMDDetectedVideoInputFormatFlags configuredCsBitDepth{};
237
238public:
239 int newFrameReady{}; // -1 == timeout
240 BMDTimeValue frameTime{};
241 IDeckLinkVideoFrame *rightEyeFrame{};
242 void *pixelFrame{};
244 IDeckLinkVideoInputFrame *lastFrame{nullptr};
245 uint32_t timecode{};
248
249 VideoDelegate(struct vidcap_decklink_state *state, struct device_state &device_) : s(state), device(device_) {
250 }
251
256
257 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID *) override { return E_NOINTERFACE; }
258 virtual ULONG STDMETHODCALLTYPE AddRef(void) override {
259 return mRefCount++;
260 }
261 virtual ULONG STDMETHODCALLTYPE Release(void) override {
262 int32_t newRefValue = mRefCount--;
263 if (newRefValue == 0)
264 {
265 delete this;
266 return 0;
267 }
268 return newRefValue;
269 }
270 static auto getNotificationEventsStr(BMDVideoInputFormatChangedEvents notificationEvents, BMDDetectedVideoInputFormatFlags flags) noexcept {
271 string status{};
272 map<BMDDetectedVideoInputFormatFlags, string> change_map {
273 { bmdVideoInputDisplayModeChanged, "display mode"s },
274 { bmdVideoInputFieldDominanceChanged, "field dominance"s },
275 { bmdVideoInputColorspaceChanged, "color space"s },
276 };
277 for (auto &i : change_map) {
278 if ((notificationEvents & i.first) != 0U) {
279 if (!status.empty()) {
280 status += ", "s;
281 }
282 status += i.second;
283 }
284 }
285 if (status.empty()) {
286 status = "unknown"s;
287 }
288 status += " - ";
289 map<BMDDetectedVideoInputFormatFlags, string> flag_map {
290 { bmdDetectedVideoInputYCbCr422, "YCbCr422"s },
291 { bmdDetectedVideoInputRGB444, "RGB444"s },
292 { bmdDetectedVideoInputDualStream3D, "DualStream3D"s },
293 { bmdDetectedVideoInput12BitDepth, "12bit"s },
294 { bmdDetectedVideoInput10BitDepth, "10bit"s },
295 { bmdDetectedVideoInput8BitDepth, "8bit"s },
296 };
297 bool first = true;
298 for (auto &i : flag_map) {
299 if ((flags & i.first) != 0U) {
300 if (!first) {
301 status += ", "s;
302 }
303 status += i.second;
304 first = false;
305 }
306 }
307 return status;
308 }
309
311 BMDVideoInputFormatChangedEvents notificationEvents,
312 IDeckLinkDisplayMode* mode,
313 BMDDetectedVideoInputFormatFlags flags) noexcept override {
315 << MOD_NAME << "Format change detected ("
316 << getNotificationEventsStr(notificationEvents, flags)
317 << ").\n";
318
319 bool detected_3d = (flags & bmdDetectedVideoInputDualStream3D) != 0U;
320 if (detected_3d != s->stereo) {
322 << MOD_NAME << "Stereoscopic 3D "
323 << (detected_3d ? "" : "not ") << "detected but "
324 << (s->stereo ? "" : "not ")
325 << "enabled! "
326 "Switching automatically. This behavior is "
327 "experimental so please report any problems. "
328 "You can also specify `3D` option explicitly.\n";
329 s->stereo = detected_3d;
330 s->enable_flags ^= bmdVideoInputDualStream3D;
331 }
332 BMDDetectedVideoInputFormatFlags csBitDepth = flags & (csMask | bitDepthMask);
333 unique_lock<mutex> lk(s->lock);
334 if (notificationEvents & bmdVideoInputColorspaceChanged && csBitDepth != configuredCsBitDepth) {
335 if ((csBitDepth & bitDepthMask) == 0U) { // if no bit depth, assume 8-bit
336 csBitDepth |= bmdDetectedVideoInput8BitDepth;
337 }
338 if (s->requested_bit_depth != 0) {
339 csBitDepth = (flags & csMask) | s->requested_bit_depth;
340 }
341 unordered_map<BMDDetectedVideoInputFormatFlags, codec_t> m = {
342 {bmdDetectedVideoInputYCbCr422 | bmdDetectedVideoInput8BitDepth, UYVY},
343 {bmdDetectedVideoInputYCbCr422 | bmdDetectedVideoInput10BitDepth, v210},
344 {bmdDetectedVideoInputYCbCr422 | bmdDetectedVideoInput12BitDepth, v210}, // weird
345 {bmdDetectedVideoInputRGB444 | bmdDetectedVideoInput8BitDepth, RGBA},
346 {bmdDetectedVideoInputRGB444 | bmdDetectedVideoInput10BitDepth, R10k},
347 {bmdDetectedVideoInputRGB444 | bmdDetectedVideoInput12BitDepth, R12L},
348 };
349 if (s->requested_bit_depth == 0 && (csBitDepth & bmdDetectedVideoInput8BitDepth) == 0) {
350 const string & depth = (flags & bmdDetectedVideoInput10BitDepth) != 0U ? "10"s : "12"s;
352 << MOD_NAME << "Detected " << depth
353 << "-bit signal, use \":codec=UYVY\" to "
354 "enforce 8-bit capture (old "
355 "behavior).\n";
356 }
357
358 s->set_codec(m.at(csBitDepth));
359 configuredCsBitDepth = csBitDepth;
360 }
361
362 if (notificationEvents & bmdVideoInputDisplayModeChanged) {
363 IDeckLinkInput *deckLinkInput = device.deckLinkInput;
364 deckLinkInput->PauseStreams();
365 BMDPixelFormat pf{};
366 if (HRESULT result = set_display_mode_properties(s, device.tile, mode, /* out */ &pf); FAILED(result)) {
367 LOG(LOG_LEVEL_ERROR) << MOD_NAME << "set_display_mode_properties: " << bmd_hresult_to_string(result) << "\n";
368 return result;
369 }
370 CALL_AND_CHECK(deckLinkInput->EnableVideoInput(mode->GetDisplayMode(), pf, s->enable_flags), "EnableVideoInput");
371 deckLinkInput->FlushStreams();
372 deckLinkInput->StartStreams();
373 }
374
375 return S_OK;
376 }
377 virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(IDeckLinkVideoInputFrame*, IDeckLinkAudioInputPacket*) override;
378};
379
380HRESULT
381VideoDelegate::VideoInputFrameArrived (IDeckLinkVideoInputFrame *videoFrame, IDeckLinkAudioInputPacket *audioPacket)
382{
383 bool nosig = false;
384
385 unique_lock<mutex> lk(s->lock);
386// LOCK - LOCK - LOCK - LOCK - LOCK - LOCK - LOCK - LOCK - LOCK - LOCK - LOCK //
387
388 // Video
389 if (videoFrame)
390 {
391 if (videoFrame->GetFlags() & bmdFrameHasNoInputSource) {
392 nosig = true;
393 log_msg(LOG_LEVEL_INFO, "Frame received (#%d) - No input signal detected\n", s->frames);
394 newFrameReady = s->nosig_send ? 1 : -1;
395 } else {
396 newFrameReady = 1; // The new frame is ready to grab
397 // printf("Frame received (#%lu) - Valid Frame (Size: %li bytes)\n", framecount, videoFrame->GetRowBytes() * videoFrame->GetHeight());
398 }
399
400 BMDTimeValue unused_duration = 0;
401 videoFrame->GetStreamTime(&frameTime, &unused_duration,
403 }
404
405 if (audioPacket && !nosig) {
406 if (s->audioPackets.size() < MAX_AUDIO_PACKETS) {
407 audioPacket->AddRef();
408 s->audioPackets.push(audioPacket);
409 } else {
410 LOG(LOG_LEVEL_WARNING) << MOD_NAME "Dropping audio packet, queue full.\n";
411 }
412 }
413
414 if (videoFrame && newFrameReady == 1 && (!nosig || !lastFrame)) {
416 videoFrame->GetBytes(&pixelFrame);
417
419 lastFrame = videoFrame;
420 lastFrame->AddRef();
421
422 IDeckLinkTimecode *tc = NULL;
423 if (videoFrame->GetTimecode(bmdTimecodeRP188Any, &tc) == S_OK) {
424 timecode = tc->GetBCD();
425 tc->Release();
426 } else {
427 timecode = 0;
428 if (s->sync_timecode) {
429 log_msg(LOG_LEVEL_ERROR, "Failed to acquire timecode from stream. Disabling sync.\n");
430 s->sync_timecode = false;
431 }
432 }
433
435 pixelFrameRight = NULL;
436
437 if(s->stereo) {
438 IDeckLinkVideoFrame3DExtensions *rightEye;
439 HRESULT result;
440 result = videoFrame->QueryInterface(IID_IDeckLinkVideoFrame3DExtensions, (void **)&rightEye);
441
442 if (result == S_OK) {
443 result = rightEye->GetFrameForRightEye(&rightEyeFrame);
444
445 if(result == S_OK) {
446 if (rightEyeFrame->GetFlags() & bmdFrameHasNoInputSource)
447 {
448 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Right Eye Frame received (#%d) - No input signal detected\n", s->frames);
449 }
450 rightEyeFrame->GetBytes(&pixelFrameRight);
451 }
452 }
453 rightEye->Release();
454 if(!pixelFrameRight) {
455 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Sending right eye error.\n");
456 }
457 }
458 }
459
460 lk.unlock();
461 s->boss_cv.notify_one();
462
463// UNLOCK - UNLOCK - UNLOCK - UNLOCK - UNLOCK - UNLOCK - UNLOCK - UNLOCK - UN //
464
465 return S_OK;
466}
467
469static void
470vidcap_decklink_print_card_info(IDeckLink *deckLink, const char *query_prop_fcc)
471{
472
473 // ** List the video input display modes supported by the card
475
476 IDeckLinkProfileAttributes *deckLinkAttributes = nullptr;
477
478 HRESULT result = deckLink->QueryInterface(IID_IDeckLinkProfileAttributes, (void**)&deckLinkAttributes);
479 if (result != S_OK) {
480 cout << "Could not query device attributes.\n\n";
481 return;
482 }
483
484 IDeckLinkInput *deckLinkInput = nullptr;
485 color_printf("\n\tsupported pixel formats:" TERM_BOLD);
486 if ((deckLink->QueryInterface(IID_IDeckLinkInput,
487 (void **) &deckLinkInput)) == S_OK) {
488 for (auto &c : uv_to_bmd_codec_map) {
489 if (decklink_supports_codec(deckLinkInput, c.second)) {
490 printf(" %s", get_codec_name(c.first));
491 }
492 }
494 } else {
495 color_printf(TRED("(error)"));
496 }
498
499 int64_t connections = 0;
500 if (deckLinkAttributes->GetInt(BMDDeckLinkVideoInputConnections, &connections) != S_OK) {
501 LOG(LOG_LEVEL_ERROR) << MOD_NAME "Could not get connections.\n";
502 } else {
503 cout << "\n\tConnection can be one of following:\n";
504 for (auto const &it : get_connection_string_map()) {
505 if (connections & it.first) {
506 col() << "\t\t" << SBOLD(it.second) << "\n";
507 }
508 }
509 }
510 cout << "\n";
511
512 if (query_prop_fcc != nullptr) {
514 cout << "\n";
515 }
516
517 // Release the IDeckLink instance when we've finished with it to prevent leaks
519}
520
521/* HELP */
522static int
523decklink_help(bool full, const char *query_prop_fcc = nullptr)
524{
525 col() << "\nDeckLink options:\n";
526 col() << SBOLD(SRED("\t-t decklink") << ":[full]help") << " | "
527 << SBOLD(SRED("-t decklink") << ":query=<FourCC>") << "\n";
528 col() << SBOLD(SRED("\t-t decklink")
529 << "{:m[ode]=<mode>|:d[evice]=<idx|ID|name>|:c[odec]=<colorspace>...<key>=<"
530 "val>}*")
531 << "\n";
532 col() << SBOLD(SRED("\t-t decklink")
533 << "[:<device_index(indices)>[:<mode>:<colorspace>[:3D]["
534 ":sync_timecode][:connection=<input>][:detect-"
535 "format][:conversion=<conv_mode>]]")
536 << "\n";
537 col() << "\t(mode specification is mandatory if your card does not support format autodetection; syntax on the first line is recommended, the second is obsolescent)\n";
538 col() << "\n";
539
540 col() << SBOLD("3D") << "\n";
541 printf("\tUse this to capture 3D from supported card (eg. DeckLink HD 3D Extreme).\n");
542 printf("\tDo not use it for eg. Quad or Duo. Availability of the mode is indicated\n");
543 printf("\tin video format listing above by flag \"3D\".\n");
544 printf("\n");
545
546 col() << SBOLD("fullhelp") << "\n";
547 col() << "\tPrint description of all available options.\n";
548 col() << "\n";
549
550 col() << SBOLD("half-duplex | full-duplex | simplex") << "\n";
551 col() << "\tSet a profile that allows maximal number of simultaneous "
552 "IOs / set device to better compatibility (3D, dual-link) / "
553 "use all connectors as single input.\n";
554 col() << "\n";
555
556 col() << SBOLD("[no]passthrough[=keep]") << "\n";
557 col() << "\tDisables/enables/keeps capture passthrough (default is "
558 "disable).\n";
559 col() << "\n";
560
561 if (full) {
562 col() << SBOLD("conversion") << "\n";
563 col() << SBOLD("\tnone") << " - No video input conversion\n";
564 col() << SBOLD("\t10lb") << " - HD1080 to SD video input down conversion\n";
565 col() << SBOLD("\t10am") << " - Anamorphic from HD1080 to SD video input down conversion\n";
566 col() << SBOLD("\t72lb") << " - Letter box from HD720 to SD video input down conversion\n";
567 col() << SBOLD("\t72ab") << " - Letterbox video input up conversion\n";
568 col() << SBOLD("\tamup") << " - Anamorphic video input up conversion\n";
569 col() << "\tThen use the set the resulting mode (!) for capture, eg. for 1080p to PAL conversion:\n"
570 "\t\t-t decklink:mode=pal:conversion=10lb\n";
571 col() << "\n";
572
573 col() << SBOLD("query=<FourCC>") << "\n";
574 col() << "\tQueries device attribute, eg. `decklink:q=mach` to "
575 "see max embed. channels).\n";
576 col() << "\n";
577
578 col() << SBOLD("p_not_i") << "\n";
579 col() << "\tIncoming signal should be treated as progressive even if detected as interlaced (PsF).\n";
580 col() << "\n";
581
582 col() << SBOLD("Use1080PsF[=true|false]") << "\n";
583 col() << "\tIncoming signal should be treated as PsF instead of progressive.\n";
584 col() << "\n";
585
586 col() << SBOLD("nosig-send") << "\n";
587 col() << "\tSend video even if no signal was detected (useful when video interrupts\n"
588 "\tbut the video stream needs to be preserved, eg. to keep sync with audio).\n";
589 col() << "\n";
590
591 col() << SBOLD("detect-format") << "\n";
592 col() << "\tTry to detect input video format even if the "
593 "device doesn't support\n"
594 "\tautodetect, eg. \"-t "
595 "decklink:connection=HDMI:detect-format\".\n";
596 col() << "\n";
597
598 col() << SBOLD("profile=<FourCC>") << " - use desired device profile:\n";
600 col() << "\n";
601 col() << SBOLD("sync_timecode") << "\n";
602 col() << "\tTry to synchronize inputs based on timecode (for multiple inputs, eg. tiled 4K)\n";
603 col() << "\n";
604 col() << SBOLD("keep-settings") << "\n\tdo not apply any DeckLink settings by UG than required (keep user-selected defaults)\n";
605 col() << "\n";
606 col() << SBOLD("<option_FourCC>=<value>") << " - arbitrary BMD option (given a FourCC) and corresponding value, i.a.:\n";
607 col() << SBOLD("\taacl[=no]")
608 << "\t\tset analog audio levels to maximum gain "
609 "(consumer audio level)\n";
610 col() << "\n";
611 } else {
612 col() << "(other options available, use \"" << SBOLD("fullhelp") << "\" to see complete list of options)\n\n";
613 }
614
615 col() << "Available color spaces:";
616 for (auto & i : uv_to_bmd_codec_map) {
617 if (i != *uv_to_bmd_codec_map.begin()) {
618 col() << ",";
619 }
620
621 col() << " " << SBOLD(get_codec_name(i.first));
622 }
623 cout << "\n";
624 if (!full) {
625 col() << "Possible connections: " << TBOLD("SDI") << ", " << TBOLD("HDMI") << ", " << TBOLD("OpticalSDI") << ", " << TBOLD("Component") << ", " << TBOLD("Composite") << ", " << TBOLD("SVideo") << "\n";
626 }
627 cout << "\n";
628
629 // Create an IDeckLinkIterator object to enumerate all DeckLink cards in the system
630 bool com_initialized = false;
631
632 cout << "Devices (idx, topological ID, name):\n";
633 // Enumerate all cards in this system
634 int numDevices = 0;
635 const bool natural_sort =
637 for (auto &d :
638 bmd_get_sorted_devices(&com_initialized, true, natural_sort)) {
639 IDeckLink *deckLink = get<0>(d).get();
640 string deviceName = bmd_get_device_name(deckLink);
641 if (deviceName.empty()) {
642 deviceName = "(unable to get name)";
643 }
644
645 char index[STR_LEN] = "";
646 if (!natural_sort) {
647 snprintf(index, sizeof index,
648 TBOLD("%c") ") ", get<char>(d));
649 }
650 if (full || natural_sort) {
651 snprintf(index + strlen(index),
652 sizeof index - strlen(index), TBOLD("%d") ") ",
653 get<int>(d));
654 }
655 // *** Print the model name of the DeckLink card
656 color_printf("\t%s" TBOLD("%6x") ") " TBOLD(
657 TGREEN("%s")) "\n",
658 index, get<unsigned>(d),
659 deviceName.c_str());
660
661 // Increment the total number of DeckLink cards found
662 numDevices++;
663
664 if (full) {
666 }
667 }
668 if (!full) {
669 col() << "\n(use \"-t decklink:"
670 << SBOLD(
671 "fullhelp") "\" to see full list of device modes "
672 "and available connections)\n\n";
673 }
674
675 decklink_uninitialize(&com_initialized);
676
677 // If no DeckLink cards were found in the system, inform the user
678 if (numDevices == 0)
679 {
680 log_msg(LOG_LEVEL_ERROR, "No Blackmagic Design devices were found.\n");
681 }
682
683 printf("Examples:\n");
684 col() << "\t" << SBOLD(uv_argv[0] << " -t decklink")
685 << " # captures autodetected video from first DeckLink (index 0) "
686 "in system\n";
687 col() << "\t" << SBOLD(uv_argv[0] << " -t decklink:d=b:m=Hp30:c=v210")
688 << " # specify mode for 2nd device which doesn't have "
689 "autodetection\n";
690 col() << "\t"
691 << SBOLD(uv_argv[0]
692 << " -t decklink:d=\"DeckLink 8K Pro (1)\":profile=1dfd")
693 << " # capture from 8K Pro and set profile to 1-subdevice "
694 "full-duplex (useful for 3D capture)\n";
695
696 printf("\n");
697
699
700 printf("\n");
701
702 return 1;
703}
704
705/* SETTINGS */
706static void parse_devices(struct vidcap_decklink_state *s, const char *devs)
707{
708 assert(devs != NULL && strlen(devs) > 0);
709 char *devices = strdup(devs);
710 char *ptr;
711 char *save_ptr_dev;
712 char *tmp = devices;
713
714 s->devices_cnt = 0;
715 while ((ptr = strtok_r(tmp, ",", &save_ptr_dev))) {
716 s->devices_cnt += 1;
717 s->state.resize(s->devices_cnt);
718 s->state[s->devices_cnt - 1].device_id = ptr;
719 tmp = NULL;
720 }
721 free (devices);
722}
723
724/* Parses option in format key=value */
725static bool parse_option(struct vidcap_decklink_state *s, const char *opt)
726{
727 const char *val = strchr(opt, '=') + 1;
728 if(strcasecmp(opt, "3D") == 0) {
729 s->stereo = true;
730 } else if(strcasecmp(opt, "timecode") == 0) {
731 s->sync_timecode = true;
732 } else if (IS_KEY_PREFIX(opt, "codec")) {
733 const char *codec = strchr(opt, '=') + 1;
735 if(s->codec == VIDEO_CODEC_NONE) {
736 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Wrong config. Unknown color space %s\n", codec);
737 return false;
738 }
739 } else if (IS_KEY_PREFIX(opt, "connection")) {
740 const char *connection = strchr(opt, '=') + 1;
741 for (auto const & it : get_connection_string_map()) {
742 if (strcasecmp(connection, it.second.c_str()) == 0) {
743 s->device_options[bmdDeckLinkConfigVideoInputConnection] = bmd_option((int64_t) it.first);
744 }
745 }
746 if (s->device_options.find(bmdDeckLinkConfigVideoInputConnection) == s->device_options.end()) {
747 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unrecognized connection %s.\n", connection);
748 return false;
749 }
750 } else if(strncasecmp(opt, "audio_level=",
751 strlen("audio_level=")) == 0) {
752 s->device_options[bmdDeckLinkConfigAnalogAudioConsumerLevels] =
754 } else if (IS_KEY_PREFIX(opt, "conversion")) {
755 s->device_options[bmdDeckLinkConfigVideoInputConversionMode].parse(strchr(opt, '='));
756 } else if (IS_KEY_PREFIX(opt, "device")) {
757 parse_devices(s, strchr(opt, '=') + 1);
758 } else if (IS_KEY_PREFIX(opt, "mode")) {
759 s->mode = strchr(opt, '=') + 1;
760 } else if (IS_KEY_PREFIX(opt, "profile")) {
761 s->profile.parse(strchr(opt, '=') + 1);
762 } else if (strcasecmp(opt, "detect-format") == 0) {
763 s->detect_format = true;
764 } else if (strcasecmp(opt, "p_not_i") == 0) {
765 s->p_not_i = true;
766 } else if (strstr(opt, "Use1080PsF") != nullptr) {
767 s->device_options[bmdDeckLinkConfigCapture1080pAsPsF].set_flag(strchr(opt, '=') == nullptr || strcasecmp(strchr(opt, '='), "false") != 0);
768 } else if (strncasecmp(opt, "passthrough", 4) == 0 ||
769 strncasecmp(opt, "nopassthrough", 6) == 0) {
770 if (strstr(opt, "keep")) {
771 s->device_options.erase(bmdDeckLinkConfigCapturePassThroughMode);
772 } else {
773 s->device_options[bmdDeckLinkConfigCapturePassThroughMode] = bmd_option((int64_t) (opt[0] == 'n' ? bmdDeckLinkCapturePassthroughModeDisabled
774 : bmdDeckLinkCapturePassthroughModeCleanSwitch));
775 }
776 } else if (strstr(opt, "full-duplex") == opt) {
777 s->profile.set_int(bmdProfileOneSubDeviceFullDuplex);
778 } else if (strstr(opt, "half-duplex") == opt) {
779 s->profile.set_int(bmdDuplexHalf);
780 } else if (strstr(opt, "simplex") == opt) {
781 s->profile.set_int(bmdProfileOneSubDeviceHalfDuplex);
782 } else if (strcasecmp(opt, "nosig-send") == 0) {
783 s->nosig_send = true;
784 } else if (strstr(opt, "keep-settings") == opt) {
785 s->keep_device_defaults = true;
786 } else if ((strchr(opt, '=') != nullptr && strchr(opt, '=') - opt == 4) || strlen(opt) == 4) {
787 s->device_options[(BMDDeckLinkConfigurationID) bmd_read_fourcc(opt)].parse(strchr(opt, '=') + 1);
788 } else {
789 log_msg(LOG_LEVEL_ERROR, MOD_NAME "unknown option in init string: %s\n", opt);
790 return false;
791 }
792
793 return true;
794}
795
796static bool settings_init_key_val(struct vidcap_decklink_state *s, char **save_ptr)
797{
798 char *tmp;
799
800 while((tmp = strtok_r(NULL, ":", save_ptr))) {
801 if (!parse_option(s, tmp)) {
802 return false;
803 }
804 }
805
806 return true;
807}
808
809static bool
811{
812 char *tmp;
813 char *save_ptr = NULL;
814
815 if (!fmt || (tmp = strtok_r(fmt, ":", &save_ptr)) == NULL) {
816 MSG(INFO, "Auto-choosen device 0.\n");
817 return true;
818 }
819
820 // options are in format <device>:<mode>:<codec>[:other_opts]
821 if (isdigit(tmp[0]) && strcasecmp(tmp, "3D") != 0) {
823 << MOD_NAME "Deprecated syntax used, please use options in "
824 "format \"key=value\"\n";
825 // choose device
826 parse_devices(s, tmp);
827
828 // choose mode
829 tmp = strtok_r(NULL, ":", &save_ptr);
830 if(tmp) {
831 s->mode = tmp;
832
833 tmp = strtok_r(NULL, ":", &save_ptr);
834 if (tmp) {
836 if(s->codec == VIDEO_CODEC_NONE) {
837 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Wrong config. Unknown color space %s\n", tmp);
838 return false;
839 }
840 }
841 if (!settings_init_key_val(s, &save_ptr)) {
842 return false;
843 }
844 }
845 } else { // options are in format key=val
846 if (!parse_option(s, tmp)) {
847 return false;
848 }
849 if (!settings_init_key_val(s, &save_ptr)) {
850 return false;
851 }
852 }
853
854 return true;
855}
856
857/* External API ***************************************************************/
858
859static void vidcap_decklink_probe(device_info **available_cards, int *card_count, void (**deleter)(void *))
860{
861 device_info *cards = nullptr;
862 *card_count = 0;
863 *deleter = free;
864
865 // Create an IDeckLinkIterator object to enumerate all DeckLink cards in the system
866 bool com_initialized = false;
867
868 // Enumerate all cards in this system
869 for (auto &d : bmd_get_sorted_devices(&com_initialized, false)) {
870 IDeckLink *deckLink = get<0>(d).get();
871 HRESULT result;
872 IDeckLinkProfileAttributes *deckLinkAttributes;
873
874 result = deckLink->QueryInterface(IID_IDeckLinkProfileAttributes, (void**)&deckLinkAttributes);
875 if (result != S_OK) {
876 continue;
877 }
878 int64_t connections_bitmap;
879 if(deckLinkAttributes->GetInt(BMDDeckLinkVideoInputConnections, &connections_bitmap) != S_OK) {
880 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Could not get connections, skipping device.\n");
881 continue;
882 }
883
884 string deviceName = bmd_get_device_name(deckLink);
885 if (deviceName.empty()) {
886 deviceName = "(unknown)";
887 }
888
889 *card_count += 1;
890 cards = (struct device_info *)
891 realloc(cards, *card_count * sizeof(struct device_info));
892 memset(&cards[*card_count - 1], 0, sizeof(struct device_info));
893 snprintf(cards[*card_count - 1].dev, sizeof cards[*card_count - 1].dev,
894 ":device=%6x", get<unsigned>(d));
895 snprintf(cards[*card_count - 1].name, sizeof cards[*card_count - 1].name,
896 "%s", deviceName.c_str());
897 snprintf(cards[*card_count - 1].extra, sizeof cards[*card_count - 1].extra,
898 "\"embeddedAudioAvailable\":\"t\"");
899
900
901 list<string> connections;
902 for (auto const &it : get_connection_string_map()) {
903 if (connections_bitmap & it.first) {
904 connections.push_back(it.second);
905 }
906 }
907 list<tuple<int, string, string, string>> modes = get_input_modes (deckLink);
908
909 int i = 0;
910 const int mode_count = sizeof cards[*card_count - 1].modes /
911 sizeof cards[*card_count - 1].modes[0];
912
913 for (auto &c : connections) {
914 if (i >= mode_count) {
915 break;
916 }
917 snprintf(cards[*card_count - 1].modes[i].id,
918 sizeof cards[*card_count - 1].modes[i].id,
919 R"({"modeOpt":"connection=%s:codec=UYVY"})",
920 c.c_str());
921 snprintf(cards[*card_count - 1].modes[i].name,
922 sizeof cards[*card_count - 1].modes[i].name,
923 "DeckLink Auto (%s)", c.c_str());
924 if (++i >= mode_count) {
925 break;
926 }
927 snprintf(cards[*card_count - 1].modes[i].id,
928 sizeof cards[*card_count - 1].modes[i].id,
929 R"({"modeOpt":"detect-format:connection=%s:codec=UYVY"})",
930 c.c_str());
931 snprintf(cards[*card_count - 1].modes[i].name,
932 sizeof cards[*card_count - 1].modes[i].name,
933 "UltraGrid auto-detect (%s) (SLOW)", c.c_str());
934 i++;
935 }
936
937 for (auto &m : modes) {
938 for (auto &c : connections) {
939 if (i >= mode_count) { // no space
940 break;
941 }
942
943 snprintf(cards[*card_count - 1].modes[i].id,
944 sizeof cards[*card_count - 1].modes[i].id,
945 R"({"modeOpt":"connection=%s:mode=%s:codec=UYVY"})",
946 c.c_str(), get<1>(m).c_str());
947 snprintf(cards[*card_count - 1].modes[i].name,
948 sizeof cards[*card_count - 1].modes[i].name,
949 "%s (%s)", get<2>(m).c_str(), c.c_str());
950 i++;
951 }
952 }
953
954 dev_add_option(&cards[*card_count - 1], "3D", "3D", "3D", ":3D", true);
955 dev_add_option(&cards[*card_count - 1], "Profile", "Duplex profile can be one of: 1dhd, 2dhd, 2dfd, 4dhd, keep", "profile", ":profile=", false);
956
957 RELEASE_IF_NOT_NULL(deckLinkAttributes);
958 }
959
960 decklink_uninitialize(&com_initialized);
961 *available_cards = cards;
962}
963
964static HRESULT set_display_mode_properties(struct vidcap_decklink_state *s, struct tile *tile, IDeckLinkDisplayMode* displayMode, /* out */ BMDPixelFormat *pf)
965{
966 BMD_STR displayModeBMDString = nullptr;
967
968 if (HRESULT result = displayMode->GetName(&displayModeBMDString); FAILED(result)) {
969 LOG(LOG_LEVEL_ERROR) << MOD_NAME << "IDeckLinkDisplayMode::GetName failed: " << bmd_hresult_to_string(result) << "\n";
970 }
971 string displayModeString = get_str_from_bmd_api_str(displayModeBMDString);
972 release_bmd_api_str(displayModeBMDString);
973
974 auto it = std::find_if(uv_to_bmd_codec_map.begin(),
976 [&s](const std::pair<codec_t, BMDPixelFormat>& el){ return el.first == s->codec; });
977 if (it == uv_to_bmd_codec_map.end()) {
978 LOG(LOG_LEVEL_ERROR) << "Unsupported codec: " << get_codec_name(s->codec) << "!\n";
979 return E_FAIL;
980 }
981 *pf = it->second;
982
983 // get avarage time between frames
984 BMDTimeValue frameRateDuration = 0;
985
986 tile->width = displayMode->GetWidth();
987 tile->height = displayMode->GetHeight();
988 s->frame->color_spec = s->codec;
989
990 displayMode->GetFrameRate(&frameRateDuration, &s->frameRateScale);
991 s->frame->fps = static_cast<double>(s->frameRateScale) / frameRateDuration;
992 s->next_frame_time = static_cast<int>(std::chrono::microseconds::period::den / s->frame->fps); // in microseconds
993 switch(displayMode->GetFieldDominance()) {
994 case bmdLowerFieldFirst:
996 MOD_NAME "Lower field first format detected, "
997 "fields can be switched! ");
998 // fall through
999 case bmdUpperFieldFirst:
1001 break;
1002 case bmdProgressiveFrame:
1003 case bmdProgressiveSegmentedFrame:
1005 break;
1006 case bmdUnknownFieldDominance:
1007 LOG(LOG_LEVEL_WARNING) << MOD_NAME "Unknown field dominance!\n";
1009 break;
1010 }
1011
1012 if (s->p_not_i) {
1014 }
1015
1016 LOG(LOG_LEVEL_DEBUG) << MOD_NAME << displayModeString << " \t " << tile->width << " x " << tile->height << " \t " <<
1017 s->frame->fps << " FPS \t " << s->next_frame_time << " AVAREGE TIME BETWEEN FRAMES\n";
1018 LOG(LOG_LEVEL_NOTICE) << MOD_NAME "Enable video input: " << displayModeString << ((s->enable_flags & bmdVideoInputDualStream3D) != 0U ? " (stereoscopic 3D)" : "") << "\n";
1019
1020 tile->data_len =
1022
1023 if(s->stereo) {
1024 s->frame->tile_count = 2;
1025 s->frame->tiles[1].width = s->frame->tiles[0].width;
1026 s->frame->tiles[1].height = s->frame->tiles[0].height;
1027 s->frame->tiles[1].data_len = s->frame->tiles[0].data_len;
1028 } else {
1029 s->frame->tile_count = s->devices_cnt;
1030 }
1031
1032 return S_OK;
1033}
1034
1039static bool detect_format(struct vidcap_decklink_state *s, BMDDisplayMode *outDisplayMode, struct device_state *device)
1040{
1041 IDeckLinkDisplayMode *displayMode;
1042 HRESULT result;
1043 IDeckLinkDisplayModeIterator* displayModeIterator = NULL;
1044 result = device->deckLinkInput->GetDisplayModeIterator(&displayModeIterator);
1045 if (result != S_OK) {
1046 return false;
1047 }
1048
1049 vector<BMDPixelFormat> pfs = {bmdFormat8BitYUV, bmdFormat8BitBGRA};
1050
1051 while (displayModeIterator->Next(&displayMode) == S_OK) {
1052 for (BMDPixelFormat pf : pfs) {
1053 uint32_t mode = ntohl(displayMode->GetDisplayMode());
1054 log_msg(LOG_LEVEL_NOTICE, "DeckLink: trying mode %.4s, pixel format %.4s\n", (const char *) &mode, (const char *) &pf);
1055 result = device->deckLinkInput->EnableVideoInput(displayMode->GetDisplayMode(), pf, 0);
1056 if (result == S_OK) {
1057 device->deckLinkInput->StartStreams();
1058 unique_lock<mutex> lk(s->lock);
1059 s->boss_cv.wait_for(
1060 lk, milliseconds(1200), [device] {
1061 return device->delegate
1062 ->newFrameReady == 1;
1063 });
1064 lk.unlock();
1065 device->deckLinkInput->StopStreams();
1066 device->deckLinkInput->DisableVideoInput();
1067
1068 if (device->delegate->newFrameReady == 1) {
1069 *outDisplayMode = displayMode->GetDisplayMode();
1070 // set also detected codec (!)
1071 s->set_codec(pf == bmdFormat8BitYUV ? UYVY : RGBA);
1072 displayMode->Release();
1073 displayModeIterator->Release();
1074 return true;
1075 }
1076 }
1077 }
1078
1079 displayMode->Release();
1080 }
1081
1082 displayModeIterator->Release();
1083 return false;
1084}
1085
1086static bool decklink_cap_configure_audio(struct vidcap_decklink_state *s, unsigned int audio_src_flag, BMDAudioConnection *audioConnection) {
1087 if (audio_src_flag == 0U) {
1088 return true;
1089 }
1090
1091 s->state[0].audio = true;
1092 switch (audio_src_flag) {
1094 *audioConnection = bmdAudioConnectionEmbedded;
1095 break;
1097 *audioConnection = bmdAudioConnectionAESEBU;
1098 break;
1100 *audioConnection = bmdAudioConnectionAnalog;
1101 break;
1102 default:
1103 LOG(LOG_LEVEL_FATAL) << MOD_NAME << "Unexpected audio flag " << audio_src_flag << " encountered.\n";
1104 abort();
1105 }
1107 if (s->audio.bps != 2 && s->audio.bps != 4) {
1109 << MOD_NAME << "Unsupported audio Bps "
1111 << "! Supported is 2 or 4 bytes only!\n";
1112 return false;
1113 }
1114 if (audio_capture_sample_rate != 0 && audio_capture_sample_rate != bmdAudioSampleRate48kHz) {
1115 LOG(LOG_LEVEL_ERROR) << MOD_NAME "Unsupported sample rate " << audio_capture_sample_rate << "! Only " << bmdAudioSampleRate48kHz << " is supported.\n";
1116 return false;
1117 }
1118 s->audio.sample_rate = bmdAudioSampleRate48kHz;
1120 s->audio.max_size = (s->audio.sample_rate / 10) * s->audio.ch_count * s->audio.bps;
1121 s->audio.data = (char *) malloc(s->audio.max_size);
1122 s->audio.flags |= TIMESTAMP_VALID;
1123
1124 return true;
1125}
1126
1133
1134#define INIT_ERR() do { RELEASE_IF_NOT_NULL(displayMode); RELEASE_IF_NOT_NULL(displayModeIterator); return false; } while (0)
1135bool device_state::init(struct vidcap_decklink_state *s, struct tile *t, BMDAudioConnection audioConnection)
1136{
1137 IDeckLinkDisplayModeIterator* displayModeIterator = NULL;
1138 IDeckLinkDisplayMode* displayMode = NULL;
1139
1140 tile = t;
1141
1142 bool com_initialized = false;
1143 for (auto &d : bmd_get_sorted_devices(&com_initialized, true)) {
1144 deckLink = get<0>(d).release(); // we must release manually!
1145 string deviceName = bmd_get_device_name(deckLink);
1146 if (!deviceName.empty() && deviceName == device_id.c_str()) {
1147 break;
1148 }
1149
1150 // topological ID
1151 const unsigned long tid =
1152 strtoul(device_id.c_str(), nullptr, 16);
1153 if (get<unsigned>(d) == tid) {
1154 break;
1155 }
1156 // natural (old) index
1157 if (isdigit(device_id.c_str()[0])) {
1158 if (atoi(device_id.c_str()) == get<int>(d)) {
1159 break;
1160 }
1161 }
1162 // new (character) index
1163 if (device_id.length() == 1 && device_id[0] >= 'a') {
1164 if (device_id[0] == get<char>(d)) {
1165 break;
1166 }
1167 }
1168
1169 // Release the IDeckLink instance when we've finished with it to prevent leaks
1170 deckLink->Release();
1171 deckLink = NULL;
1172 }
1173
1174 if (deckLink == nullptr) {
1175 LOG(LOG_LEVEL_ERROR) << MOD_NAME "Device " << device_id << " was not found.\n";
1176 INIT_ERR();
1177 }
1178
1179 // Print the model name of the DeckLink card
1180 string deviceName = bmd_get_device_name(deckLink);
1181 if (!deviceName.empty()) {
1182 LOG(LOG_LEVEL_INFO) << MOD_NAME "Using device " << deviceName << "\n";
1183 }
1184
1185 if (!s->keep_device_defaults && !s->profile.keep()) {
1187 INIT_ERR();
1188 }
1189 }
1190
1191 // Query the DeckLink for its configuration interface
1192 BMD_CHECK(deckLink->QueryInterface(IID_IDeckLinkInput, (void**)&deckLinkInput), "Could not obtain the IDeckLinkInput interface", INIT_ERR());
1193
1194 // Query the DeckLink for its configuration interface
1195 BMD_CHECK(deckLinkInput->QueryInterface(IID_IDeckLinkConfiguration, (void**)&deckLinkConfiguration), "Could not obtain the IDeckLinkConfiguration interface", INIT_ERR());
1196
1197 IDeckLinkProfileAttributes *deckLinkAttributes;
1198 BMD_CHECK(deckLinkInput->QueryInterface(IID_IDeckLinkProfileAttributes, (void**)&deckLinkAttributes), "Could not query device attributes", INIT_ERR());
1199
1200 for (const auto &o : s->device_options) {
1201 if (s->keep_device_defaults && !o.second.is_user_set()) {
1202 continue;
1203 }
1204 if (!o.second.device_write(deckLinkConfiguration, o.first, MOD_NAME)) {
1205 INIT_ERR();
1206 }
1207 }
1208 const BMDVideoInputConversionMode supported_conversion_mode = s->device_options.find(bmdDeckLinkConfigVideoInputConversionMode) != s->device_options.end() ? (BMDVideoInputConversionMode) s->device_options.at(bmdDeckLinkConfigVideoInputConversionMode).get_int() : (BMDVideoInputConversionMode) bmdNoVideoInputConversion;
1209 if (s->stereo) {
1210 CALL_AND_CHECK(deckLinkConfiguration->SetFlag(bmdDeckLinkConfigSDIInput3DPayloadOverride, true), "Unable to set 3D payload override");
1211 }
1212
1213 // set Callback which returns frames
1214 delegate = make_unique<VideoDelegate>(s, *this);
1215 deckLinkInput->SetCallback(delegate.get());
1216
1217 BMDDisplayMode detectedDisplayMode = bmdModeUnknown;
1218 if (s->detect_format) {
1219 if (!detect_format(s, &detectedDisplayMode, this)) {
1220 LOG(LOG_LEVEL_WARNING) << "Signal could have not been detected!\n";
1221 INIT_ERR();
1222 }
1223 }
1224
1225 // Obtain an IDeckLinkDisplayModeIterator to enumerate the display modes supported on input
1226 BMD_CHECK(deckLinkInput->GetDisplayModeIterator(&displayModeIterator),
1227 "Could not obtain the video input display mode iterator:", INIT_ERR());
1228
1230
1231 int mnum = 0;
1232 enum { MODE_SPEC_AUTODETECT = -1, MODE_SPEC_FOURCC = -2, MODE_SPEC_DETECTED = -3 };
1233 int mode_idx = MODE_SPEC_AUTODETECT;
1234
1235 // mode selected manually - either by index or FourCC
1236 if (s->mode.length() > 0) {
1237 if (s->mode.length() <= 2) {
1238 mode_idx = atoi(s->mode.c_str());
1239 } else {
1240 mode_idx = MODE_SPEC_FOURCC;
1241 }
1242 }
1243 if (s->detect_format) { // format already detected manually
1244 mode_idx = MODE_SPEC_DETECTED;
1245 }
1246
1247 while (displayModeIterator->Next(&displayMode) == S_OK) {
1248 if (mode_idx == MODE_SPEC_DETECTED) { // format already detected manually
1249 if (detectedDisplayMode == displayMode->GetDisplayMode()) {
1250 break;
1251 } else {
1252 displayMode->Release();
1253 }
1254 } else if (mode_idx == MODE_SPEC_AUTODETECT) { // autodetect, pick first eligible mode and let device autodetect
1255 if ((s->stereo && (displayMode->GetFlags() & bmdDisplayModeSupports3D) == 0u) || displayMode->GetFieldDominance() == bmdLowerFieldFirst) {
1256 displayMode->Release();
1257 continue;
1258 }
1259 auto it = std::find_if(uv_to_bmd_codec_map.begin(),
1260 uv_to_bmd_codec_map.end(),
1261 [&s](const std::pair<codec_t, BMDPixelFormat>& el){ return el.first == s->codec; });
1262 if (it == uv_to_bmd_codec_map.end()) {
1263 LOG(LOG_LEVEL_ERROR) << "Unsupported codec: " << get_codec_name(s->codec) << "!\n";
1264 INIT_ERR();
1265 }
1266 BMDPixelFormat pf = it->second;
1267 BMD_BOOL supported = 0;
1268 const BMDVideoConnection connection = s->device_options.find(bmdDeckLinkConfigVideoInputConnection) != s->device_options.end()
1269 ? (BMDVideoConnection) s->device_options.at(bmdDeckLinkConfigVideoInputConnection).get_int() : (BMDVideoConnection) bmdVideoConnectionUnspecified;
1270 BMD_CHECK(deckLinkInput->DoesSupportVideoMode(connection, displayMode->GetDisplayMode(), pf, supported_conversion_mode, s->supported_flags, nullptr, &supported), "DoesSupportVideoMode", INIT_ERR());
1271 if (supported) {
1272 break;
1273 }
1274 } else if (mode_idx != MODE_SPEC_FOURCC) { // manually given idx
1275 if (mode_idx != mnum) {
1276 mnum++;
1277 // Release the IDeckLinkDisplayMode object to prevent a leak
1278 displayMode->Release();
1279 continue;
1280 }
1281
1282 mnum++;
1283 break;
1284 } else { // manually given FourCC
1285 if (displayMode->GetDisplayMode() == bmd_read_fourcc(s->mode.c_str())) {
1286 break;
1287 }
1288 displayMode->Release();
1289 }
1290 }
1291
1292 if (displayMode) {
1293 BMD_STR displayModeString = NULL;
1294 if (HRESULT result = displayMode->GetName(&displayModeString); result == S_OK) {
1296 << "The desired display mode is supported: "
1297 << get_str_from_bmd_api_str(displayModeString)
1298 << "\n";
1299 release_bmd_api_str(displayModeString);
1300 }
1301 } else {
1302 if (mode_idx == MODE_SPEC_FOURCC) {
1303 MSG(ERROR,
1304 "Desired mode \"%s\" is invalid or not "
1305 "supported.\n",
1306 s->mode.c_str());
1307 } else if (mode_idx >= 0) {
1308 MSG(ERROR, "Desired mode index %s is out of bounds.\n", s->mode.c_str());
1309 } else if (mode_idx == MODE_SPEC_AUTODETECT) {
1310 MSG(ERROR, "Cannot set initial format for "
1311 "autodetection - perhaps imposible "
1312 "combinations of parameters were set.\n");
1313 } else {
1314 assert("Invalid mode spec." && 0);
1315 }
1316 INIT_ERR();
1317 }
1318
1319 BMDPixelFormat pf;
1320 BMD_CHECK(set_display_mode_properties(s, tile, displayMode, &pf),
1321 "Could not set display mode properties", INIT_ERR());
1322
1323 deckLinkInput->StopStreams();
1324
1325 if (mode_idx == MODE_SPEC_AUTODETECT) {
1326 MSG(INFO, "Trying to autodetect format.\n");
1327 BMD_BOOL autodetection;
1328 BMD_CHECK(deckLinkAttributes->GetFlag(BMDDeckLinkSupportsInputFormatDetection, &autodetection), "Could not verify if device supports autodetection", INIT_ERR());
1329 if (autodetection == BMD_FALSE) {
1330 MSG(ERROR,
1331 "Device doesn't support format autodetection, you "
1332 "must set it manually or try \"-t "
1333 "decklink:detect-format[:connection=<in>]\"\n");
1334 INIT_ERR();
1335 }
1336 s->enable_flags |= bmdVideoInputEnableFormatDetection;
1337 }
1338
1339 BMD_BOOL supported = 0;
1340 const BMDVideoConnection connection = s->device_options.find(bmdDeckLinkConfigVideoInputConnection) != s->device_options.end()
1341 ? (BMDVideoConnection) s->device_options.at(bmdDeckLinkConfigVideoInputConnection).get_int() : (BMDVideoConnection) bmdVideoConnectionUnspecified;
1342 BMD_CHECK(deckLinkInput->DoesSupportVideoMode(connection, displayMode->GetDisplayMode(), pf, supported_conversion_mode, s->supported_flags, nullptr, &supported), "DoesSupportVideoMode", INIT_ERR());
1343
1344 if (!supported) {
1345 LOG(LOG_LEVEL_ERROR) << MOD_NAME "Requested display mode not supported with the selected pixel format\n";
1346 INIT_ERR();
1347 }
1348
1349 if (HRESULT result = deckLinkInput->EnableVideoInput(displayMode->GetDisplayMode(), pf, s->enable_flags); result != S_OK) {
1350 switch (result) {
1351 case E_INVALIDARG:
1352 log_msg(LOG_LEVEL_ERROR, MOD_NAME "You have required invalid video mode and pixel format combination.\n");
1353 break;
1354 case E_ACCESSDENIED:
1355 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to access the hardware or input "
1356 "stream currently active (another application using it?).\n");
1357 break;
1358 default:
1359 LOG(LOG_LEVEL_ERROR) << MOD_NAME "Could not enable video input: " << bmd_hresult_to_string(result) << "\n";
1360 }
1361 INIT_ERR();
1362 }
1363
1364 if (!audio) { //TODO: figure out output from multiple streams
1365 deckLinkInput->DisableAudioInput();
1366 } else {
1367 if (deckLinkConfiguration->SetInt(bmdDeckLinkConfigAudioInputConnection,
1368 audioConnection) == S_OK) {
1369 const map<BMDAudioConnection, string> mapping = {
1370 { bmdAudioConnectionEmbedded, "embedded" },
1371 { bmdAudioConnectionAESEBU, "AES/EBU" },
1372 { bmdAudioConnectionAnalog, "analog" },
1373 { bmdAudioConnectionAnalogXLR, "analogXLR" },
1374 { bmdAudioConnectionAnalogRCA, "analogRCA" },
1375 { bmdAudioConnectionMicrophone, "microphone" },
1376 { bmdAudioConnectionHeadphones, "headphones" },
1377 };
1378 log_msg(LOG_LEVEL_INFO, MOD_NAME "Audio input set to: %s\n", mapping.find(audioConnection) != mapping.end() ? mapping.at(audioConnection).c_str() : "unknown");
1379 } else {
1380 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to set audio input!!! Please check if it is OK. Continuing anyway.\n");
1381
1382 }
1383 if (s->audio.ch_count == 4 ||
1384 s->audio.ch_count > BMD_MAX_AUD_CH ||
1385 !is_power_of_two(s->audio.ch_count)) {
1387 MOD_NAME
1388 "DeckLink may not be able to grab %d audio "
1389 "channels. "
1390 "1, 2, 8, 16, 32 or 64 may work.\n",
1391 s->audio.ch_count);
1392 }
1393 BMD_CHECK(deckLinkInput->EnableAudioInput(
1394 bmdAudioSampleRate48kHz,
1395 s->audio.bps == 2
1396 ? bmdAudioSampleType16bitInteger
1397 : bmdAudioSampleType32bitInteger,
1398 max(s->audio.ch_count, 2)), // capture at least 2
1399 "EnableAudioInput", INIT_ERR());
1400 }
1401
1402 // Start streaming
1403 BMD_CHECK(deckLinkInput->StartStreams(), "Could not start stream", INIT_ERR());
1404
1405 displayMode->Release();
1406 displayMode = NULL;
1407
1408 displayModeIterator->Release();
1409 displayModeIterator = NULL;
1410 return true;
1411}
1412
1413static int
1415{
1416 const char *fmt = vidcap_params_get_fmt(params);
1417
1418 if (strcmp(fmt, "help") == 0 || strcmp(fmt, "fullhelp") == 0) {
1419 decklink_help(strcmp(fmt, "fullhelp") == 0);
1420 return VIDCAP_INIT_NOERR;
1421 }
1422 if (IS_KEY_PREFIX(fmt, "query")) {
1423 decklink_help(true, strchr(fmt, '=') + 1);
1424 return VIDCAP_INIT_NOERR;
1425 }
1426
1428 return VIDCAP_INIT_FAIL;
1429 }
1430
1431 struct vidcap_decklink_state *s = nullptr;
1432 try {
1433 if ((s = new vidcap_decklink_state()) == nullptr) {
1434 LOG(LOG_LEVEL_ERROR) << "Unable to allocate DeckLink state\n";
1435 return VIDCAP_INIT_FAIL;
1436 }
1437 } catch(...) {
1438 return VIDCAP_INIT_FAIL;
1439 }
1440
1441 // SET UP device and mode
1442 char *tmp_fmt = strdup(fmt);
1443 bool ret = false;
1444 try {
1445 ret = settings_init(s, tmp_fmt);
1446 } catch (exception &e) {
1447 MSG(ERROR, "%s\n", e.what());
1448 }
1449 free(tmp_fmt);
1450 if (!ret) {
1451 delete s;
1452 return VIDCAP_INIT_FAIL;
1453 }
1454
1455 switch (get_bits_per_component(s->codec)) {
1456 case 0: s->requested_bit_depth = 0; break;
1457 case 8: s->requested_bit_depth = bmdDetectedVideoInput8BitDepth; break;
1458 case 10: s->requested_bit_depth = bmdDetectedVideoInput10BitDepth; break;
1459 case 12: s->requested_bit_depth = bmdDetectedVideoInput12BitDepth; break;
1460 default: abort();
1461 }
1462 if (s->codec == VIDEO_CODEC_NONE) {
1463 s->set_codec(UYVY); // default one
1464 }
1465
1466 BMDAudioConnection audioConnection = bmdAudioConnectionEmbedded;
1468 delete s;
1469 return VIDCAP_INIT_FAIL;
1470 }
1471
1472 if(s->stereo) {
1473 s->enable_flags |= bmdVideoInputDualStream3D;
1474 s->supported_flags = (BMDSupportedVideoModeFlags) (s->supported_flags | bmdSupportedVideoModeDualStream3D);
1475 if (s->devices_cnt > 1) {
1476 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Passed more than one device while setting 3D mode. "
1477 "In this mode, only one device needs to be passed.");
1478 delete s;
1479 return VIDCAP_INIT_FAIL;
1480 }
1481 }
1482 s->frame = vf_alloc(MAX(s->devices_cnt, 2));
1483 s->frame->tile_count = s->stereo ? 2 : s->devices_cnt;
1485
1486 /* TODO: make sure that all devices are have compatible properties */
1487 for (int i = 0; i < s->devices_cnt; ++i) {
1488 if (!s->state[i].init(s, vf_get_tile(s->frame, i), audioConnection)) {
1489 cleanup_common(s);
1490 return VIDCAP_INIT_FAIL;
1491 }
1492 }
1493
1494 *state = s;
1495 return VIDCAP_INIT_OK;
1496}
1497
1499 while (!s->audioPackets.empty()) {
1500 auto *audioPacket = s->audioPackets.front();
1501 s->audioPackets.pop();
1502 audioPacket->Release();
1503 }
1504
1505 for (int i = 0; i < s->devices_cnt; ++i) {
1506 RELEASE_IF_NOT_NULL(s->state[i].deckLinkConfiguration);
1507 RELEASE_IF_NOT_NULL(s->state[i].deckLinkAttributes);
1508 RELEASE_IF_NOT_NULL(s->state[i].deckLinkInput);
1509 RELEASE_IF_NOT_NULL(s->state[i].deckLink);
1510 }
1511
1512 free(s->audio.data);
1513
1514 vf_free(s->frame);
1515 delete s;
1516}
1517
1518static void
1520{
1521 debug_msg("vidcap_decklink_done\n"); /* TOREMOVE */
1522
1523 HRESULT result;
1524
1525 struct vidcap_decklink_state *s = (struct vidcap_decklink_state *) state;
1526
1527 assert (s != NULL);
1528
1529 for (int i = 0; i < s->devices_cnt; ++i)
1530 {
1531 result = s->state[i].deckLinkInput->StopStreams();
1532 if (result != S_OK) {
1534 << MOD_NAME "Could not stop stream: "
1535 << bmd_hresult_to_string(result) << "\n";
1536 }
1537
1538 if (s->state[i].audio) {
1539 result = s->state[i].deckLinkInput->DisableAudioInput();
1540 if (result != S_OK) {
1542 << MOD_NAME "Could disable audio input: "
1543 << bmd_hresult_to_string(result) << "\n";
1544 }
1545 }
1546 result = s->state[i].deckLinkInput->DisableVideoInput();
1547 if (result != S_OK) {
1549 << MOD_NAME "Could disable video input: "
1550 << bmd_hresult_to_string(result) << "\n";
1551 }
1552 }
1553
1554 cleanup_common(s);
1555}
1556
1566static int nr_frames(struct vidcap_decklink_state *s) {
1567 BMDTimecodeBCD max_timecode = 0u;
1568 int tiles_total = 0;
1569 int i;
1570
1571 /* If we use timecode, take maximal timecode value... */
1572 if (s->sync_timecode) {
1573 for (i = 0; i < s->devices_cnt; ++i) {
1574 if(s->state[i].delegate->newFrameReady == 1) {
1575 if (s->state[i].delegate->timecode > max_timecode) {
1576 max_timecode = s->state[i].delegate->timecode;
1577 }
1578 }
1579 }
1580 }
1581
1582 /* count all tiles */
1583 for (i = 0; i < s->devices_cnt; ++i) {
1584 if(s->state[i].delegate->newFrameReady == 1) {
1585 /* if inputs are synchronized, use only up-to-date frames (with same TC)
1586 * as the most recent */
1587 if(s->sync_timecode) {
1588 if(s->state[i].delegate->timecode && s->state[i].delegate->timecode != max_timecode) {
1589 s->state[i].delegate->newFrameReady = 0;
1590 } else {
1591 tiles_total++;
1592 }
1593 }
1594 /* otherwise, simply add up the count */
1595 else {
1596 tiles_total++;
1597 }
1598 }
1599 if (s->state[i].delegate->newFrameReady == -1) {
1600 return -1;
1601 }
1602 }
1603 return tiles_total;
1604}
1605
1607 if (s->audioPackets.empty()) {
1608 return nullptr;
1609 }
1610 s->audio.data_len = 0;
1611
1612 BMDTimeValue audio_time = 0;
1613 s->audioPackets.front()->GetPacketTime(&audio_time, bmdAudioSampleRate48kHz);
1614 s->audio.timestamp =
1615 ((int64_t)audio_time * 90000 + bmdAudioSampleRate48kHz - 1) / bmdAudioSampleRate48kHz;
1616
1617 while (!s->audioPackets.empty()) {
1618 auto *audioPacket = s->audioPackets.front();
1619 s->audioPackets.pop();
1620
1621 void *audioFrame = nullptr;
1622 audioPacket->GetBytes(&audioFrame);
1623
1624 if (s->audio.ch_count == 1) { // there are actually 2 channels grabbed
1625 if (s->audio.data_len + audioPacket->GetSampleFrameCount() * 1U * s->audio.bps <= static_cast<unsigned>(s->audio.max_size)) {
1626 demux_channel(s->audio.data + s->audio.data_len, static_cast<char *>(audioFrame), s->audio.bps, min<int64_t>(audioPacket->GetSampleFrameCount() * 2 /* channels */ * s->audio.bps, INT_MAX), 2 /* channels (originally) */, 0 /* we want first channel */);
1627 s->audio.data_len = min<int64_t>(s->audio.data_len + audioPacket->GetSampleFrameCount() * 1 * s->audio.bps, INT_MAX);
1628 } else {
1629 MSG(WARNING, "Audio frame too small!\n");
1630 }
1631 } else {
1632 if (s->audio.data_len + audioPacket->GetSampleFrameCount() * s->audio.ch_count * s->audio.bps <= s->audio.max_size) {
1633 memcpy(s->audio.data + s->audio.data_len, audioFrame, audioPacket->GetSampleFrameCount() * s->audio.ch_count * s->audio.bps);
1634 s->audio.data_len = min<int64_t>(s->audio.data_len + audioPacket->GetSampleFrameCount() * s->audio.ch_count * s->audio.bps, INT_MAX);
1635 } else {
1636 MSG(WARNING, "Audio frame too small!\n");
1637 }
1638 }
1639 audioPacket->Release();
1640 }
1641 return &s->audio;
1642}
1643
1645 if (s->codec == RGBA) {
1646 for (unsigned i = 0; i < s->frame->tile_count; ++i) {
1647 vc_copylineToRGBA_inplace((unsigned char*) s->frame->tiles[i].data,
1648 (unsigned char*)s->frame->tiles[i].data,
1649 s->frame->tiles[i].data_len, 16, 8, 0);
1650 }
1651 }
1652 if (s->codec == R10k && get_commandline_param(R10K_FULL_OPT) == nullptr) {
1653 for (unsigned i = 0; i < s->frame->tile_count; ++i) {
1655 s->frame->tiles[i].data_len);
1656 }
1657 }
1658}
1659
1660static struct video_frame *
1662{
1663 struct vidcap_decklink_state *s = (struct vidcap_decklink_state *) state;
1664 int tiles_total = 0;
1665 int i;
1666 bool frame_ready = true;
1667
1668 int timeout = 0;
1669
1670 unique_lock<mutex> lk(s->lock);
1671// LOCK - LOCK - LOCK - LOCK - LOCK - LOCK - LOCK - LOCK - LOCK - LOCK - LOCK //
1672
1673 tiles_total = nr_frames(s);
1674
1675 while(tiles_total != s->devices_cnt) {
1676 //while (!s->state[0].delegate->newFrameReady) {
1677 cv_status rc = cv_status::no_timeout;
1678 steady_clock::time_point t0(steady_clock::now());
1679
1680 while(rc == cv_status::no_timeout
1681 && tiles_total != s->devices_cnt /* not all tiles */
1682 && tiles_total != -1
1683 && !timeout) {
1684 rc = s->boss_cv.wait_for(lk, microseconds(2 * s->next_frame_time));
1685 // recompute tiles count
1686 tiles_total = nr_frames(s);
1687
1688 // this is for the case of multiple tiles (eg. when one tile is persistently
1689 // missing, eg. 01301301. Therefore, pthread_cond_timewait doesn't timeout but
1690 // actual timeout time has reached.
1691 steady_clock::time_point t(steady_clock::now());
1692 if (duration_cast<microseconds>(t - t0).count() > 2 * s->next_frame_time)
1693 timeout = 1;
1694
1695 }
1696
1697 if (rc != cv_status::no_timeout || timeout || tiles_total == -1) { //(rc == ETIMEDOUT) {
1698 log_msg(LOG_LEVEL_VERBOSE, "Waiting for new frame timed out!\n");
1699
1700 // try to restart stream
1701 /*
1702 HRESULT result;
1703 debug_msg("Try to restart DeckLink stream!\n");
1704 result = s->deckLinkInput->StopStreams();
1705 if (result != S_OK)
1706 {
1707 debug_msg("Could not stop stream: %08x\n", result);
1708 }
1709 result = s->deckLinkInput->StartStreams();
1710 if (result != S_OK)
1711 {
1712 debug_msg("Could not start stream: %08x\n", result);
1713 return NULL; // really end ???
1714 }
1715 */
1716
1717 frame_ready = false;
1718 break;
1719 }
1720 }
1721
1722 /* cleanup newframe flag */
1723 for (i = 0; i < s->devices_cnt; ++i) {
1724 if(s->state[i].delegate->newFrameReady == 0) {
1725 frame_ready = false;
1726 }
1727 s->state[i].delegate->newFrameReady = 0;
1728 }
1729
1730 *audio = process_new_audio_packets(s); // return audio even if there is no video to avoid
1731 // hoarding and then dropping of audio packets
1732// UNLOCK - UNLOCK - UNLOCK - UNLOCK - UNLOCK - UNLOCK - UNLOCK - UNLOCK - UN //
1733 lk.unlock();
1734
1735 if(!frame_ready)
1736 return NULL;
1737
1738 /* count returned tiles */
1739 int count = 0;
1740 if(s->stereo) {
1741 if (s->state[0].delegate->pixelFrame != NULL &&
1742 s->state[0].delegate->pixelFrameRight != NULL) {
1743 s->frame->tiles[0].data = (char*)s->state[0].delegate->pixelFrame;
1744 s->frame->tiles[1].data = (char*)s->state[0].delegate->pixelFrameRight;
1745 ++count;
1746 } // else count == 0 -> return NULL
1747 } else {
1748 for (i = 0; i < s->devices_cnt; ++i) {
1749 if (s->state[i].delegate->pixelFrame == NULL) {
1750 break;
1751 }
1752 s->frame->tiles[i].data = (char*)s->state[i].delegate->pixelFrame;
1753 ++count;
1754 }
1755 }
1757 return NULL;
1758 }
1760
1761 s->frames++;
1762 s->frame->timecode = s->state[0].delegate->timecode;
1763 s->frame->timestamp =
1764 ((int64_t)s->state[0].delegate->frameTime * 90000 +
1765 s->frameRateScale - 1) /
1766 s->frameRateScale;
1767 return s->frame;
1768}
1769
1770/* function from DeckLink SDK sample DeviceList */
1771static list<tuple<int, string, string, string>> get_input_modes (IDeckLink* deckLink)
1772{
1773 list<tuple<int, string, string, string>> ret;
1774 IDeckLinkInput* deckLinkInput = NULL;
1775 IDeckLinkDisplayModeIterator* displayModeIterator = NULL;
1776 IDeckLinkDisplayMode* displayMode = NULL;
1777 HRESULT result;
1778 int displayModeNumber = 0;
1779
1780 // Query the DeckLink for its configuration interface
1781 if ((result = deckLink->QueryInterface(IID_IDeckLinkInput, (void**)&deckLinkInput)) != S_OK) {
1782 LOG(LOG_LEVEL_ERROR) << MOD_NAME "Could not obtain the IDeckLinkInput interface: " << bmd_hresult_to_string(result) << "\n";
1783 if (result == E_NOINTERFACE) {
1784 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Device doesn't support video capture.\n");
1785 }
1786 goto bail;
1787 }
1788
1789 // Obtain an IDeckLinkDisplayModeIterator to enumerate the display modes supported on input
1790 if ((result = deckLinkInput->GetDisplayModeIterator(&displayModeIterator)) != S_OK) {
1791 LOG(LOG_LEVEL_ERROR) << MOD_NAME "Could not obtain the video input display mode iterator: " << bmd_hresult_to_string(result) << "\n";
1792 goto bail;
1793 }
1794
1795 // List all supported output display modes
1796 while (displayModeIterator->Next(&displayMode) == S_OK)
1797 {
1798 BMD_STR displayModeString = NULL;
1799
1800 result = displayMode->GetName((BMD_STR *) &displayModeString);
1801
1802 if (result == S_OK)
1803 {
1804 BMDTimeValue frameRateDuration;
1805 BMDTimeScale frameRateScale;
1806
1807 // Obtain the display mode's properties
1808 string flags_str = bmd_get_flags_str(displayMode->GetFlags());
1809 int modeWidth = displayMode->GetWidth();
1810 int modeHeight = displayMode->GetHeight();
1811 displayMode->GetFrameRate(&frameRateDuration, &frameRateScale);
1812 uint32_t mode = ntohl(displayMode->GetDisplayMode());
1813 uint32_t field_dominance_n = ntohl(displayMode->GetFieldDominance());
1814 string fcc{(char *) &mode, 4};
1815 string name{get_str_from_bmd_api_str(displayModeString)};
1816 char buf[1024];
1817 snprintf(buf, sizeof buf, "%d x %d \t %6.2f FPS \t flags: %.4s, %s", modeWidth, modeHeight,
1818 (float) ((double)frameRateScale / (double)frameRateDuration),
1819 (char *) &field_dominance_n, flags_str.c_str());
1820 string details{buf};
1821 ret.push_back(tuple<int, string, string, string> {displayModeNumber, fcc, name, details});
1822
1823 release_bmd_api_str(displayModeString);
1824 }
1825
1826 // Release the IDeckLinkDisplayMode object to prevent a leak
1827 displayMode->Release();
1828
1829 displayModeNumber++;
1830 }
1831
1832bail:
1833 // Ensure that the interfaces we obtained are released to prevent a memory leak
1834 RELEASE_IF_NOT_NULL(displayModeIterator);
1835 RELEASE_IF_NOT_NULL(deckLinkInput);
1836
1837 return ret;
1838}
1839
1840static void print_input_modes (IDeckLink* deckLink)
1841{
1842 list<tuple<int, string, string, string>> ret = get_input_modes (deckLink);
1843 printf("\tcapture modes:\n");
1844 for (auto &i : ret) {
1845 col() << "\t\t" << right << SBOLD(setw(2) << get<0>(i) << " (" << get<1>(i) << ")") << ") " <<
1846 left << setw(20) << get<2>(i) << internal << " " <<
1847 get<3>(i) << "\n";
1848 }
1849}
1850
1852 codec = c;
1854 << MOD_NAME "Using codec: " << get_codec_name(codec) << "\n";
1855 if (c == R10k && get_commandline_param(R10K_FULL_OPT) == nullptr) {
1856 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Using limited range R10k as specified by BMD, use '--param "
1857 R10K_FULL_OPT "' to override.\n");
1858 }
1859}
1860
1868
1870
1871/* vi: set expandtab sw=8: */
void ** state
int void * val
const char * fmt
const char unsigned int flags
struct audio_frame ** audio
int void(** deleter)(void *))
struct video_frame * vf_alloc(int count)
Allocates blank video frame.
void vf_free(struct video_frame *buf)
Frees video_frame structure.
int vc_get_linesize(unsigned int width, codec_t codec)
const char * get_codec_name(codec_t codec)
codec_t get_codec_from_name(const char *name)
static const struct codec_param mapping[AC_COUNT]
Definition libavcodec.c:87
This file contains definition of some common audio types.
bool bmd_parse_audio_levels(const char *opt) noexcept(false)
TOREMOVE (implicit "aacl" option is preferred)
bool blackmagic_api_version_check()
string bmd_get_flags_str(BMDDisplayModeFlags flags)
const map< BMDVideoConnection, string > & get_connection_string_map()
uint32_t bmd_read_fourcc(const char *str)
void print_bmd_device_profiles(const char *line_prefix)
void release_bmd_api_str(BMD_STR string)
void print_bmd_attribute(IDeckLinkProfileAttributes *deckLinkAttributes, const char *query_prop_fcc)
string bmd_hresult_to_string(HRESULT res)
string bmd_get_device_name(IDeckLink *decklink)
void decklink_uninitialize(bool *com_initialized)
bool decklink_set_profile(IDeckLink *deckLink, bmd_option const &req_profile, bool stereo)
std::string get_str_from_bmd_api_str(BMD_STR string)
bool bmd_check_stereo_profile(IDeckLink *deckLink)
std::vector< bmd_dev > bmd_get_sorted_devices(bool *com_initialized, bool verbose, bool natural_sort)
bool decklink_supports_codec(T *deckLink, BMDPixelFormat pf)
void print_decklink_version()
void r10k_limited_to_full(const char *in, char *out, size_t len)
#define R10K_FULL_OPT
@ BMD_MAX_AUD_CH
#define BMD_BOOL
#define BMD_STR
#define BMD_CHECK(cmd, name, action)
#define BMD_NAT_SORT
static std::vector< std::pair< codec_t, BMDPixelFormat > > uv_to_bmd_codec_map
#define BMD_FALSE
int32_t mRefCount
Definition decklink.cpp:233
struct vidcap_decklink_state * s
Definition decklink.cpp:246
void * pixelFrame
Definition decklink.cpp:242
uint32_t timecode
Definition decklink.cpp:245
IDeckLinkVideoInputFrame * lastFrame
Definition decklink.cpp:244
VideoDelegate(struct vidcap_decklink_state *state, struct device_state &device_)
Definition decklink.cpp:249
static constexpr BMDDetectedVideoInputFormatFlags csMask
Definition decklink.cpp:234
virtual ULONG STDMETHODCALLTYPE AddRef(void) override
Definition decklink.cpp:258
virtual ~VideoDelegate()
Definition decklink.cpp:252
struct device_state & device
Definition decklink.cpp:247
virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(IDeckLinkVideoInputFrame *, IDeckLinkAudioInputPacket *) override
Definition decklink.cpp:381
IDeckLinkVideoFrame * rightEyeFrame
Definition decklink.cpp:241
virtual ULONG STDMETHODCALLTYPE Release(void) override
Definition decklink.cpp:261
virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged(BMDVideoInputFormatChangedEvents notificationEvents, IDeckLinkDisplayMode *mode, BMDDetectedVideoInputFormatFlags flags) noexcept override
Definition decklink.cpp:310
static auto getNotificationEventsStr(BMDVideoInputFormatChangedEvents notificationEvents, BMDDetectedVideoInputFormatFlags flags) noexcept
Definition decklink.cpp:270
BMDTimeValue frameTime
Definition decklink.cpp:240
void * pixelFrameRight
Definition decklink.cpp:243
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID *) override
Definition decklink.cpp:257
static constexpr BMDDetectedVideoInputFormatFlags bitDepthMask
Definition decklink.cpp:235
BMDDetectedVideoInputFormatFlags configuredCsBitDepth
Definition decklink.cpp:236
int color_printf(const char *format,...)
#define TERM_BOLD
Definition color_out.h:48
#define SRED(x)
Definition color_out.h:85
#define TERM_RESET
Definition color_out.h:47
#define TGREEN(x)
Definition color_out.h:72
#define SBOLD(x)
Definition color_out.h:80
#define TRED(x)
Definition color_out.h:75
#define TBOLD(x)
Definition color_out.h:70
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
void bug_msg(int level, const char *format,...)
prints the error with the advice to report the bug
Definition debug.cpp:379
#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 LOG_LEVEL_VERBOSE
Definition debug.h:63
#define MSG(l, fmt,...)
Definition debug.h:96
#define LOG(level)
Definition debug.h:313
#define debug_msg(...)
#define debug_msg(...) log_msg(LOG_LEVEL_DEBUG, "[pid/%d +%d %s] ", getpid(), LINE,...
Definition debug.h:92
#define LOG_LEVEL_FATAL
errors that prevent UG run
Definition debug.h:58
#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 VIDEO_CAPTURE_ABI_VERSION
#define VIDCAP_INIT_FAIL
error ocured
int count
Definition hd-rum.c:44
unsigned int audio_capture_bps
Definition host.cpp:164
const char * get_commandline_param(const char *key)
Definition host.cpp:894
unsigned int audio_capture_sample_rate
Definition host.cpp:165
char ** uv_argv
Definition host.cpp:174
unsigned int audio_capture_channels
Definition host.cpp:163
#define DEFAULT_AUDIO_CAPTURE_CHANNELS
Definition host.h:89
#define LOG_LEVEL_DEBUG
Definition kernels.cu:48
@ 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
#define IS_KEY_PREFIX(tok, key)
expands to true value if <k> from tok in format <k>=<v> is prefix of key
Definition macros.h:138
@ STR_LEN
"standard" string length placeholder
Definition macros.h:134
#define MAX(a, b)
Definition macros.h:65
bool is_power_of_two(unsigned long long x)
Definition math.c:69
enum openssl_mode mode
decoder_func_t vc_copylineToRGBA_inplace
#define min(a, b)
Definition rtp.c:99
codec_t first
Definition sdl2.c:423
const char * name
Definition settings.cpp:193
struct used to comunicate with audio capture/playback device
Definition types.h:99
void set_int(int64_t val_)
void parse(const char *)
bool keep() const
char extra[1024]
Definition types.h:389
struct device_info::mode modes[512]
char dev[1024]
Definition types.h:383
string device_id
Definition decklink.cpp:175
struct tile * tile
Definition decklink.cpp:177
IDeckLink * deckLink
Definition decklink.cpp:170
bool init(struct vidcap_decklink_state *s, struct tile *tile, BMDAudioConnection audioConnection)
IDeckLinkInput * deckLinkInput
Definition decklink.cpp:171
void check_attributes(struct vidcap_decklink_state *s)
unique_ptr< VideoDelegate > delegate
Definition decklink.cpp:172
IDeckLinkConfiguration * deckLinkConfiguration
Definition decklink.cpp:174
IDeckLinkProfileAttributes * deckLinkAttributes
Definition decklink.cpp:173
Struct tile is an area of video_frame.
Definition types.h:279
unsigned int width
Definition types.h:280
char * data
Raw tile data. Pointer must be at least 4B aligned.
Definition types.h:287
unsigned int data_len
length of the data
Definition types.h:288
unsigned int height
Definition types.h:281
Struct video_frame represents a video frame and contains video description.
Definition types.h:305
enum interlacing_t interlacing
Definition types.h:307
struct tile tiles[FLEXIBLE_ARRAY_MEMBER]
Definition types.h:345
int64_t timestamp
Definition types.h:331
double fps
Definition types.h:308
unsigned int tile_count
Definition types.h:344
int flags
Definition types.h:329
codec_t color_spec
Definition types.h:306
uint32_t timecode
BCD timecode (hrs, min, sec, frm num)
Definition types.h:335
This file contains some common data types.
depth
Definition types.h:125
@ TIMESTAMP_VALID
timestamp set by source (in 90 kHz clock)
Definition types.h:223
void dev_add_option(struct device_info *dev, const char *name, const char *desc, const char *key, const char *opt_str, bool is_boolean)
@ PROGRESSIVE
progressive frame
Definition types.h:164
@ INTERLACED_MERGED
Columngs of both fields are interlaced together.
Definition types.h:167
codec_t
Definition types.h:65
@ R10k
RGB 10-bit packed - RGBX, big-endian (2 bit padding)
Definition types.h:73
@ UYVY
YCbCr 422 8-bit - Cb Y0 Cr Y1.
Definition types.h:71
@ VIDEO_CODEC_NONE
dummy color spec
Definition types.h:66
@ R12L
RGB 12-bit packed, little-endian.
Definition types.h:74
@ RGBA
RGBA 8-bit, big-endian.
Definition types.h:70
@ v210
YCbCr 422 10-bit - xVY0U|xY2UY1|xUY3V|xY5VY4, little-endian (2 bit padding /'x'/),...
Definition types.h:75
void demux_channel(char *out, char *in, int bps, int in_len, int in_stream_channels, int pos_in_stream)
Definition utils.cpp:334
This is an umbrella header for video functions.
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.
int get_bits_per_component(codec_t codec)
struct tile * vf_get_tile(struct video_frame *buf, int pos)
Returns pointer to n-th tile from video frame Equivalent to &video_frame::tiles[pos].
@ FAILED
bool com_initialize(bool *com_initialized, const char *err_prefix)
Definition windows.c:65
void com_uninitialize(bool *com_initialized)
Definition windows.c:94