UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
decklink.cpp
Go to the documentation of this file.
1
7/*
8 * Copyright (c) 2012-2023 CESNET, z. s. p. o.
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 */
39
40#ifdef HAVE_CONFIG_H
41#include "config.h"
42#include "config_unix.h"
43#include "config_win32.h"
44#endif
45
46#include <iostream>
47
49#include "audio/types.h"
50#include "audio/utils.h"
51#include "blackmagic_common.hpp"
52#include "debug.h"
53#include "host.h"
54#include "lib_common.h"
55#include "tv.h"
56#include "utils/color_out.h"
57#include "video_codec.h"
58#include "video_capture.h"
59
60#ifndef _WIN32
61#define STDMETHODCALLTYPE
62#endif
63
64namespace {
65class PlaybackDelegate;
66}
67
68#define DECKLINK_MAGIC 0x415f46d0
69#define MOD_NAME "[DeckLink audio play.] "
70
72 uint32_t magic;
73
75
77
78 PlaybackDelegate *delegate;
80 IDeckLink *deckLink;
81 IDeckLinkOutput *deckLinkOutput;
82 IDeckLinkMutableVideoFrame *deckLinkFrame;
83
84 BMDTimeValue frameRateDuration;
85 BMDTimeScale frameRateScale;
86
87 int frames;
89 };
90
91namespace {
92class PlaybackDelegate : public IDeckLinkVideoOutputCallback // , public IDeckLinkAudioOutputCallback
93{
94 struct state_decklink * s;
95
96public:
97 PlaybackDelegate (struct state_decklink* owner) {
98 s = owner;
99 }
100
101
102 // IUnknown needs only a dummy implementation
103 virtual HRESULT STDMETHODCALLTYPE QueryInterface (REFIID, LPVOID *) { return E_NOINTERFACE;}
104 virtual ULONG STDMETHODCALLTYPE AddRef () {return 1;}
105 virtual ULONG STDMETHODCALLTYPE Release () {return 1;}
106
107 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted (IDeckLinkVideoFrame *, BMDOutputFrameCompletionResult) {
108 s->deckLinkOutput->ScheduleVideoFrame(s->deckLinkFrame,
110 s->frames++;
111 return S_OK;
112 }
113 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped () { return S_OK; }
114 //virtual HRESULT RenderAudioSamples (bool preroll);
115};
116
117static void audio_play_decklink_probe(struct device_info **available_devices, int *count, void (**deleter)(void *))
118{
119 *deleter = free;
120 *available_devices = static_cast<struct device_info *>(calloc(1, sizeof(struct device_info)));
121 strcpy((*available_devices)[0].dev, "");
122 strcpy((*available_devices)[0].name, "Audio-only output through DeckLink device");
123 *count = 1;
124}
125
126static void audio_play_decklink_help()
127{
128 IDeckLinkIterator* deckLinkIterator;
129 IDeckLink* deckLink;
130 int numDevices = 0;
131 HRESULT result;
132
133 printf("Audio-only DeckLink output. For simultaneous audio and video use the DeckLink display "
134 "with analog/embedded audio playback module.\n");
135 printf("Usage:\n");
136 col() << SBOLD(SRED("\t-r decklink")) << "[:<index>][:audioConsumerLevels={true|false}]\n";
137 printf("\n");
138 col() << SBOLD("audioConsumerLevels") "\n";
139 printf("\tIf set true the analog audio levels are set to maximum gain on audio input.\n");
140 printf("\tIf set false the selected analog input gain levels are used.\n");
141
142 // Create an IDeckLinkIterator object to enumerate all DeckLink cards in the system
143 bool com_initialized = false;
144 deckLinkIterator = create_decklink_iterator(&com_initialized, true);
145 if (deckLinkIterator == NULL)
146 {
147 return;
148 }
149
150 printf("Available Blackmagic audio playback devices:\n");
151
152 // Enumerate all cards in this system
153 while (deckLinkIterator->Next(&deckLink) == S_OK)
154 {
155 BMD_STR deviceNameString = NULL;
156
157 // *** Print the model name of the DeckLink card
158 result = deckLink->GetModelName(&deviceNameString);
159 if (result == S_OK)
160 {
161 char *deviceNameCString = get_cstr_from_bmd_api_str(deviceNameString);
162 printf("\tdecklink:%d : Blackmagic %s\n",numDevices, deviceNameCString);
163 release_bmd_api_str(deviceNameString);
164 free(deviceNameCString);
165 }
166
167 // Increment the total number of DeckLink cards found
168 numDevices++;
169
170 // Release the IDeckLink instance when we've finished with it to prevent leaks
171 deckLink->Release();
172 }
173
174 deckLinkIterator->Release();
175 decklink_uninitialize(&com_initialized);
176
177 // If no DeckLink cards were found in the system, inform the user
178 if (numDevices == 0)
179 {
180 printf("\nNo Blackmagic Design devices were found.\n");
181 return;
182 }
183}
184
185static void *audio_play_decklink_init(const char *cfg)
186{
187 struct state_decklink *s = NULL;
188 IDeckLinkIterator* deckLinkIterator;
189 HRESULT result;
190 IDeckLinkConfiguration* deckLinkConfiguration = NULL;
191 // for Decklink Studio which has switchable XLR - analog 3 and 4 or AES/EBU 3,4 and 5,6
192 //BMDAudioOutputAnalogAESSwitch audioConnection = (BMDAudioOutputAnalogAESSwitch) 0;
193 int cardIdx = 0;
194 int dnum = 0;
195 IDeckLink *deckLink;
196 IDeckLinkDisplayModeIterator *displayModeIterator = NULL;
197 IDeckLinkDisplayMode *deckLinkDisplayMode = NULL;
198 //BMDDisplayMode displayMode = bmdModeUnknown;
199 int width, height;
200
201 s = (struct state_decklink *)calloc(1, sizeof(struct state_decklink));
203 s->audio_consumer_levels = -1;
204
205 if (strlen(cfg) == 0) {
206 cardIdx = 0;
207 fprintf(stderr, "Card number unset, using first found (see -r decklink:help)!\n");
208 } else if (strcmp(cfg, "help") == 0) {
209 audio_play_decklink_help();
210 free(s);
211 return INIT_NOERR;
212 } else {
213 char *tmp = strdup(cfg);
214 char *item, *save_ptr;
215 item = strtok_r(tmp, ":", &save_ptr);
216 if (item) {
217 if(strncasecmp(item, "audioConsumerLevels=",
218 strlen("audioConsumerLevels=")) == 0) {
219 char *levels = item + strlen("audioConsumerLevels=");
220 if (strcasecmp(levels, "false") == 0) {
222 } else {
224 }
225 item = strtok_r(NULL, ":", &save_ptr);
226 if (item) {
227 cardIdx = atoi(cfg);
228 }
229 } else {
230 cardIdx = atoi(cfg);
231 }
232 }
233 free(tmp);
234 }
235
237 free(s);
238 return NULL;
239 }
240
241 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Audio-only DeckLink output, if also video is needed, use "
242 "\"-d decklink -r analog\" instead.\n");
243
244 // Initialize the DeckLink API
245 deckLinkIterator = create_decklink_iterator(&s->com_initialized, true);
246 if (!deckLinkIterator) {
247 goto error;
248 }
249
250 // Connect to the first DeckLink instance
251 while (deckLinkIterator->Next(&deckLink) == S_OK)
252 {
253 if (dnum == cardIdx){
254 s->deckLink = deckLink;
255 } else {
256 deckLink->Release();
257 }
258 dnum++;
259 }
260
261 if(s->deckLink == NULL) {
262 fprintf(stderr, "No DeckLink PCI card #%d found\n", cardIdx);
263 goto error;
264 }
265
266 // Obtain the audio/video output interface (IDeckLinkOutput)
267 if (s->deckLink->QueryInterface(IID_IDeckLinkOutput, (void**)&s->deckLinkOutput) != S_OK) {
268 goto error;
269 }
270
271 // Query the DeckLink for its configuration interface
272 result = s->deckLink->QueryInterface(IID_IDeckLinkConfiguration, (void**)&deckLinkConfiguration);
273 if (result != S_OK)
274 {
275 printf("Could not obtain the IDeckLinkConfiguration interface: %08x\n", (int) result);
276 goto error;
277 }
278
279 if (s->audio_consumer_levels != -1) {
280 result = deckLinkConfiguration->SetFlag(bmdDeckLinkConfigAnalogAudioConsumerLevels,
281 s->audio_consumer_levels == 1 ? true : false);
282 if(result != S_OK) {
283 fprintf(stderr, "[DeckLink capture] Unable set input audio consumer levels.\n");
284 }
285 }
286 deckLinkConfiguration->Release();
287 deckLinkConfiguration = NULL;
288
289 // Populate the display mode combo with a list of display modes supported by the installed DeckLink card
290 if (FAILED(s->deckLinkOutput->GetDisplayModeIterator(&displayModeIterator)))
291 {
292 fprintf(stderr, "Fatal: cannot create display mode iterator [decklink].\n");
293 goto error;
294 }
295
296 // pick first display mode, no matter which it is
297 if(displayModeIterator->Next(&deckLinkDisplayMode) == S_OK)
298 {
299 width = deckLinkDisplayMode->GetWidth();
300 height = deckLinkDisplayMode->GetHeight();
301 deckLinkDisplayMode->GetFrameRate(&s->frameRateDuration, &s->frameRateScale);
302 } else {
303 fprintf(stderr, "[decklink] Fatal: cannot get any display mode.\n");
304 goto error;
305 }
306
307 s->frames = 0;
308
309 s->deckLinkOutput->CreateVideoFrame(width, height, width*2, bmdFormat8BitYUV, bmdFrameFlagDefault, &s->deckLinkFrame);
310
311 s->delegate = new PlaybackDelegate(s);
312 // Provide this class as a delegate to the audio and video output interfaces
313 s->deckLinkOutput->SetScheduledFrameCompletionCallback(s->delegate);
314 //s->state[i].deckLinkOutput->DisableAudioOutput();
315 //
316 s->deckLinkOutput->EnableVideoOutput(deckLinkDisplayMode->GetDisplayMode(), bmdVideoOutputFlagDefault);
317
318 displayModeIterator->Release();
319 displayModeIterator = NULL;
320 deckLinkDisplayMode->Release();
321 deckLinkDisplayMode = NULL;
322
323
324 return (void *)s;
325
326error:
327 if (displayModeIterator)
328 displayModeIterator->Release();
329 if (deckLinkDisplayMode)
330 deckLinkDisplayMode->Release();
331 if (deckLinkConfiguration)
332 deckLinkConfiguration->Release();
333 if (s->deckLinkOutput != NULL)
334 s->deckLinkOutput->Release();
335 if (s->deckLink != NULL)
336 s->deckLink->Release();
337 free(s);
338 return NULL;
339}
340
341static void audio_play_decklink_put_frame(void *state, const struct audio_frame *frame)
342{
343 struct state_decklink *s = (struct state_decklink *)state;
344 unsigned int sampleFrameCount = frame->data_len /
346 uint32_t sampleFramesWritten;
347 char *data = frame->data;
348 // tmp_frame is used if we need to perform 1->2 channel multiplication
349 struct audio_frame tmp_frame;
350 tmp_frame.data = NULL;
351
352 /* we got probably channel count that cannot be played directly (probably 1) */
354 assert(s->audio_desc.ch_count == 1); /* only supported value so far */
355 memcpy(&tmp_frame, frame, sizeof(tmp_frame));
356 // allocate enough space to hold resulting data
357 tmp_frame.max_size = sampleFrameCount * s->output_audio_channel_count
358 * frame->bps;
359 tmp_frame.data = (char *) malloc(tmp_frame.max_size);
360 memcpy(tmp_frame.data, frame->data, frame->data_len);
361
364
365 data = tmp_frame.data;
366 }
367
368 s->deckLinkOutput->ScheduleAudioSamples (data, sampleFrameCount, 0,
369 0, &sampleFramesWritten);
370 if(sampleFramesWritten != sampleFrameCount)
371 LOG(LOG_LEVEL_ERROR) << "[decklink] audio buffer overflow! (" << sampleFramesWritten << " written, " << sampleFrameCount - sampleFramesWritten << "\n";
372
373 free(tmp_frame.data);
374
375}
376
377static bool audio_play_decklink_ctl(void *state [[gnu::unused]], int request, void *data, size_t *len)
378{
379 switch (request) {
381 if (*len >= sizeof(struct audio_desc)) {
382 struct audio_desc desc;
383 memcpy(&desc, data, sizeof desc);
384 desc = audio_desc{desc.bps <= 2 ? 2 : 4, 48000, desc.ch_count <= 2 ? 2 :
385 (desc.ch_count <= 8 ? 8 : 16), AC_PCM};
386 memcpy(data, &desc, sizeof desc);
387 *len = sizeof desc;
388 return true;
389 } else {
390 return false;
391 }
392 default:
393 return false;
394 }
395}
396
397static bool audio_play_decklink_reconfigure(void *state, struct audio_desc desc) {
398 struct state_decklink *s = (struct state_decklink *)state;
399 BMDAudioSampleType sample_type;
400
401 s->audio_desc = desc;
402 s->output_audio_channel_count = desc.ch_count;
403
404 if (s->audio_desc.ch_count != 1 &&
405 s->audio_desc.ch_count != 2 &&
406 s->audio_desc.ch_count != 8 &&
407 s->audio_desc.ch_count != 16) {
408 fprintf(stderr, "[decklink] requested channel count isn't supported: "
409 "%d\n", s->audio_desc.ch_count);
410 return false;
411 }
412
413 /* toggle one channel to supported two */
414 if(s->audio_desc.ch_count == 1) {
416 }
417
418 if((desc.bps != 2 && desc.bps != 4) ||
419 desc.sample_rate != 48000) {
420 LOG(LOG_LEVEL_ERROR) << "[decklink] audio format isn't supported: " <<
421 desc;
422 return false;
423 }
424 switch(desc.bps) {
425 case 2:
426 sample_type = bmdAudioSampleType16bitInteger;
427 break;
428 case 4:
429 sample_type = bmdAudioSampleType32bitInteger;
430 break;
431 default:
432 return false;
433 }
434
435 s->deckLinkOutput->EnableAudioOutput(bmdAudioSampleRate48kHz,
436 sample_type,
438 bmdAudioOutputStreamContinuous);
439 s->deckLinkOutput->StartScheduledPlayback(0, s->frameRateScale, s->frameRateDuration);
440
441 return true;
442}
443
444static void audio_play_decklink_done(void *state)
445{
446 struct state_decklink *s = (struct state_decklink *)state;
447
448 s->deckLinkOutput->DisableAudioOutput();
449 s->deckLinkOutput->DisableVideoOutput();
450 s->deckLinkFrame->Release();
451 s->deckLink->Release();
452 s->deckLinkOutput->Release();
454 free(s);
455}
456
457static const struct audio_playback_info aplay_decklink_info = {
458 audio_play_decklink_probe,
459 audio_play_decklink_init,
460 audio_play_decklink_put_frame,
461 audio_play_decklink_ctl,
462 audio_play_decklink_reconfigure,
463 audio_play_decklink_done
464};
465
467
468} // end of unnamed namespace
469
void ** state
struct video_desc desc
struct video_frame * frame
int void size_t * len
int void(** deleter)(void *))
This file contains definition of some common audio types.
@ AC_PCM
Definition types.h:53
#define MOD_NAME
Definition audio.cpp:110
#define AUDIO_PLAYBACK_CTL_QUERY_FORMAT
#define AUDIO_PLAYBACK_ABI_VERSION
bool blackmagic_api_version_check()
IDeckLinkIterator * create_decklink_iterator(bool *com_initialized, bool verbose)
void release_bmd_api_str(BMD_STR string)
void decklink_uninitialize(bool *com_initialized)
char * get_cstr_from_bmd_api_str(BMD_STR bmd_string)
#define BMD_STR
#define SRED(x)
Definition color_out.h:85
#define SBOLD(x)
Definition color_out.h:80
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define LOG(level)
Definition debug.h:313
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
int count
Definition hd-rum.c:44
#define INIT_NOERR
Definition host.h:73
@ LIBRARY_CLASS_AUDIO_PLAYBACK
Definition lib_common.h:77
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
Definition lib_common.h:144
const char * name
Definition settings.cpp:193
int ch_count
Definition types.h:79
int bps
Definition types.h:77
struct used to comunicate with audio capture/playback device
Definition types.h:99
char * data
Definition types.h:102
Definition hd-rum.c:27
void audio_frame_multiply_channel(struct audio_frame *frame, int new_channel_count)
Definition utils.cpp:328
@ FAILED