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, &timescale) == 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