1 /*****************************************************************************
2 * decklink.cpp: BlackMagic DeckLink SDI output module
3 *****************************************************************************
4 * Copyright (C) 2012-2013 Rafaël Carré
5 * Copyright (C) 2009 Michael Niedermayer <michaelni@gmx.at>
6 * Copyright (c) 2009 Baptiste Coudurier <baptiste dot coudurier at gmail dot com>
7 *
8 * Authors: Rafaël Carré <funman@videolan.org>
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
24
25 /*
26 * TODO: test non stereo audio
27 */
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc_fixups.h>
34 #include <cinttypes>
35
36 #include <vlc_common.h>
37 #include <vlc_plugin.h>
38 #include <vlc_threads.h>
39
40 #include <vlc_vout_display.h>
41 #include <vlc_picture_pool.h>
42
43 #include <vlc_block.h>
44 #include <vlc_image.h>
45 #include <vlc_aout.h>
46 #include <arpa/inet.h>
47
48 #include <DeckLinkAPI.h>
49 #include <DeckLinkAPIDispatch.cpp>
50 #include <DeckLinkAPIVersion.h>
51 #if BLACKMAGIC_DECKLINK_API_VERSION < 0x0b010000
52 #define IID_IDeckLinkProfileAttributes IID_IDeckLinkAttributes
53 #define IDeckLinkProfileAttributes IDeckLinkAttributes
54 #endif
55
56 #define FRAME_SIZE 1920
57 #define CHANNELS_MAX 6
58
59 #if 0
60 static const int pi_channels_maps[CHANNELS_MAX+1] =
61 {
62 0,
63 AOUT_CHAN_CENTER,
64 AOUT_CHANS_STEREO,
65 AOUT_CHANS_3_0,
66 AOUT_CHANS_4_0,
67 AOUT_CHANS_5_0,
68 AOUT_CHANS_5_1,
69 };
70 #endif
71
72 #define NOSIGNAL_INDEX_TEXT N_("Timelength after which we assume there is no signal.")
73 #define NOSIGNAL_INDEX_LONGTEXT N_(\
74 "Timelength after which we assume there is no signal.\n"\
75 "After this delay we black out the video."\
76 )
77
78 #define AFD_INDEX_TEXT N_("Active Format Descriptor value")
79
80 #define AR_INDEX_TEXT N_("Aspect Ratio")
81 #define AR_INDEX_LONGTEXT N_("Aspect Ratio of the source picture.")
82
83 #define AFDLINE_INDEX_TEXT N_("Active Format Descriptor line")
84 #define AFDLINE_INDEX_LONGTEXT N_("VBI line on which to output Active Format Descriptor.")
85
86 #define NOSIGNAL_IMAGE_TEXT N_("Picture to display on input signal loss")
87 #define NOSIGNAL_IMAGE_LONGTEXT NOSIGNAL_IMAGE_TEXT
88
89 #define CARD_INDEX_TEXT N_("Output card")
90 #define CARD_INDEX_LONGTEXT N_(\
91 "DeckLink output card, if multiple exist. " \
92 "The cards are numbered from 0.")
93
94 #define MODE_TEXT N_("Desired output mode")
95 #define MODE_LONGTEXT N_(\
96 "Desired output mode for DeckLink output. " \
97 "This value should be a FOURCC code in textual " \
98 "form, e.g. \"ntsc\".")
99
100 #define AUDIO_CONNECTION_TEXT N_("Audio connection")
101 #define AUDIO_CONNECTION_LONGTEXT N_(\
102 "Audio connection for DeckLink output.")
103
104
105 #define RATE_TEXT N_("Audio samplerate (Hz)")
106 #define RATE_LONGTEXT N_(\
107 "Audio sampling rate (in hertz) for DeckLink output. " \
108 "0 disables audio output.")
109
110 #define CHANNELS_TEXT N_("Number of audio channels")
111 #define CHANNELS_LONGTEXT N_(\
112 "Number of output channels for DeckLink output. " \
113 "Must be 2, 8 or 16. 0 disables audio output.")
114
115 #define VIDEO_CONNECTION_TEXT N_("Video connection")
116 #define VIDEO_CONNECTION_LONGTEXT N_(\
117 "Video connection for DeckLink output.")
118
119 #define VIDEO_TENBITS_TEXT N_("10 bits")
120 #define VIDEO_TENBITS_LONGTEXT N_(\
121 "Use 10 bits per pixel for video frames.")
122
123 #define CFG_PREFIX "decklink-output-"
124 #define VIDEO_CFG_PREFIX "decklink-vout-"
125 #define AUDIO_CFG_PREFIX "decklink-aout-"
126
127 /* Video Connections */
128 static const char *const ppsz_videoconns[] = {
129 "sdi",
130 "hdmi",
131 "opticalsdi",
132 "component",
133 "composite",
134 "svideo"
135 };
136 static const char *const ppsz_videoconns_text[] = {
137 "SDI",
138 "HDMI",
139 "Optical SDI",
140 "Component",
141 "Composite",
142 "S-video",
143 };
144 static const BMDVideoConnection rgbmd_videoconns[] =
145 {
146 bmdVideoConnectionSDI,
147 bmdVideoConnectionHDMI,
148 bmdVideoConnectionOpticalSDI,
149 bmdVideoConnectionComponent,
150 bmdVideoConnectionComposite,
151 bmdVideoConnectionSVideo,
152 };
153 static_assert(ARRAY_SIZE(rgbmd_videoconns) == ARRAY_SIZE(ppsz_videoconns), "videoconn arrays messed up");
154 static_assert(ARRAY_SIZE(rgbmd_videoconns) == ARRAY_SIZE(ppsz_videoconns_text), "videoconn arrays messed up");
155
156 static const int rgi_afd_values[] = {
157 0, 2, 3, 4, 8, 9, 10, 11, 13, 14, 15,
158 };
159 static const char * const rgsz_afd_text[] = {
160 "0: Undefined",
161 "2: Box 16:9 (top aligned)",
162 "3: Box 14:9 (top aligned)",
163 "4: Box > 16:9 (centre aligned)",
164 "8: Same as coded frame (full frame)",
165 "9: 4:3 (centre aligned)",
166 "10: 16:9 (centre aligned)",
167 "11: 14:9 (centre aligned)",
168 "13: 4:3 (with shoot and protect 14:9 centre)",
169 "14: 16:9 (with shoot and protect 14:9 centre)",
170 "15: 16:9 (with shoot and protect 4:3 centre)",
171 };
172 static_assert(ARRAY_SIZE(rgi_afd_values) == ARRAY_SIZE(rgsz_afd_text), "afd arrays messed up");
173
174 static const int rgi_ar_values[] = {
175 0, 1,
176 };
177 static const char * const rgsz_ar_text[] = {
178 "0: 4:3",
179 "1: 16:9",
180 };
181 static_assert(ARRAY_SIZE(rgi_ar_values) == ARRAY_SIZE(rgsz_ar_text), "afd arrays messed up");
182
183 /* Only one audio output module and one video output module
184 * can be used per process.
185 * We use a static mutex in audio/video submodules entry points. */
186 typedef struct decklink_sys_t
187 {
188 /* With LOCK */
189 IDeckLinkOutput *p_output;
190
191 /*
192 * Synchronizes aout and vout modules:
193 * vout module waits until aout has been initialized.
194 * That means video-only output is NOT supported.
195 */
196 vlc_mutex_t lock;
197 vlc_cond_t cond;
198 uint8_t users;
199 bool b_videomodule;
200 bool b_recycling;
201
202 //int i_channels;
203 int i_rate;
204
205 BMDTimeScale timescale;
206 BMDTimeValue frameduration;
207
208 /* XXX: workaround card clock drift */
209 mtime_t offset;
210
211 /* !With LOCK */
212
213 /* single video module exclusive */
214 struct
215 {
216 video_format_t currentfmt;
217 picture_pool_t *pool;
218 bool tenbits;
219 uint8_t afd, ar;
220 int nosignal_delay;
221 picture_t *pic_nosignal;
222 } video;
223 } decklink_sys_t;
224
225 /*****************************************************************************
226 * Local prototypes.
227 *****************************************************************************/
228
229 static int OpenVideo (vlc_object_t *);
230 static void CloseVideo (vlc_object_t *);
231 static int OpenAudio (vlc_object_t *);
232 static void CloseAudio (vlc_object_t *);
233
234 /*****************************************************************************
235 * Module descriptor
236 *****************************************************************************/
237
238 vlc_module_begin()
239 set_shortname(N_("DecklinkOutput"))
240 set_description(N_("Output module to write to Blackmagic SDI card"))
241 set_section(N_("DeckLink General Options"), NULL)
242 add_integer(CFG_PREFIX "card-index", 0,
243 CARD_INDEX_TEXT, CARD_INDEX_LONGTEXT, true)
244
245 add_submodule ()
246 set_description (N_("DeckLink Video Output module"))
247 set_category(CAT_VIDEO)
248 set_subcategory(SUBCAT_VIDEO_VOUT)
249 set_capability("vout display", 0)
250 set_callbacks (OpenVideo, CloseVideo)
251 set_section(N_("DeckLink Video Options"), NULL)
252 add_string(VIDEO_CFG_PREFIX "video-connection", "sdi",
253 VIDEO_CONNECTION_TEXT, VIDEO_CONNECTION_LONGTEXT, true)
254 change_string_list(ppsz_videoconns, ppsz_videoconns_text)
255 add_string(VIDEO_CFG_PREFIX "mode", "",
256 MODE_TEXT, MODE_LONGTEXT, true)
257 add_bool(VIDEO_CFG_PREFIX "tenbits", true,
258 VIDEO_TENBITS_TEXT, VIDEO_TENBITS_LONGTEXT, true)
259 add_integer(VIDEO_CFG_PREFIX "nosignal-delay", 5,
260 NOSIGNAL_INDEX_TEXT, NOSIGNAL_INDEX_LONGTEXT, true)
261 add_integer(VIDEO_CFG_PREFIX "afd-line", 16,
262 AFDLINE_INDEX_TEXT, AFDLINE_INDEX_LONGTEXT, true)
263 add_integer_with_range(VIDEO_CFG_PREFIX "afd", 8, 0, 16,
264 AFD_INDEX_TEXT, AFD_INDEX_TEXT, true)
265 change_integer_list(rgi_afd_values, rgsz_afd_text)
266 add_integer_with_range(VIDEO_CFG_PREFIX "ar", 1, 0, 1,
267 AR_INDEX_TEXT, AR_INDEX_LONGTEXT, true)
268 change_integer_list(rgi_ar_values, rgsz_ar_text)
269 add_loadfile(VIDEO_CFG_PREFIX "nosignal-image", NULL,
270 NOSIGNAL_IMAGE_TEXT, NOSIGNAL_IMAGE_LONGTEXT, true)
271
272
273 add_submodule ()
274 set_description (N_("DeckLink Audio Output module"))
275 set_category(CAT_AUDIO)
276 set_subcategory(SUBCAT_AUDIO_AOUT)
277 set_capability("audio output", 0)
278 set_callbacks (OpenAudio, CloseAudio)
279 set_section(N_("DeckLink Audio Options"), NULL)
280 add_obsolete_string("audio-connection")
281 add_integer(AUDIO_CFG_PREFIX "audio-rate", 48000,
282 RATE_TEXT, RATE_LONGTEXT, true)
283 add_integer(AUDIO_CFG_PREFIX "audio-channels", 2,
284 CHANNELS_TEXT, CHANNELS_LONGTEXT, true)
285 vlc_module_end ()
286
287 /* Protects decklink_sys_t creation/deletion */
288 static vlc_mutex_t sys_lock = VLC_STATIC_MUTEX;
289
HoldDLSys(vlc_object_t * obj,int i_cat)290 static decklink_sys_t *HoldDLSys(vlc_object_t *obj, int i_cat)
291 {
292 vlc_object_t *libvlc = VLC_OBJECT(obj->obj.libvlc);
293 decklink_sys_t *sys;
294
295 vlc_mutex_lock(&sys_lock);
296
297 if (var_Type(libvlc, "decklink-sys") == VLC_VAR_ADDRESS)
298 {
299 sys = (decklink_sys_t*)var_GetAddress(libvlc, "decklink-sys");
300 sys->users++;
301
302 if(i_cat == VIDEO_ES)
303 {
304 while(sys->b_videomodule)
305 {
306 vlc_mutex_unlock(&sys_lock);
307 msg_Info(obj, "Waiting for previous vout module to exit");
308 msleep(CLOCK_FREQ / 10);
309 vlc_mutex_lock(&sys_lock);
310 }
311 }
312 }
313 else
314 {
315 sys = (decklink_sys_t*)malloc(sizeof(*sys));
316 if (sys) {
317 sys->p_output = NULL;
318 sys->offset = 0;
319 sys->users = 1;
320 sys->b_videomodule = (i_cat == VIDEO_ES);
321 sys->b_recycling = false;
322 sys->i_rate = var_InheritInteger(obj, AUDIO_CFG_PREFIX "audio-rate");
323 if(sys->i_rate > 0)
324 sys->i_rate = -1;
325 vlc_mutex_init(&sys->lock);
326 vlc_cond_init(&sys->cond);
327 var_Create(libvlc, "decklink-sys", VLC_VAR_ADDRESS);
328 var_SetAddress(libvlc, "decklink-sys", (void*)sys);
329 }
330 }
331
332 vlc_mutex_unlock(&sys_lock);
333 return sys;
334 }
335
ReleaseDLSys(vlc_object_t * obj,int i_cat)336 static void ReleaseDLSys(vlc_object_t *obj, int i_cat)
337 {
338 vlc_object_t *libvlc = VLC_OBJECT(obj->obj.libvlc);
339
340 vlc_mutex_lock(&sys_lock);
341
342 struct decklink_sys_t *sys = (struct decklink_sys_t*)var_GetAddress(libvlc, "decklink-sys");
343
344 if (--sys->users == 0) {
345 msg_Dbg(obj, "Destroying decklink data");
346 vlc_mutex_destroy(&sys->lock);
347 vlc_cond_destroy(&sys->cond);
348
349 if (sys->p_output) {
350 sys->p_output->StopScheduledPlayback(0, NULL, 0);
351 sys->p_output->DisableVideoOutput();
352 sys->p_output->DisableAudioOutput();
353 sys->p_output->Release();
354 }
355
356 /* Clean video specific */
357 if (sys->video.pool)
358 picture_pool_Release(sys->video.pool);
359 if (sys->video.pic_nosignal)
360 picture_Release(sys->video.pic_nosignal);
361 video_format_Clean(&sys->video.currentfmt);
362
363 free(sys);
364 var_Destroy(libvlc, "decklink-sys");
365 }
366 else if (i_cat == VIDEO_ES)
367 {
368 sys->b_videomodule = false;
369 sys->b_recycling = true;
370 }
371
372 vlc_mutex_unlock(&sys_lock);
373 }
374
getVConn(vout_display_t * vd,BMDVideoConnection mask)375 static BMDVideoConnection getVConn(vout_display_t *vd, BMDVideoConnection mask)
376 {
377 BMDVideoConnection conn = 0;
378 char *psz = var_InheritString(vd, VIDEO_CFG_PREFIX "video-connection");
379 if (psz)
380 {
381 for(size_t i=0; i<ARRAY_SIZE(rgbmd_videoconns); i++)
382 {
383 if (!strcmp(psz, ppsz_videoconns[i]) && (mask & rgbmd_videoconns[i]))
384 {
385 conn = rgbmd_videoconns[i];
386 break;
387 }
388 }
389 free(psz);
390 }
391 else /* Pick one as default connection */
392 {
393 conn = ctz(mask);
394 conn = conn ? ( 1 << conn ) : bmdVideoConnectionSDI;
395 }
396 return conn;
397 }
398
399 /*****************************************************************************
400 *
401 *****************************************************************************/
402
403 static struct
404 {
405 long i_return_code;
406 const char * const psz_string;
407 } const errors_to_string[] = {
408 { E_UNEXPECTED, "Unexpected error" },
409 { E_NOTIMPL, "Not implemented" },
410 { E_OUTOFMEMORY, "Out of memory" },
411 { E_INVALIDARG, "Invalid argument" },
412 { E_NOINTERFACE, "No interface" },
413 { E_POINTER, "Invalid pointer" },
414 { E_HANDLE, "Invalid handle" },
415 { E_ABORT, "Aborted" },
416 { E_FAIL, "Failed" },
417 { E_ACCESSDENIED,"Access denied" }
418 };
419
lookup_error_string(long i_code)420 static const char * lookup_error_string(long i_code)
421 {
422 for(size_t i=0; i<ARRAY_SIZE(errors_to_string); i++)
423 {
424 if(errors_to_string[i].i_return_code == i_code)
425 return errors_to_string[i].psz_string;
426 }
427 return NULL;
428 }
429
CreateNoSignalPicture(vlc_object_t * p_this,const video_format_t * fmt,const char * psz_file)430 static picture_t * CreateNoSignalPicture(vlc_object_t *p_this, const video_format_t *fmt,
431 const char *psz_file)
432 {
433 picture_t *p_pic = NULL;
434 image_handler_t *img = image_HandlerCreate(p_this);
435 if (!img)
436 {
437 msg_Err(p_this, "Could not create image converter");
438 return NULL;
439 }
440
441 video_format_t in, dummy;
442 video_format_Init(&dummy, 0);
443 video_format_Init(&in, 0);
444 video_format_Setup(&in, 0,
445 fmt->i_width, fmt->i_height,
446 fmt->i_width, fmt->i_height, 1, 1);
447
448 picture_t *png = image_ReadUrl(img, psz_file, &dummy, &in);
449 if (png)
450 {
451 video_format_Clean(&dummy);
452 video_format_Copy(&dummy, fmt);
453 p_pic = image_Convert(img, png, &in, &dummy);
454 if(!video_format_IsSimilar(&dummy, fmt))
455 {
456 picture_Release(p_pic);
457 p_pic = NULL;
458 }
459 picture_Release(png);
460 }
461 image_HandlerDelete(img);
462 video_format_Clean(&in);
463 video_format_Clean(&dummy);
464
465 return p_pic;
466 }
467
MatchDisplayMode(vout_display_t * vd,IDeckLinkOutput * output,const video_format_t * fmt,BMDDisplayMode forcedmode=bmdModeUnknown)468 static IDeckLinkDisplayMode * MatchDisplayMode(vout_display_t *vd,
469 IDeckLinkOutput *output,
470 const video_format_t *fmt,
471 BMDDisplayMode forcedmode = bmdModeUnknown)
472 {
473 HRESULT result;
474 IDeckLinkDisplayMode *p_selected = NULL;
475 IDeckLinkDisplayModeIterator *p_iterator = NULL;
476
477 for(int i=0; i<4 && p_selected==NULL; i++)
478 {
479 int i_width = (i % 2 == 0) ? fmt->i_width : fmt->i_visible_width;
480 int i_height = (i % 2 == 0) ? fmt->i_height : fmt->i_visible_height;
481 int i_div = (i > 2) ? 4 : 0;
482
483 result = output->GetDisplayModeIterator(&p_iterator);
484 if(result == S_OK)
485 {
486 IDeckLinkDisplayMode *p_mode = NULL;
487 while(p_iterator->Next(&p_mode) == S_OK)
488 {
489 BMDDisplayMode mode_id = p_mode->GetDisplayMode();
490 BMDTimeValue frameduration;
491 BMDTimeScale timescale;
492 const char *psz_mode_name;
493
494 if(p_mode->GetFrameRate(&frameduration, ×cale) == S_OK &&
495 p_mode->GetName(&psz_mode_name) == S_OK)
496 {
497 BMDDisplayMode modenl = htonl(mode_id);
498 if(i==0)
499 {
500 BMDFieldDominance field = htonl(p_mode->GetFieldDominance());
501 msg_Dbg(vd, "Found mode '%4.4s': %s (%ldx%ld, %.3f fps, %4.4s, scale %ld dur %ld)",
502 (char*)&modenl, psz_mode_name,
503 p_mode->GetWidth(), p_mode->GetHeight(),
504 (char *)&field,
505 double(timescale) / frameduration,
506 timescale, frameduration);
507 }
508 }
509 else
510 {
511 p_mode->Release();
512 continue;
513 }
514
515 if(forcedmode != bmdModeUnknown && unlikely(!p_selected))
516 {
517 BMDDisplayMode modenl = htonl(forcedmode);
518 msg_Dbg(vd, "Forced mode '%4.4s'", (char *)&modenl);
519 if(forcedmode == mode_id)
520 p_selected = p_mode;
521 else
522 p_mode->Release();
523 continue;
524 }
525
526 if(p_selected == NULL && forcedmode == bmdModeUnknown)
527 {
528 if(i_width >> i_div == p_mode->GetWidth() >> i_div &&
529 i_height >> i_div == p_mode->GetHeight() >> i_div)
530 {
531 unsigned int num_deck, den_deck;
532 unsigned int num_stream, den_stream;
533 vlc_ureduce(&num_deck, &den_deck, timescale, frameduration, 0);
534 vlc_ureduce(&num_stream, &den_stream,
535 fmt->i_frame_rate, fmt->i_frame_rate_base, 0);
536
537 if (num_deck == num_stream && den_deck == den_stream)
538 {
539 msg_Info(vd, "Matches incoming stream");
540 p_selected = p_mode;
541 continue;
542 }
543 }
544 }
545
546 p_mode->Release();
547 }
548 p_iterator->Release();
549 }
550 }
551 return p_selected;
552 }
553
OpenDecklink(vout_display_t * vd,decklink_sys_t * sys)554 static int OpenDecklink(vout_display_t *vd, decklink_sys_t *sys)
555 {
556 #define CHECK(message) do { \
557 if (result != S_OK) \
558 { \
559 const char *psz_err = lookup_error_string(result); \
560 if(psz_err)\
561 msg_Err(vd, message ": %s", psz_err); \
562 else \
563 msg_Err(vd, message ": 0x%X", result); \
564 goto error; \
565 } \
566 } while(0)
567
568 HRESULT result;
569 IDeckLinkIterator *decklink_iterator = NULL;
570 IDeckLinkDisplayMode *p_display_mode = NULL;
571 IDeckLinkConfiguration *p_config = NULL;
572 IDeckLinkProfileAttributes *p_attributes = NULL;
573 IDeckLink *p_card = NULL;
574 BMDDisplayMode wanted_mode_id = bmdModeUnknown;
575
576 vlc_mutex_lock(&sys->lock);
577
578 /* wait until aout is ready */
579 msg_Info(vd, "Waiting for DeckLink audio input module to start");
580 while (sys->i_rate == -1)
581 vlc_cond_wait(&sys->cond, &sys->lock);
582
583 int i_card_index = var_InheritInteger(vd, CFG_PREFIX "card-index");
584 char *mode = var_InheritString(vd, VIDEO_CFG_PREFIX "mode");
585
586 if(mode)
587 {
588 size_t len = strlen(mode);
589 if (len > 4)
590 {
591 free(mode);
592 msg_Err(vd, "Invalid mode %s", mode);
593 goto error;
594 }
595 memset(&wanted_mode_id, ' ', 4);
596 strncpy((char*)&wanted_mode_id, mode, 4);
597 wanted_mode_id = ntohl(wanted_mode_id);
598 free(mode);
599 }
600
601 if (i_card_index < 0)
602 {
603 msg_Err(vd, "Invalid card index %d", i_card_index);
604 goto error;
605 }
606
607 decklink_iterator = CreateDeckLinkIteratorInstance();
608 if (!decklink_iterator)
609 {
610 msg_Err(vd, "DeckLink drivers not found.");
611 goto error;
612 }
613
614 for(int i = 0; i <= i_card_index; ++i)
615 {
616 if (p_card)
617 p_card->Release();
618 result = decklink_iterator->Next(&p_card);
619 CHECK("Card not found");
620 }
621
622 const char *psz_model_name;
623 result = p_card->GetModelName(&psz_model_name);
624 CHECK("Unknown model name");
625
626 msg_Dbg(vd, "Opened DeckLink PCI card %s", psz_model_name);
627
628 /* Read attributes */
629
630 result = p_card->QueryInterface(IID_IDeckLinkProfileAttributes, (void**)&p_attributes);
631 CHECK("Could not get IDeckLinkAttributes");
632
633 int64_t vconn;
634 result = p_attributes->GetInt(BMDDeckLinkVideoOutputConnections, &vconn); /* reads mask */
635 CHECK("Could not get BMDDeckLinkVideoOutputConnections");
636
637 result = p_card->QueryInterface(IID_IDeckLinkOutput,
638 (void**)&sys->p_output);
639 CHECK("No outputs");
640
641 result = p_card->QueryInterface(IID_IDeckLinkConfiguration,
642 (void**)&p_config);
643 CHECK("Could not get config interface");
644
645 /* Now configure card */
646
647 vconn = getVConn(vd, (BMDVideoConnection) vconn);
648 if (vconn == 0)
649 {
650 msg_Err(vd, "Invalid video connection specified");
651 goto error;
652 }
653
654 result = p_config->SetInt(bmdDeckLinkConfigVideoOutputConnection, (BMDVideoConnection) vconn);
655 CHECK("Could not set video output connection");
656
657 p_display_mode = MatchDisplayMode(vd, sys->p_output,
658 &vd->fmt, wanted_mode_id);
659 if(p_display_mode == NULL)
660 {
661 msg_Err(vd, "Could not negociate a compatible display mode");
662 goto error;
663 }
664 else
665 {
666 BMDDisplayMode mode_id = p_display_mode->GetDisplayMode();
667 BMDDisplayMode modenl = htonl(mode_id);
668 msg_Dbg(vd, "Selected mode '%4.4s'", (char *) &modenl);
669
670 BMDPixelFormat pixelFormat = sys->video.tenbits ? bmdFormat10BitYUV : bmdFormat8BitYUV;
671 BMDVideoOutputFlags flags = bmdVideoOutputVANC;
672 if (mode_id == bmdModeNTSC ||
673 mode_id == bmdModeNTSC2398 ||
674 mode_id == bmdModePAL)
675 {
676 flags = bmdVideoOutputVITC;
677 }
678 bool supported;
679 #if BLACKMAGIC_DECKLINK_API_VERSION < 0x0b010000
680 BMDDisplayModeSupport support = bmdDisplayModeNotSupported;
681 result = sys->p_output->DoesSupportVideoMode(mode_id,
682 pixelFormat,
683 flags,
684 &support,
685 NULL);
686 supported = (support != bmdDisplayModeNotSupported);
687 #else
688 result = sys->p_output->DoesSupportVideoMode(vconn,
689 mode_id,
690 pixelFormat,
691 bmdSupportedVideoModeDefault,
692 NULL,
693 &supported);
694 #endif
695 CHECK("Does not support video mode");
696 if (!supported)
697 {
698 msg_Err(vd, "Video mode not supported");
699 goto error;
700 }
701
702 if (p_display_mode->GetWidth() <= 0 || p_display_mode->GetWidth() & 1)
703 {
704 msg_Err(vd, "Unknown video mode specified.");
705 goto error;
706 }
707
708 result = p_display_mode->GetFrameRate(&sys->frameduration,
709 &sys->timescale);
710 CHECK("Could not read frame rate");
711
712 result = sys->p_output->EnableVideoOutput(mode_id, flags);
713 CHECK("Could not enable video output");
714
715 video_format_t *fmt = &sys->video.currentfmt;
716 video_format_Copy(fmt, &vd->fmt);
717 fmt->i_width = fmt->i_visible_width = p_display_mode->GetWidth();
718 fmt->i_height = fmt->i_visible_height = p_display_mode->GetHeight();
719 fmt->i_x_offset = 0;
720 fmt->i_y_offset = 0;
721 fmt->i_sar_num = 0;
722 fmt->i_sar_den = 0;
723 fmt->i_chroma = !sys->video.tenbits ? VLC_CODEC_UYVY : VLC_CODEC_I422_10L; /* we will convert to v210 */
724 fmt->i_frame_rate = (unsigned) sys->frameduration;
725 fmt->i_frame_rate_base = (unsigned) sys->timescale;
726 }
727
728 if (/*decklink_sys->i_channels > 0 &&*/ sys->i_rate > 0)
729 {
730 result = sys->p_output->EnableAudioOutput(
731 sys->i_rate,
732 bmdAudioSampleType16bitInteger,
733 /*decklink_sys->i_channels*/ 2,
734 bmdAudioOutputStreamTimestamped);
735 CHECK("Could not start audio output");
736 }
737
738 /* start */
739 result = sys->p_output->StartScheduledPlayback(
740 (mdate() * sys->timescale) / CLOCK_FREQ, sys->timescale, 1.0);
741 CHECK("Could not start playback");
742
743 p_config->Release();
744 p_display_mode->Release();
745 p_card->Release();
746 p_attributes->Release();
747 decklink_iterator->Release();
748
749 vlc_mutex_unlock(&sys->lock);
750
751 vout_display_DeleteWindow(vd, NULL);
752
753 return VLC_SUCCESS;
754
755 error:
756 if (sys->p_output) {
757 sys->p_output->Release();
758 sys->p_output = NULL;
759 }
760 if (p_card)
761 p_card->Release();
762 if (p_config)
763 p_config->Release();
764 if (p_attributes)
765 p_attributes->Release();
766 if (decklink_iterator)
767 decklink_iterator->Release();
768 if (p_display_mode)
769 p_display_mode->Release();
770
771 vlc_mutex_unlock(&sys->lock);
772
773 return VLC_EGENERIC;
774 #undef CHECK
775 }
776
777 /*****************************************************************************
778 * Video
779 *****************************************************************************/
780
PoolVideo(vout_display_t * vd,unsigned requested_count)781 static picture_pool_t *PoolVideo(vout_display_t *vd, unsigned requested_count)
782 {
783 struct decklink_sys_t *sys = (struct decklink_sys_t *) vd->sys;
784 if (!sys->video.pool)
785 sys->video.pool = picture_pool_NewFromFormat(&vd->fmt, requested_count);
786 return sys->video.pool;
787 }
788
put_le32(uint8_t ** p,uint32_t d)789 static inline void put_le32(uint8_t **p, uint32_t d)
790 {
791 SetDWLE(*p, d);
792 (*p) += 4;
793 }
794
clip(int a)795 static inline int clip(int a)
796 {
797 if (a < 4) return 4;
798 else if (a > 1019) return 1019;
799 else return a;
800 }
801
v210_convert(void * frame_bytes,picture_t * pic,int dst_stride)802 static void v210_convert(void *frame_bytes, picture_t *pic, int dst_stride)
803 {
804 int width = pic->format.i_width;
805 int height = pic->format.i_height;
806 int line_padding = dst_stride - ((width * 8 + 11) / 12) * 4;
807 int h, w;
808 uint8_t *data = (uint8_t*)frame_bytes;
809
810 const uint16_t *y = (const uint16_t*)pic->p[0].p_pixels;
811 const uint16_t *u = (const uint16_t*)pic->p[1].p_pixels;
812 const uint16_t *v = (const uint16_t*)pic->p[2].p_pixels;
813
814 #define WRITE_PIXELS(a, b, c) \
815 do { \
816 val = clip(*a++); \
817 val |= (clip(*b++) << 10) | \
818 (clip(*c++) << 20); \
819 put_le32(&data, val); \
820 } while (0)
821
822 for (h = 0; h < height; h++) {
823 uint32_t val = 0;
824 for (w = 0; w < width - 5; w += 6) {
825 WRITE_PIXELS(u, y, v);
826 WRITE_PIXELS(y, u, y);
827 WRITE_PIXELS(v, y, u);
828 WRITE_PIXELS(y, v, y);
829 }
830 if (w < width - 1) {
831 WRITE_PIXELS(u, y, v);
832
833 val = clip(*y++);
834 if (w == width - 2)
835 put_le32(&data, val);
836 #undef WRITE_PIXELS
837 }
838 if (w < width - 3) {
839 val |= (clip(*u++) << 10) | (clip(*y++) << 20);
840 put_le32(&data, val);
841
842 val = clip(*v++) | (clip(*y++) << 10);
843 put_le32(&data, val);
844 }
845
846 memset(data, 0, line_padding);
847 data += line_padding;
848
849 y += pic->p[0].i_pitch / 2 - width;
850 u += pic->p[1].i_pitch / 2 - width / 2;
851 v += pic->p[2].i_pitch / 2 - width / 2;
852 }
853 }
854
send_AFD(uint8_t afdcode,uint8_t ar,uint8_t * buf)855 static void send_AFD(uint8_t afdcode, uint8_t ar, uint8_t *buf)
856 {
857 const size_t len = 6 /* vanc header */ + 8 /* AFD data */ + 1 /* csum */;
858 const size_t s = ((len + 5) / 6) * 6; // align for v210
859
860 uint16_t afd[s];
861
862 afd[0] = 0x000;
863 afd[1] = 0x3ff;
864 afd[2] = 0x3ff;
865 afd[3] = 0x41; // DID
866 afd[4] = 0x05; // SDID
867 afd[5] = 8; // Data Count
868
869 int bar_data_flags = 0;
870 int bar_data_val1 = 0;
871 int bar_data_val2 = 0;
872
873 afd[ 6] = ((afdcode & 0x0F) << 3) | ((ar & 0x01) << 2); /* SMPTE 2016-1 */
874 afd[ 7] = 0; // reserved
875 afd[ 8] = 0; // reserved
876 afd[ 9] = bar_data_flags << 4;
877 afd[10] = bar_data_val1 << 8;
878 afd[11] = bar_data_val1 & 0xff;
879 afd[12] = bar_data_val2 << 8;
880 afd[13] = bar_data_val2 & 0xff;
881
882 /* parity bit */
883 for (size_t i = 3; i < len - 1; i++)
884 afd[i] |= parity(afd[i]) ? 0x100 : 0x200;
885
886 /* vanc checksum */
887 uint16_t vanc_sum = 0;
888 for (size_t i = 3; i < len - 1; i++) {
889 vanc_sum += afd[i];
890 vanc_sum &= 0x1ff;
891 }
892
893 afd[len - 1] = vanc_sum | ((~vanc_sum & 0x100) << 1);
894
895 /* pad */
896 for (size_t i = len; i < s; i++)
897 afd[i] = 0x040;
898
899 /* convert to v210 and write into VANC */
900 for (size_t w = 0; w < s / 6 ; w++) {
901 put_le32(&buf, afd[w*6+0] << 10);
902 put_le32(&buf, afd[w*6+1] | (afd[w*6+2] << 20));
903 put_le32(&buf, afd[w*6+3] << 10);
904 put_le32(&buf, afd[w*6+4] | (afd[w*6+5] << 20));
905 }
906 }
907
PrepareVideo(vout_display_t * vd,picture_t * picture,subpicture_t *)908 static void PrepareVideo(vout_display_t *vd, picture_t *picture, subpicture_t *)
909 {
910 decklink_sys_t *sys = (decklink_sys_t *) vd->sys;
911 mtime_t now = mdate();
912
913 if (!picture)
914 return;
915
916 if (now - picture->date > sys->video.nosignal_delay * CLOCK_FREQ) {
917 msg_Dbg(vd, "no signal");
918 if (sys->video.pic_nosignal) {
919 picture = sys->video.pic_nosignal;
920 } else {
921 if (sys->video.tenbits) { // I422_10L
922 plane_t *y = &picture->p[0];
923 memset(y->p_pixels, 0x0, y->i_lines * y->i_pitch);
924 for (int i = 1; i < picture->i_planes; i++) {
925 plane_t *p = &picture->p[i];
926 size_t len = p->i_lines * p->i_pitch / 2;
927 int16_t *data = (int16_t*)p->p_pixels;
928 for (size_t j = 0; j < len; j++) // XXX: SIMD
929 data[j] = 0x200;
930 }
931 } else { // UYVY
932 size_t len = picture->p[0].i_lines * picture->p[0].i_pitch;
933 for (size_t i = 0; i < len; i+= 2) { // XXX: SIMD
934 picture->p[0].p_pixels[i+0] = 0x80;
935 picture->p[0].p_pixels[i+1] = 0;
936 }
937 }
938 }
939 picture->date = now;
940 }
941
942 HRESULT result;
943 int w, h, stride, length;
944 w = vd->fmt.i_width;
945 h = vd->fmt.i_height;
946
947 IDeckLinkMutableVideoFrame *pDLVideoFrame;
948 result = sys->p_output->CreateVideoFrame(w, h, w*3,
949 sys->video.tenbits ? bmdFormat10BitYUV : bmdFormat8BitYUV,
950 bmdFrameFlagDefault, &pDLVideoFrame);
951
952 if (result != S_OK) {
953 msg_Err(vd, "Failed to create video frame: 0x%X", result);
954 pDLVideoFrame = NULL;
955 goto end;
956 }
957
958 void *frame_bytes;
959 pDLVideoFrame->GetBytes((void**)&frame_bytes);
960 stride = pDLVideoFrame->GetRowBytes();
961
962 if (sys->video.tenbits) {
963 IDeckLinkVideoFrameAncillary *vanc;
964 int line;
965 void *buf;
966
967 result = sys->p_output->CreateAncillaryData(
968 sys->video.tenbits ? bmdFormat10BitYUV : bmdFormat8BitYUV, &vanc);
969 if (result != S_OK) {
970 msg_Err(vd, "Failed to create vanc: %d", result);
971 goto end;
972 }
973
974 line = var_InheritInteger(vd, VIDEO_CFG_PREFIX "afd-line");
975 result = vanc->GetBufferForVerticalBlankingLine(line, &buf);
976 if (result != S_OK) {
977 msg_Err(vd, "Failed to get VBI line %d: %d", line, result);
978 goto end;
979 }
980 send_AFD(sys->video.afd, sys->video.ar, (uint8_t*)buf);
981
982 v210_convert(frame_bytes, picture, stride);
983
984 result = pDLVideoFrame->SetAncillaryData(vanc);
985 vanc->Release();
986 if (result != S_OK) {
987 msg_Err(vd, "Failed to set vanc: %d", result);
988 goto end;
989 }
990 }
991 else for(int y = 0; y < h; ++y) {
992 uint8_t *dst = (uint8_t *)frame_bytes + stride * y;
993 const uint8_t *src = (const uint8_t *)picture->p[0].p_pixels +
994 picture->p[0].i_pitch * y;
995 memcpy(dst, src, w * 2 /* bpp */);
996 }
997
998
999 // compute frame duration in CLOCK_FREQ units
1000 length = (sys->frameduration * CLOCK_FREQ) / sys->timescale;
1001
1002 picture->date -= sys->offset;
1003 result = sys->p_output->ScheduleVideoFrame(pDLVideoFrame,
1004 picture->date, length, CLOCK_FREQ);
1005
1006 if (result != S_OK) {
1007 msg_Err(vd, "Dropped Video frame %" PRId64 ": 0x%x",
1008 picture->date, result);
1009 goto end;
1010 }
1011
1012 now = mdate() - sys->offset;
1013
1014 BMDTimeValue decklink_now;
1015 double speed;
1016 sys->p_output->GetScheduledStreamTime (CLOCK_FREQ, &decklink_now, &speed);
1017
1018 if ((now - decklink_now) > 400000) {
1019 /* XXX: workaround card clock drift */
1020 sys->offset += 50000;
1021 msg_Err(vd, "Delaying: offset now %" PRId64, sys->offset);
1022 }
1023
1024 end:
1025 if (pDLVideoFrame)
1026 pDLVideoFrame->Release();
1027 }
1028
DisplayVideo(vout_display_t *,picture_t * picture,subpicture_t *)1029 static void DisplayVideo(vout_display_t *, picture_t *picture, subpicture_t *)
1030 {
1031 picture_Release(picture);
1032 }
1033
ControlVideo(vout_display_t * vd,int query,va_list args)1034 static int ControlVideo(vout_display_t *vd, int query, va_list args)
1035 {
1036 (void) vd; (void) query; (void) args;
1037 return VLC_EGENERIC;
1038 }
1039
OpenVideo(vlc_object_t * p_this)1040 static int OpenVideo(vlc_object_t *p_this)
1041 {
1042 vout_display_t *vd = (vout_display_t *)p_this;
1043 decklink_sys_t *sys = HoldDLSys(p_this, VIDEO_ES);
1044 if(!sys)
1045 return VLC_ENOMEM;
1046
1047 vd->sys = (vout_display_sys_t*) sys;
1048
1049 bool b_init;
1050 vlc_mutex_lock(&sys->lock);
1051 b_init = !sys->b_recycling;
1052 vlc_mutex_unlock(&sys->lock);
1053
1054 if( b_init )
1055 {
1056 sys->video.tenbits = var_InheritBool(p_this, VIDEO_CFG_PREFIX "tenbits");
1057 sys->video.nosignal_delay = var_InheritInteger(p_this, VIDEO_CFG_PREFIX "nosignal-delay");
1058 sys->video.afd = var_InheritInteger(p_this, VIDEO_CFG_PREFIX "afd");
1059 sys->video.ar = var_InheritInteger(p_this, VIDEO_CFG_PREFIX "ar");
1060 sys->video.pic_nosignal = NULL;
1061 sys->video.pool = NULL;
1062 video_format_Init( &sys->video.currentfmt, 0 );
1063
1064 if (OpenDecklink(vd, sys) != VLC_SUCCESS)
1065 {
1066 CloseVideo(p_this);
1067 return VLC_EGENERIC;
1068 }
1069
1070 char *pic_file = var_InheritString(p_this, VIDEO_CFG_PREFIX "nosignal-image");
1071 if (pic_file)
1072 {
1073 sys->video.pic_nosignal = CreateNoSignalPicture(p_this, &vd->fmt, pic_file);
1074 if (!sys->video.pic_nosignal)
1075 msg_Err(p_this, "Could not create no signal picture");
1076 free(pic_file);
1077 }
1078 }
1079
1080 /* vout must adapt */
1081 video_format_Clean( &vd->fmt );
1082 video_format_Copy( &vd->fmt, &sys->video.currentfmt );
1083
1084 vd->pool = PoolVideo;
1085 vd->prepare = PrepareVideo;
1086 vd->display = DisplayVideo;
1087 vd->control = ControlVideo;
1088
1089 return VLC_SUCCESS;
1090 }
1091
CloseVideo(vlc_object_t * p_this)1092 static void CloseVideo(vlc_object_t *p_this)
1093 {
1094 ReleaseDLSys(p_this, VIDEO_ES);
1095 }
1096
1097 /*****************************************************************************
1098 * Audio
1099 *****************************************************************************/
1100
Flush(audio_output_t * aout,bool drain)1101 static void Flush (audio_output_t *aout, bool drain)
1102 {
1103 decklink_sys_t *sys = (decklink_sys_t *) aout->sys;
1104 vlc_mutex_lock(&sys->lock);
1105 IDeckLinkOutput *p_output = sys->p_output;
1106 vlc_mutex_unlock(&sys->lock);
1107 if (!p_output)
1108 return;
1109
1110 if (drain) {
1111 uint32_t samples;
1112 sys->p_output->GetBufferedAudioSampleFrameCount(&samples);
1113 msleep(CLOCK_FREQ * samples / sys->i_rate);
1114 } else if (sys->p_output->FlushBufferedAudioSamples() == E_FAIL)
1115 msg_Err(aout, "Flush failed");
1116 }
1117
TimeGet(audio_output_t *,mtime_t * restrict)1118 static int TimeGet(audio_output_t *, mtime_t* restrict)
1119 {
1120 /* synchronization is handled by the card */
1121 return -1;
1122 }
1123
Start(audio_output_t * aout,audio_sample_format_t * restrict fmt)1124 static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
1125 {
1126 decklink_sys_t *sys = (decklink_sys_t *) aout->sys;
1127
1128 if (sys->i_rate == 0)
1129 return VLC_EGENERIC;
1130
1131 fmt->i_format = VLC_CODEC_S16N;
1132 fmt->i_channels = 2; //decklink_sys->i_channels;
1133 fmt->i_physical_channels = AOUT_CHANS_STEREO; //pi_channels_maps[fmt->i_channels];
1134 fmt->channel_type = AUDIO_CHANNEL_TYPE_BITMAP;
1135 fmt->i_rate = sys->i_rate;
1136 fmt->i_bitspersample = 16;
1137 fmt->i_blockalign = fmt->i_channels * fmt->i_bitspersample /8 ;
1138 fmt->i_frame_length = FRAME_SIZE;
1139
1140 return VLC_SUCCESS;
1141 }
1142
PlayAudio(audio_output_t * aout,block_t * audio)1143 static void PlayAudio(audio_output_t *aout, block_t *audio)
1144 {
1145 decklink_sys_t *sys = (decklink_sys_t *) aout->sys;
1146 vlc_mutex_lock(&sys->lock);
1147 IDeckLinkOutput *p_output = sys->p_output;
1148 audio->i_pts -= sys->offset;
1149 vlc_mutex_unlock(&sys->lock);
1150 if (!p_output) {
1151 block_Release(audio);
1152 return;
1153 }
1154
1155 uint32_t sampleFrameCount = audio->i_buffer / (2 * 2 /*decklink_sys->i_channels*/);
1156 uint32_t written;
1157 HRESULT result = p_output->ScheduleAudioSamples(
1158 audio->p_buffer, sampleFrameCount, audio->i_pts, CLOCK_FREQ, &written);
1159
1160 if (result != S_OK)
1161 msg_Err(aout, "Failed to schedule audio sample: 0x%X", result);
1162 else if (sampleFrameCount != written)
1163 msg_Err(aout, "Written only %d samples out of %d", written, sampleFrameCount);
1164
1165 block_Release(audio);
1166 }
1167
OpenAudio(vlc_object_t * p_this)1168 static int OpenAudio(vlc_object_t *p_this)
1169 {
1170 audio_output_t *aout = (audio_output_t *)p_this;
1171 decklink_sys_t *sys = HoldDLSys(p_this, AUDIO_ES);
1172 if(!sys)
1173 return VLC_ENOMEM;
1174
1175 aout->sys = (aout_sys_t *) sys;
1176
1177 vlc_mutex_lock(&sys->lock);
1178 //decklink_sys->i_channels = var_InheritInteger(vd, AUDIO_CFG_PREFIX "audio-channels");
1179 sys->i_rate = var_InheritInteger(aout, AUDIO_CFG_PREFIX "audio-rate");
1180 vlc_cond_signal(&sys->cond);
1181 vlc_mutex_unlock(&sys->lock);
1182
1183 aout->play = PlayAudio;
1184 aout->start = Start;
1185 aout->flush = Flush;
1186 aout->time_get = TimeGet;
1187
1188 aout->pause = NULL;
1189 aout->stop = NULL;
1190 aout->mute_set = NULL;
1191 aout->volume_set= NULL;
1192
1193 return VLC_SUCCESS;
1194 }
1195
CloseAudio(vlc_object_t * p_this)1196 static void CloseAudio(vlc_object_t *p_this)
1197 {
1198 decklink_sys_t *sys = (decklink_sys_t *) ((audio_output_t *)p_this)->sys;
1199 vlc_mutex_lock(&sys->lock);
1200 vlc_mutex_unlock(&sys->lock);
1201 ReleaseDLSys(p_this, AUDIO_ES);
1202 }
1203