1 #include "decklink_capture.h"
9 #include <DeckLinkAPI.h>
10 #include <DeckLinkAPIConfiguration.h>
11 #include <DeckLinkAPIDiscovery.h>
12 #include <DeckLinkAPIModes.h>
13 #include "bmusb/bmusb.h"
16 using namespace std::placeholders;
20 // TODO: Support stride.
21 // TODO: Support AVX2 (adapt from bmusb).
22 void memcpy_interleaved(uint8_t *dest1, uint8_t *dest2, const uint8_t *src, size_t n)
25 uint8_t *dptr1 = dest1;
26 uint8_t *dptr2 = dest2;
28 for (size_t i = 0; i < n; i += 2) {
36 DeckLinkCapture::DeckLinkCapture(IDeckLink *card, int card_index)
39 const char *model_name;
41 if (card->GetModelName(&model_name) == S_OK) {
42 snprintf(buf, sizeof(buf), "Card %d: %s", card_index, model_name);
44 snprintf(buf, sizeof(buf), "Card %d: Unknown DeckLink card", card_index);
49 if (card->QueryInterface(IID_IDeckLinkInput, (void**)&input) != S_OK) {
50 fprintf(stderr, "Card %d has no inputs\n", card_index);
54 /* Set up the video and audio sources. */
55 IDeckLinkConfiguration *config;
56 if (card->QueryInterface(IID_IDeckLinkConfiguration, (void**)&config) != S_OK) {
57 fprintf(stderr, "Failed to get configuration interface for card %d\n", card_index);
61 if (config->SetInt(bmdDeckLinkConfigVideoInputConnection, bmdVideoConnectionHDMI) != S_OK) {
62 fprintf(stderr, "Failed to set video input connection for card %d\n", card_index);
66 if (config->SetInt(bmdDeckLinkConfigAudioInputConnection, bmdAudioConnectionEmbedded) != S_OK) {
67 fprintf(stderr, "Failed to set video input connection for card %d\n", card_index);
71 // TODO: Make the user mode selectable.
72 BMDDisplayModeSupport support;
73 IDeckLinkDisplayMode *display_mode;
74 if (input->DoesSupportVideoMode(bmdModeHD720p5994, bmdFormat8BitYUV, /*flags=*/0, &support, &display_mode)) {
75 fprintf(stderr, "Failed to query display mode for card %d\n", card_index);
79 if (support == bmdDisplayModeNotSupported) {
80 fprintf(stderr, "Card %d does not support display mode\n", card_index);
84 if (display_mode->GetFrameRate(&frame_duration, &time_scale) != S_OK) {
85 fprintf(stderr, "Could not get frame rate for card %d\n", card_index);
89 if (input->EnableVideoInput(bmdModeHD720p5994, bmdFormat8BitYUV, 0) != S_OK) {
90 fprintf(stderr, "Failed to set 720p59.94 connection for card %d\n", card_index);
94 if (input->EnableAudioInput(48000, bmdAudioSampleType16bitInteger, 2) != S_OK) {
95 fprintf(stderr, "Failed to enable audio input for card %d\n", card_index);
99 input->SetCallback(this);
102 DeckLinkCapture::~DeckLinkCapture()
104 if (has_dequeue_callbacks) {
105 dequeue_cleanup_callback();
109 HRESULT STDMETHODCALLTYPE DeckLinkCapture::QueryInterface(REFIID, LPVOID *)
111 return E_NOINTERFACE;
114 ULONG STDMETHODCALLTYPE DeckLinkCapture::AddRef(void)
116 return refcount.fetch_add(1) + 1;
119 ULONG STDMETHODCALLTYPE DeckLinkCapture::Release(void)
121 int new_ref = refcount.fetch_sub(1) - 1;
127 HRESULT STDMETHODCALLTYPE DeckLinkCapture::VideoInputFormatChanged(
128 BMDVideoInputFormatChangedEvents,
129 IDeckLinkDisplayMode* display_mode,
130 BMDDetectedVideoInputFormatFlags)
132 if (display_mode->GetFrameRate(&frame_duration, &time_scale) != S_OK) {
133 fprintf(stderr, "Could not get new frame rate\n");
139 HRESULT STDMETHODCALLTYPE DeckLinkCapture::VideoInputFrameArrived(
140 IDeckLinkVideoInputFrame *video_frame,
141 IDeckLinkAudioInputPacket *audio_frame)
144 if (has_dequeue_callbacks) {
145 dequeue_init_callback();
150 FrameAllocator::Frame current_video_frame, current_audio_frame;
151 VideoFormat video_format;
154 if (video_frame->GetFlags() & bmdFrameHasNoInputSource) {
155 // TODO: Make a way to propagate this flag down to the theme code,
156 // independent of the signal resolution.
157 fprintf(stderr, "Warning: No input signal detected\n");
160 int width = video_frame->GetWidth();
161 int height = video_frame->GetHeight();
162 const int stride = video_frame->GetRowBytes();
163 assert(stride == width * 2);
165 current_video_frame = video_frame_allocator->alloc_frame();
166 if (current_video_frame.data != nullptr) {
167 const uint8_t *frame_bytes;
168 video_frame->GetBytes((void **)&frame_bytes);
170 memcpy_interleaved(current_video_frame.data, current_video_frame.data2,
171 frame_bytes, width * height * 2);
172 current_video_frame.len += width * height * 2;
174 video_format.width = width;
175 video_format.height = height;
176 video_format.frame_rate_nom = time_scale;
177 video_format.frame_rate_den = frame_duration;
181 if (current_video_frame.data != nullptr || current_audio_frame.data != nullptr) {
182 // TODO: Put into a queue and put into a dequeue thread, if the
183 // BlackMagic drivers don't already do that for us?
184 frame_callback(timecode,
185 current_video_frame, /*video_offset=*/0, video_format,
186 current_audio_frame, /*audio_offset=*/0, 0x0000);
193 void DeckLinkCapture::start_bm_capture()
195 if (input->StartStreams() != S_OK) {
196 fprintf(stderr, "StartStreams failed\n");
201 void DeckLinkCapture::stop_dequeue_thread()
203 if (input->StopStreams() != S_OK) {
204 fprintf(stderr, "StopStreams failed\n");