1 #include "decklink_capture.h"
10 #include <DeckLinkAPI.h>
11 #include <DeckLinkAPIConfiguration.h>
12 #include <DeckLinkAPIDiscovery.h>
13 #include <DeckLinkAPIModes.h>
14 #include "bmusb/bmusb.h"
16 #define FRAME_SIZE (8 << 20) // 8 MB.
19 using namespace std::placeholders;
23 // TODO: Support stride.
24 // TODO: Support AVX2 (adapt from bmusb).
25 void memcpy_interleaved(uint8_t *dest1, uint8_t *dest2, const uint8_t *src, size_t n)
28 uint8_t *dptr1 = dest1;
29 uint8_t *dptr2 = dest2;
31 for (size_t i = 0; i < n; i += 2) {
39 DeckLinkCapture::DeckLinkCapture(IDeckLink *card, int card_index)
42 const char *model_name;
44 if (card->GetModelName(&model_name) == S_OK) {
45 snprintf(buf, sizeof(buf), "Card %d: %s", card_index, model_name);
47 snprintf(buf, sizeof(buf), "Card %d: Unknown DeckLink card", card_index);
52 if (card->QueryInterface(IID_IDeckLinkInput, (void**)&input) != S_OK) {
53 fprintf(stderr, "Card %d has no inputs\n", card_index);
57 /* Set up the video and audio sources. */
58 IDeckLinkConfiguration *config;
59 if (card->QueryInterface(IID_IDeckLinkConfiguration, (void**)&config) != S_OK) {
60 fprintf(stderr, "Failed to get configuration interface for card %d\n", card_index);
64 if (config->SetInt(bmdDeckLinkConfigVideoInputConnection, bmdVideoConnectionHDMI) != S_OK) {
65 fprintf(stderr, "Failed to set video input connection for card %d\n", card_index);
69 if (config->SetInt(bmdDeckLinkConfigAudioInputConnection, bmdAudioConnectionEmbedded) != S_OK) {
70 fprintf(stderr, "Failed to set video input connection for card %d\n", card_index);
74 // TODO: Make the user mode selectable.
75 BMDDisplayModeSupport support;
76 IDeckLinkDisplayMode *display_mode;
77 if (input->DoesSupportVideoMode(bmdModeHD720p5994, bmdFormat8BitYUV, /*flags=*/0, &support, &display_mode)) {
78 fprintf(stderr, "Failed to query display mode for card %d\n", card_index);
82 if (support == bmdDisplayModeNotSupported) {
83 fprintf(stderr, "Card %d does not support display mode\n", card_index);
87 if (display_mode->GetFrameRate(&frame_duration, &time_scale) != S_OK) {
88 fprintf(stderr, "Could not get frame rate for card %d\n", card_index);
92 if (input->EnableVideoInput(bmdModeHD720p5994, bmdFormat8BitYUV, 0) != S_OK) {
93 fprintf(stderr, "Failed to set 720p59.94 connection for card %d\n", card_index);
97 if (input->EnableAudioInput(48000, bmdAudioSampleType32bitInteger, 2) != S_OK) {
98 fprintf(stderr, "Failed to enable audio input for card %d\n", card_index);
102 input->SetCallback(this);
105 DeckLinkCapture::~DeckLinkCapture()
107 if (has_dequeue_callbacks) {
108 dequeue_cleanup_callback();
112 HRESULT STDMETHODCALLTYPE DeckLinkCapture::QueryInterface(REFIID, LPVOID *)
114 return E_NOINTERFACE;
117 ULONG STDMETHODCALLTYPE DeckLinkCapture::AddRef(void)
119 return refcount.fetch_add(1) + 1;
122 ULONG STDMETHODCALLTYPE DeckLinkCapture::Release(void)
124 int new_ref = refcount.fetch_sub(1) - 1;
130 HRESULT STDMETHODCALLTYPE DeckLinkCapture::VideoInputFormatChanged(
131 BMDVideoInputFormatChangedEvents,
132 IDeckLinkDisplayMode* display_mode,
133 BMDDetectedVideoInputFormatFlags)
135 if (display_mode->GetFrameRate(&frame_duration, &time_scale) != S_OK) {
136 fprintf(stderr, "Could not get new frame rate\n");
142 HRESULT STDMETHODCALLTYPE DeckLinkCapture::VideoInputFrameArrived(
143 IDeckLinkVideoInputFrame *video_frame,
144 IDeckLinkAudioInputPacket *audio_frame)
147 if (has_dequeue_callbacks) {
148 dequeue_init_callback();
153 FrameAllocator::Frame current_video_frame, current_audio_frame;
154 VideoFormat video_format;
155 AudioFormat audio_format;
158 video_format.has_signal = !(video_frame->GetFlags() & bmdFrameHasNoInputSource);
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;
182 int num_samples = audio_frame->GetSampleFrameCount();
184 current_audio_frame = audio_frame_allocator->alloc_frame();
185 if (current_audio_frame.data != nullptr) {
186 const uint8_t *frame_bytes;
187 audio_frame->GetBytes((void **)&frame_bytes);
188 current_audio_frame.len = sizeof(int32_t) * 2 * num_samples;
190 memcpy(current_audio_frame.data, frame_bytes, current_audio_frame.len);
192 audio_format.bits_per_sample = 32;
193 audio_format.num_channels = 2;
197 if (current_video_frame.data != nullptr || current_audio_frame.data != nullptr) {
198 // TODO: Put into a queue and put into a dequeue thread, if the
199 // BlackMagic drivers don't already do that for us?
200 frame_callback(timecode,
201 current_video_frame, /*video_offset=*/0, video_format,
202 current_audio_frame, /*audio_offset=*/0, audio_format);
209 void DeckLinkCapture::configure_card()
211 if (video_frame_allocator == nullptr) {
212 set_video_frame_allocator(new MallocFrameAllocator(FRAME_SIZE, NUM_QUEUED_VIDEO_FRAMES)); // FIXME: leak.
214 if (audio_frame_allocator == nullptr) {
215 set_audio_frame_allocator(new MallocFrameAllocator(65536, NUM_QUEUED_AUDIO_FRAMES)); // FIXME: leak.
219 void DeckLinkCapture::start_bm_capture()
221 if (input->StartStreams() != S_OK) {
222 fprintf(stderr, "StartStreams failed\n");
227 void DeckLinkCapture::stop_dequeue_thread()
229 if (input->StopStreams() != S_OK) {
230 fprintf(stderr, "StopStreams failed\n");