]> git.sesse.net Git - nageru/blob - decklink_capture.cpp
Use the new has_signal flag.
[nageru] / decklink_capture.cpp
1 #include "decklink_capture.h"
2
3 #include <assert.h>
4 #include <stdint.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <cstddef>
8
9 #include <DeckLinkAPI.h>
10 #include <DeckLinkAPIConfiguration.h>
11 #include <DeckLinkAPIDiscovery.h>
12 #include <DeckLinkAPIModes.h>
13 #include "bmusb/bmusb.h"
14
15 using namespace std;
16 using namespace std::placeholders;
17
18 namespace {
19
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)
23 {
24         assert(n % 2 == 0);
25         uint8_t *dptr1 = dest1;
26         uint8_t *dptr2 = dest2;
27
28         for (size_t i = 0; i < n; i += 2) {
29                 *dptr1++ = *src++;
30                 *dptr2++ = *src++;
31         }
32 }
33
34 }  // namespace
35
36 DeckLinkCapture::DeckLinkCapture(IDeckLink *card, int card_index)
37 {
38         {
39                 const char *model_name;
40                 char buf[256];
41                 if (card->GetModelName(&model_name) == S_OK) {
42                         snprintf(buf, sizeof(buf), "Card %d: %s", card_index, model_name);
43                 } else {
44                         snprintf(buf, sizeof(buf), "Card %d: Unknown DeckLink card", card_index);
45                 }
46                 description = buf;
47         }
48
49         if (card->QueryInterface(IID_IDeckLinkInput, (void**)&input) != S_OK) {
50                 fprintf(stderr, "Card %d has no inputs\n", card_index);
51                 exit(1);
52         }
53
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);
58                 exit(1);
59         }
60
61         if (config->SetInt(bmdDeckLinkConfigVideoInputConnection, bmdVideoConnectionHDMI) != S_OK) {
62                 fprintf(stderr, "Failed to set video input connection for card %d\n", card_index);
63                 exit(1);
64         }
65
66         if (config->SetInt(bmdDeckLinkConfigAudioInputConnection, bmdAudioConnectionEmbedded) != S_OK) {
67                 fprintf(stderr, "Failed to set video input connection for card %d\n", card_index);
68                 exit(1);
69         }
70
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);
76                 exit(1);
77         }
78
79         if (support == bmdDisplayModeNotSupported) {
80                 fprintf(stderr, "Card %d does not support display mode\n", card_index);
81                 exit(1);
82         }
83
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);
86                 exit(1);
87         }
88
89         if (input->EnableVideoInput(bmdModeHD720p5994, bmdFormat8BitYUV, 0) != S_OK) {
90                 fprintf(stderr, "Failed to set 720p59.94 connection for card %d\n", card_index);
91                 exit(1);
92         }
93
94         if (input->EnableAudioInput(48000, bmdAudioSampleType16bitInteger, 2) != S_OK) {
95                 fprintf(stderr, "Failed to enable audio input for card %d\n", card_index);
96                 exit(1);
97         }
98
99         input->SetCallback(this);
100 }
101
102 DeckLinkCapture::~DeckLinkCapture()
103 {
104         if (has_dequeue_callbacks) {
105                 dequeue_cleanup_callback();
106         }
107 }
108
109 HRESULT STDMETHODCALLTYPE DeckLinkCapture::QueryInterface(REFIID, LPVOID *)
110 {
111         return E_NOINTERFACE;
112 }
113
114 ULONG STDMETHODCALLTYPE DeckLinkCapture::AddRef(void)
115 {
116         return refcount.fetch_add(1) + 1;
117 }
118
119 ULONG STDMETHODCALLTYPE DeckLinkCapture::Release(void)
120 {
121         int new_ref = refcount.fetch_sub(1) - 1;
122         if (new_ref == 0)
123                 delete this;
124         return new_ref;
125 }
126
127 HRESULT STDMETHODCALLTYPE DeckLinkCapture::VideoInputFormatChanged(
128         BMDVideoInputFormatChangedEvents,
129         IDeckLinkDisplayMode* display_mode,
130         BMDDetectedVideoInputFormatFlags)
131 {
132         if (display_mode->GetFrameRate(&frame_duration, &time_scale) != S_OK) {
133                 fprintf(stderr, "Could not get new frame rate\n");
134                 exit(1);
135         }
136         return S_OK;
137 }
138
139 HRESULT STDMETHODCALLTYPE DeckLinkCapture::VideoInputFrameArrived(
140         IDeckLinkVideoInputFrame *video_frame,
141         IDeckLinkAudioInputPacket *audio_frame)
142 {
143         if (!done_init) {
144                 if (has_dequeue_callbacks) {
145                         dequeue_init_callback();
146                 }
147                 done_init = true;
148         }
149
150         FrameAllocator::Frame current_video_frame, current_audio_frame;
151         VideoFormat video_format;
152
153         if (video_frame) {
154                 video_format.has_signal = !(video_frame->GetFlags() & bmdFrameHasNoInputSource);
155
156                 int width = video_frame->GetWidth();
157                 int height = video_frame->GetHeight();
158                 const int stride = video_frame->GetRowBytes();
159                 assert(stride == width * 2);
160
161                 current_video_frame = video_frame_allocator->alloc_frame();
162                 if (current_video_frame.data != nullptr) {
163                         const uint8_t *frame_bytes;
164                         video_frame->GetBytes((void **)&frame_bytes);
165
166                         memcpy_interleaved(current_video_frame.data, current_video_frame.data2,
167                                 frame_bytes, width * height * 2);
168                         current_video_frame.len += width * height * 2;
169
170                         video_format.width = width;
171                         video_format.height = height;
172                         video_format.frame_rate_nom = time_scale;
173                         video_format.frame_rate_den = frame_duration;
174                 }
175         }
176
177         if (current_video_frame.data != nullptr || current_audio_frame.data != nullptr) {
178                 // TODO: Put into a queue and put into a dequeue thread, if the
179                 // BlackMagic drivers don't already do that for us?
180                 frame_callback(timecode,
181                         current_video_frame, /*video_offset=*/0, video_format,
182                         current_audio_frame, /*audio_offset=*/0, 0x0000);
183         }
184
185         timecode++;
186         return S_OK;
187 }
188
189 void DeckLinkCapture::start_bm_capture()
190 {
191         if (input->StartStreams() != S_OK) {
192                 fprintf(stderr, "StartStreams failed\n");
193                 exit(1);
194         }
195 }
196
197 void DeckLinkCapture::stop_dequeue_thread()
198 {
199         if (input->StopStreams() != S_OK) {
200                 fprintf(stderr, "StopStreams failed\n");
201                 exit(1);
202         }
203 }
204