]> git.sesse.net Git - vlc/blob - modules/video_output/decklink.cpp
flac decoder: supports avformat extradata
[vlc] / modules / video_output / decklink.cpp
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 #define __STDC_FORMAT_MACROS
30 #define __STDC_CONSTANT_MACROS
31
32 #ifdef HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35
36 #include <stdint.h>
37
38 #include <vlc_common.h>
39 #include <vlc_plugin.h>
40 #include <vlc_threads.h>
41
42 #include <vlc_vout_display.h>
43 #include <vlc_picture_pool.h>
44
45 #include <vlc_block.h>
46 #include <vlc_atomic.h>
47 #include <vlc_aout.h>
48 #include <arpa/inet.h>
49
50 #include <DeckLinkAPI.h>
51 #include <DeckLinkAPIDispatch.cpp>
52
53 #define FRAME_SIZE 1920
54 #define CHANNELS_MAX 6
55
56 #if 0
57 static const int pi_channels_maps[CHANNELS_MAX+1] =
58 {
59     0,
60     AOUT_CHAN_CENTER,
61     AOUT_CHANS_STEREO,
62     AOUT_CHANS_3_0,
63     AOUT_CHANS_4_0,
64     AOUT_CHANS_5_0,
65     AOUT_CHANS_5_1,
66 };
67 #endif
68
69 #define CARD_INDEX_TEXT N_("Output card")
70 #define CARD_INDEX_LONGTEXT N_(\
71     "DeckLink output card, if multiple exist. " \
72     "The cards are numbered from 0.")
73
74 #define MODE_TEXT N_("Desired output mode")
75 #define MODE_LONGTEXT N_(\
76     "Desired output mode for DeckLink output. " \
77     "This value should be a FOURCC code in textual " \
78     "form, e.g. \"ntsc\".")
79
80 #define AUDIO_CONNECTION_TEXT N_("Audio connection")
81 #define AUDIO_CONNECTION_LONGTEXT N_(\
82     "Audio connection for DeckLink output.")
83
84
85 #define RATE_TEXT N_("Audio samplerate (Hz)")
86 #define RATE_LONGTEXT N_(\
87     "Audio sampling rate (in hertz) for DeckLink output. " \
88     "0 disables audio output.")
89
90 #define CHANNELS_TEXT N_("Number of audio channels")
91 #define CHANNELS_LONGTEXT N_(\
92     "Number of output channels for DeckLink output. " \
93     "Must be 2, 8 or 16. 0 disables audio output.")
94
95 #define VIDEO_CONNECTION_TEXT N_("Video connection")
96 #define VIDEO_CONNECTION_LONGTEXT N_(\
97     "Video connection for DeckLink output.")
98
99 #define VIDEO_TENBITS_TEXT N_("10 bits")
100 #define VIDEO_TENBITS_LONGTEXT N_(\
101     "Use 10 bits per pixel for video frames.")
102
103 #define CFG_PREFIX "decklink-output-"
104 #define VIDEO_CFG_PREFIX "decklink-vout-"
105 #define AUDIO_CFG_PREFIX "decklink-aout-"
106
107
108
109 static const char *const ppsz_videoconns[] = {
110     "sdi", "hdmi", "opticalsdi", "component", "composite", "svideo"
111 };
112 static const char *const ppsz_videoconns_text[] = {
113     N_("SDI"), N_("HDMI"), N_("Optical SDI"), N_("Component"), N_("Composite"), N_("S-video")
114 };
115
116 static const char *const ppsz_audioconns[] = {
117     "embedded", "aesebu", "analog"
118 };
119 static const char *const ppsz_audioconns_text[] = {
120     N_("Embedded"), N_("AES/EBU"), N_("Analog")
121 };
122
123
124 struct vout_display_sys_t
125 {
126     picture_pool_t *pool;
127     bool tenbits;
128 };
129
130 /* Only one audio output module and one video output module
131  * can be used per process.
132  * We use a static mutex in audio/video submodules entry points.  */
133 struct decklink_sys_t
134 {
135     IDeckLinkOutput *p_output;
136
137     /*
138      * Synchronizes aout and vout modules:
139      * vout module waits until aout has been initialized.
140      * That means video-only output is NOT supported.
141      */
142     vlc_mutex_t lock;
143     vlc_cond_t cond;
144     uint8_t users;
145     BMDAudioConnection aconn;
146
147     //int i_channels;
148     int i_rate;
149
150     int i_width;
151     int i_height;
152
153     BMDTimeScale timescale;
154     BMDTimeValue frameduration;
155
156     /* XXX: workaround card clock drift */
157     mtime_t offset;
158 };
159
160 /*****************************************************************************
161  * Local prototypes.
162  *****************************************************************************/
163
164 static int  OpenVideo           (vlc_object_t *);
165 static void CloseVideo          (vlc_object_t *);
166 static int  OpenAudio           (vlc_object_t *);
167 static void CloseAudio          (vlc_object_t *);
168
169 /*****************************************************************************
170  * Module descriptor
171  *****************************************************************************/
172
173 vlc_module_begin()
174     set_shortname(N_("DecklinkOutput"))
175     set_description(N_("output module to write to Blackmagic SDI card"))
176     set_section(N_("Decklink General Options"), NULL)
177     add_integer(CFG_PREFIX "card-index", 0,
178                 CARD_INDEX_TEXT, CARD_INDEX_LONGTEXT, true)
179
180     add_submodule ()
181     set_description (N_("Decklink Video Output module"))
182     set_category(CAT_VIDEO)
183     set_subcategory(SUBCAT_VIDEO_VOUT)
184     set_capability("vout display", 0)
185     set_callbacks (OpenVideo, CloseVideo)
186     set_section(N_("Decklink Video Options"), NULL)
187     add_string(VIDEO_CFG_PREFIX "video-connection", "sdi",
188                 VIDEO_CONNECTION_TEXT, VIDEO_CONNECTION_LONGTEXT, true)
189                 change_string_list(ppsz_videoconns, ppsz_videoconns_text)
190     add_string(VIDEO_CFG_PREFIX "mode", "pal ",
191                 MODE_TEXT, MODE_LONGTEXT, true)
192     add_bool(VIDEO_CFG_PREFIX "tenbits", false,
193                 VIDEO_TENBITS_TEXT, VIDEO_TENBITS_LONGTEXT, true)
194
195
196     add_submodule ()
197     set_description (N_("Decklink Audio Output module"))
198     set_category(CAT_AUDIO)
199     set_subcategory(SUBCAT_AUDIO_AOUT)
200     set_capability("audio output", 0)
201     set_callbacks (OpenAudio, CloseAudio)
202     set_section(N_("Decklink Audio Options"), NULL)
203     add_string(AUDIO_CFG_PREFIX "audio-connection", "embedded",
204                 AUDIO_CONNECTION_TEXT, AUDIO_CONNECTION_LONGTEXT, true)
205                 change_string_list(ppsz_audioconns, ppsz_audioconns_text)
206     add_integer(AUDIO_CFG_PREFIX "audio-rate", 48000,
207                 RATE_TEXT, RATE_LONGTEXT, true)
208     add_integer(AUDIO_CFG_PREFIX "audio-channels", 2,
209                 CHANNELS_TEXT, CHANNELS_LONGTEXT, true)
210 vlc_module_end ()
211
212 /* Protects decklink_sys_t creation/deletion */
213 static vlc_mutex_t sys_lock = VLC_STATIC_MUTEX;
214
215 static struct decklink_sys_t *GetDLSys(vlc_object_t *obj)
216 {
217     vlc_object_t *libvlc = VLC_OBJECT(obj->p_libvlc);
218     struct decklink_sys_t *sys;
219
220     vlc_mutex_lock(&sys_lock);
221
222     if (var_Type(libvlc, "decklink-sys") == VLC_VAR_ADDRESS)
223         sys = (struct decklink_sys_t*)var_GetAddress(libvlc, "decklink-sys");
224     else {
225         sys = (struct decklink_sys_t*)malloc(sizeof(*sys));
226         if (sys) {
227             sys->p_output = NULL;
228             sys->offset = 0;
229             sys->users = 0;
230             sys->aconn = 0;
231             vlc_mutex_init(&sys->lock);
232             vlc_cond_init(&sys->cond);
233             var_Create(libvlc, "decklink-sys", VLC_VAR_ADDRESS);
234             var_SetAddress(libvlc, "decklink-sys", (void*)sys);
235         }
236     }
237
238     vlc_mutex_unlock(&sys_lock);
239     return sys;
240 }
241
242 static void ReleaseDLSys(vlc_object_t *obj)
243 {
244     vlc_object_t *libvlc = VLC_OBJECT(obj->p_libvlc);
245
246     vlc_mutex_lock(&sys_lock);
247
248     struct decklink_sys_t *sys = (struct decklink_sys_t*)var_GetAddress(libvlc, "decklink-sys");
249
250     if (--sys->users == 0) {
251         msg_Dbg(obj, "Destroying decklink data");
252         vlc_mutex_destroy(&sys->lock);
253         vlc_cond_destroy(&sys->cond);
254
255         if (sys->p_output) {
256             sys->p_output->StopScheduledPlayback(0, NULL, 0);
257             sys->p_output->DisableVideoOutput();
258             sys->p_output->DisableAudioOutput();
259             sys->p_output->Release();
260         }
261
262         free(sys);
263         var_Destroy(libvlc, "decklink-sys");
264     }
265
266     vlc_mutex_unlock(&sys_lock);
267 }
268
269 // Connection mode
270 static BMDAudioConnection getAConn(audio_output_t *aout)
271 {
272     BMDAudioConnection conn = bmdAudioConnectionEmbedded;
273     char *psz = var_InheritString(aout, AUDIO_CFG_PREFIX "audio-connection");
274     if (!psz)
275         return conn;
276
277     if (!strcmp(psz, "embedded"))
278         conn = bmdAudioConnectionEmbedded;
279     else if (!strcmp(psz, "aesebu"))
280         conn = bmdAudioConnectionAESEBU;
281     else if (!strcmp(psz, "analog"))
282         conn = bmdAudioConnectionAnalog;
283
284     free(psz);
285     return conn;
286 }
287
288 static BMDVideoConnection getVConn(vout_display_t *vd)
289 {
290     BMDVideoConnection conn = bmdVideoConnectionSDI;
291     char *psz = var_InheritString(vd, VIDEO_CFG_PREFIX "video-connection");
292     if (!psz)
293         goto end;
294
295          if (!strcmp(psz, "sdi"))
296         conn = bmdVideoConnectionSDI;
297     else if (!strcmp(psz, "hdmi"))
298         conn = bmdVideoConnectionHDMI;
299     else if (!strcmp(psz, "opticalsdi"))
300         conn = bmdVideoConnectionOpticalSDI;
301     else if (!strcmp(psz, "component"))
302         conn = bmdVideoConnectionComponent;
303     else if (!strcmp(psz, "composite"))
304         conn = bmdVideoConnectionComposite;
305     else if (!strcmp(psz, "svideo"))
306         conn = bmdVideoConnectionSVideo;
307
308 end:
309     free(psz);
310     return conn;
311 }
312
313 /*****************************************************************************
314  *
315  *****************************************************************************/
316
317 static struct decklink_sys_t *OpenDecklink(vout_display_t *vd)
318 {
319     vout_display_sys_t *sys = vd->sys;
320 #define CHECK(message) do { \
321     if (result != S_OK) \
322     { \
323         msg_Err(vd, message ": 0x%X", result); \
324         goto error; \
325     } \
326 } while(0)
327
328     HRESULT result;
329     IDeckLinkIterator *decklink_iterator = NULL;
330     IDeckLinkDisplayMode *p_display_mode = NULL;
331     IDeckLinkDisplayModeIterator *p_display_iterator = NULL;
332     IDeckLinkConfiguration *p_config = NULL;
333     IDeckLink *p_card = NULL;
334
335     struct decklink_sys_t *decklink_sys = GetDLSys(VLC_OBJECT(vd));
336     vlc_mutex_lock(&decklink_sys->lock);
337     decklink_sys->users++;
338
339     /* wait until aout is ready */
340     while (decklink_sys->aconn == 0)
341         vlc_cond_wait(&decklink_sys->cond, &decklink_sys->lock);
342
343     int i_card_index = var_InheritInteger(vd, CFG_PREFIX "card-index");
344     BMDVideoConnection vconn = getVConn(vd);
345     char *mode = var_InheritString(vd, VIDEO_CFG_PREFIX "mode");
346     size_t len = mode ? strlen(mode) : 0;
347     if (!mode || len > 4)
348     {
349         free(mode);
350         msg_Err(vd, "Missing or invalid mode");
351         goto error;
352     }
353
354     BMDDisplayMode wanted_mode_id;
355     memset(&wanted_mode_id, ' ', 4);
356     strncpy((char*)&wanted_mode_id, mode, 4);
357     free(mode);
358
359     if (i_card_index < 0)
360     {
361         msg_Err(vd, "Invalid card index %d", i_card_index);
362         goto error;
363     }
364
365     decklink_iterator = CreateDeckLinkIteratorInstance();
366     if (!decklink_iterator)
367     {
368         msg_Err(vd, "DeckLink drivers not found.");
369         goto error;
370     }
371
372     for(int i = 0; i <= i_card_index; ++i)
373     {
374         if (p_card)
375             p_card->Release();
376         result = decklink_iterator->Next(&p_card);
377         CHECK("Card not found");
378     }
379
380     const char *psz_model_name;
381     result = p_card->GetModelName(&psz_model_name);
382     CHECK("Unknown model name");
383
384     msg_Dbg(vd, "Opened DeckLink PCI card %s", psz_model_name);
385
386     result = p_card->QueryInterface(IID_IDeckLinkOutput,
387         (void**)&decklink_sys->p_output);
388     CHECK("No outputs");
389
390     result = p_card->QueryInterface(IID_IDeckLinkConfiguration,
391         (void**)&p_config);
392     CHECK("Could not get config interface");
393
394     if (vconn)
395     {
396         result = p_config->SetInt(
397             bmdDeckLinkConfigVideoOutputConnection, vconn);
398         CHECK("Could not set video output connection");
399     }
400
401     result = decklink_sys->p_output->GetDisplayModeIterator(&p_display_iterator);
402     CHECK("Could not enumerate display modes");
403
404     for (; ; p_display_mode->Release())
405     {
406         int w, h;
407         result = p_display_iterator->Next(&p_display_mode);
408         if (result != S_OK)
409             break;
410
411         BMDDisplayMode mode_id = ntohl(p_display_mode->GetDisplayMode());
412
413         const char *psz_mode_name;
414         result = p_display_mode->GetName(&psz_mode_name);
415         CHECK("Could not get display mode name");
416
417         result = p_display_mode->GetFrameRate(&decklink_sys->frameduration,
418             &decklink_sys->timescale);
419         CHECK("Could not get frame rate");
420
421         w = p_display_mode->GetWidth();
422         h = p_display_mode->GetHeight();
423         msg_Dbg(vd, "Found mode '%4.4s': %s (%dx%d, %.3f fps)",
424                 (char*)&mode_id, psz_mode_name, w, h,
425                 double(decklink_sys->timescale) / decklink_sys->frameduration);
426         msg_Dbg(vd, "scale %d dur %d", (int)decklink_sys->timescale,
427             (int)decklink_sys->frameduration);
428
429         if (wanted_mode_id != mode_id)
430             continue;
431
432         decklink_sys->i_width = w;
433         decklink_sys->i_height = h;
434
435         mode_id = htonl(mode_id);
436
437         BMDVideoOutputFlags flags = bmdVideoOutputVANC;
438         if (mode_id == bmdModeNTSC ||
439             mode_id == bmdModeNTSC2398 ||
440             mode_id == bmdModePAL)
441         {
442             flags = bmdVideoOutputVITC;
443         }
444
445         BMDDisplayModeSupport support;
446         IDeckLinkDisplayMode *resultMode;
447
448         result = decklink_sys->p_output->DoesSupportVideoMode(mode_id,
449             sys->tenbits ? bmdFormat10BitYUV : bmdFormat8BitYUV,
450             flags, &support, &resultMode);
451         CHECK("Does not support video mode");
452         if (support == bmdDisplayModeNotSupported)
453         {
454             msg_Err(vd, "Video mode not supported");
455                 goto error;
456         }
457
458         result = decklink_sys->p_output->EnableVideoOutput(mode_id, flags);
459         CHECK("Could not enable video output");
460
461         break;
462     }
463
464     if (decklink_sys->i_width < 0 || decklink_sys->i_width & 1)
465     {
466         msg_Err(vd, "Unknown video mode specified.");
467         goto error;
468     }
469
470     result = p_config->SetInt(
471         bmdDeckLinkConfigAudioInputConnection, decklink_sys->aconn);
472     CHECK("Could not set audio connection");
473
474     if (/*decklink_sys->i_channels > 0 &&*/ decklink_sys->i_rate > 0)
475     {
476         result = decklink_sys->p_output->EnableAudioOutput(
477             decklink_sys->i_rate,
478             bmdAudioSampleType16bitInteger,
479             /*decklink_sys->i_channels*/ 2,
480             bmdAudioOutputStreamTimestamped);
481     }
482     CHECK("Could not start audio output");
483
484     /* start */
485     result = decklink_sys->p_output->StartScheduledPlayback(
486         (mdate() * decklink_sys->timescale) / CLOCK_FREQ, decklink_sys->timescale, 1.0);
487     CHECK("Could not start playback");
488
489     p_config->Release();
490     p_display_mode->Release();
491     p_display_iterator->Release();
492     p_card->Release();
493     decklink_iterator->Release();
494
495     vlc_mutex_unlock(&decklink_sys->lock);
496
497     return decklink_sys;
498
499 error:
500     if (decklink_sys->p_output) {
501         decklink_sys->p_output->Release();
502         decklink_sys->p_output = NULL;
503     }
504     if (p_card)
505         p_card->Release();
506     if (p_config)
507         p_config->Release();
508     if (p_display_iterator)
509         p_display_iterator->Release();
510     if (decklink_iterator)
511         decklink_iterator->Release();
512     if (p_display_mode)
513         p_display_mode->Release();
514
515     vlc_mutex_unlock(&decklink_sys->lock);
516     ReleaseDLSys(VLC_OBJECT(vd));
517
518     return NULL;
519 #undef CHECK
520 }
521
522 /*****************************************************************************
523  * Video
524  *****************************************************************************/
525
526 static picture_pool_t *PoolVideo(vout_display_t *vd, unsigned requested_count)
527 {
528     vout_display_sys_t *sys = vd->sys;
529     if (!sys->pool)
530         sys->pool = picture_pool_NewFromFormat(&vd->fmt, requested_count);
531     return sys->pool;
532 }
533
534 static inline void put_le32(uint8_t **p, uint32_t d)
535 {
536     SetDWLE(*p, d);
537     (*p) += 4;
538 }
539
540 static inline int clip(int a)
541 {
542     if      (a < 4) return 4;
543     else if (a > 1019) return 1019;
544     else               return a;
545 }
546
547 static void v210_convert(void *frame_bytes, picture_t *pic, int dst_stride)
548 {
549     int width = pic->format.i_width;
550     int height = pic->format.i_height;
551     int line_padding = dst_stride - ((width * 8 + 11) / 12) * 4;
552     int h, w;
553     uint8_t *data = (uint8_t*)frame_bytes;
554
555     const uint16_t *y = (const uint16_t*)pic->p[0].p_pixels;
556     const uint16_t *u = (const uint16_t*)pic->p[1].p_pixels;
557     const uint16_t *v = (const uint16_t*)pic->p[2].p_pixels;
558
559 #define WRITE_PIXELS(a, b, c)           \
560     do {                                \
561         val =   clip(*a++);             \
562         val |= (clip(*b++) << 10) |     \
563                (clip(*c++) << 20);      \
564         put_le32(&data, val);           \
565     } while (0)
566
567     for (h = 0; h < height; h++) {
568         uint32_t val = 0;
569         for (w = 0; w < width - 5; w += 6) {
570             WRITE_PIXELS(u, y, v);
571             WRITE_PIXELS(y, u, y);
572             WRITE_PIXELS(v, y, u);
573             WRITE_PIXELS(y, v, y);
574         }
575         if (w < width - 1) {
576             WRITE_PIXELS(u, y, v);
577
578             val = clip(*y++);
579             if (w == width - 2)
580                 put_le32(&data, val);
581 #undef WRITE_PIXELS
582         }
583         if (w < width - 3) {
584             val |= (clip(*u++) << 10) | (clip(*y++) << 20);
585             put_le32(&data, val);
586
587             val = clip(*v++) | (clip(*y++) << 10);
588             put_le32(&data, val);
589         }
590
591         memset(data, 0, line_padding);
592         data += line_padding;
593
594         y += pic->p[0].i_pitch / 2 - width;
595         u += pic->p[1].i_pitch / 2 - width / 2;
596         v += pic->p[2].i_pitch / 2 - width / 2;
597     }
598 }
599
600 static void DisplayVideo(vout_display_t *vd, picture_t *picture, subpicture_t *)
601 {
602     vout_display_sys_t *sys = vd->sys;
603     struct decklink_sys_t *decklink_sys = GetDLSys(VLC_OBJECT(vd));
604
605     if (!picture)
606         return;
607
608     HRESULT result;
609     int w, h, stride, length;
610     mtime_t now;
611     w = decklink_sys->i_width;
612     h = decklink_sys->i_height;
613
614     IDeckLinkMutableVideoFrame *pDLVideoFrame;
615     result = decklink_sys->p_output->CreateVideoFrame(w, h, w*3,
616         sys->tenbits ? bmdFormat10BitYUV : bmdFormat8BitYUV,
617         bmdFrameFlagDefault, &pDLVideoFrame);
618
619     if (result != S_OK) {
620         msg_Err(vd, "Failed to create video frame: 0x%X", result);
621         pDLVideoFrame = NULL;
622         goto end;
623     }
624
625     void *frame_bytes;
626     pDLVideoFrame->GetBytes((void**)&frame_bytes);
627     stride = pDLVideoFrame->GetRowBytes();
628
629     if (sys->tenbits)
630         v210_convert(frame_bytes, picture, stride);
631     else for(int y = 0; y < h; ++y) {
632         uint8_t *dst = (uint8_t *)frame_bytes + stride * y;
633         const uint8_t *src = (const uint8_t *)picture->p[0].p_pixels +
634             picture->p[0].i_pitch * y;
635         memcpy(dst, src, w * 2 /* bpp */);
636     }
637
638
639     // compute frame duration in CLOCK_FREQ units
640     length = (decklink_sys->frameduration * CLOCK_FREQ) / decklink_sys->timescale;
641
642     picture->date -= decklink_sys->offset;
643     result = decklink_sys->p_output->ScheduleVideoFrame(pDLVideoFrame,
644         picture->date, length, CLOCK_FREQ);
645
646     if (result != S_OK) {
647         msg_Err(vd, "Dropped Video frame %"PRId64 ": 0x%x",
648             picture->date, result);
649         goto end;
650     }
651
652     now = mdate() - decklink_sys->offset;
653
654     BMDTimeValue decklink_now;
655     double speed;
656     decklink_sys->p_output->GetScheduledStreamTime (CLOCK_FREQ, &decklink_now, &speed);
657
658     if ((now - decklink_now) > 400000) {
659         /* XXX: workaround card clock drift */
660         decklink_sys->offset += 50000;
661         msg_Err(vd, "Delaying: offset now %"PRId64"", decklink_sys->offset);
662     }
663
664 end:
665     if (pDLVideoFrame)
666         pDLVideoFrame->Release();
667     picture_Release(picture);
668 }
669
670 static int ControlVideo(vout_display_t *vd, int query, va_list args)
671 {
672     VLC_UNUSED(vd);
673     const vout_display_cfg_t *cfg;
674
675     switch (query) {
676     case VOUT_DISPLAY_CHANGE_FULLSCREEN:
677         cfg = va_arg(args, const vout_display_cfg_t *);
678         return cfg->is_fullscreen ? VLC_EGENERIC : VLC_SUCCESS;
679     default:
680         return VLC_EGENERIC;
681     }
682 }
683
684 static int OpenVideo(vlc_object_t *p_this)
685 {
686     vout_display_t *vd = (vout_display_t *)p_this;
687     vout_display_sys_t *sys;
688     struct decklink_sys_t *decklink_sys;
689
690     vd->sys = sys = (vout_display_sys_t*)malloc(sizeof(*sys));
691     if (!sys)
692         return VLC_ENOMEM;
693
694     sys->tenbits = var_InheritBool(p_this, VIDEO_CFG_PREFIX "tenbits");
695
696     decklink_sys = OpenDecklink(vd);
697     if (!decklink_sys) {
698         free(sys);
699         return VLC_EGENERIC;
700     }
701
702     sys->pool = NULL;
703
704     vd->fmt.i_chroma = sys->tenbits
705         ? VLC_CODEC_I422_10L /* we will convert to v210 */
706         : VLC_CODEC_UYVY;
707     //video_format_FixRgb(&(vd->fmt));
708
709     vd->fmt.i_width = decklink_sys->i_width;
710     vd->fmt.i_height = decklink_sys->i_height;
711
712     vd->info.has_hide_mouse = true;
713     vd->pool    = PoolVideo;
714     vd->prepare = NULL;
715     vd->display = DisplayVideo;
716     vd->control = ControlVideo;
717     vd->manage  = NULL;
718     vout_display_SendEventFullscreen(vd, false);
719
720     return VLC_SUCCESS;
721 }
722
723 static void CloseVideo(vlc_object_t *p_this)
724 {
725     vout_display_t *vd = (vout_display_t *)p_this;
726     vout_display_sys_t *sys = vd->sys;
727
728     if (sys->pool)
729         picture_pool_Delete(sys->pool);
730
731     free(sys);
732
733     ReleaseDLSys(p_this);
734 }
735
736 /*****************************************************************************
737  * Audio
738  *****************************************************************************/
739
740 static void Flush (audio_output_t *aout, bool drain)
741 {
742     struct decklink_sys_t *decklink_sys = GetDLSys(VLC_OBJECT(aout));
743     vlc_mutex_lock(&decklink_sys->lock);
744     IDeckLinkOutput *p_output = decklink_sys->p_output;
745     vlc_mutex_unlock(&decklink_sys->lock);
746     if (!p_output)
747         return;
748
749     if (drain) {
750         uint32_t samples;
751         decklink_sys->p_output->GetBufferedAudioSampleFrameCount(&samples);
752         msleep(CLOCK_FREQ * samples / decklink_sys->i_rate);
753     } else if (decklink_sys->p_output->FlushBufferedAudioSamples() == E_FAIL)
754         msg_Err(aout, "Flush failed");
755 }
756
757 static int TimeGet(audio_output_t *, mtime_t* restrict)
758 {
759     /* synchronization is handled by the card */
760     return -1;
761 }
762
763 static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
764 {
765     struct decklink_sys_t *decklink_sys = GetDLSys(VLC_OBJECT(aout));
766
767     if (decklink_sys->i_rate == 0)
768         return VLC_EGENERIC;
769
770     fmt->i_format = VLC_CODEC_S16N;
771     fmt->i_channels = 2; //decklink_sys->i_channels;
772     fmt->i_physical_channels = AOUT_CHANS_STEREO; //pi_channels_maps[fmt->i_channels];
773     fmt->i_rate = decklink_sys->i_rate;
774     fmt->i_bitspersample = 16;
775     fmt->i_blockalign = fmt->i_channels * fmt->i_bitspersample /8 ;
776     fmt->i_frame_length  = FRAME_SIZE;
777
778     return VLC_SUCCESS;
779 }
780
781 static void PlayAudio(audio_output_t *aout, block_t *audio)
782 {
783     struct decklink_sys_t *decklink_sys = GetDLSys(VLC_OBJECT(aout));
784     vlc_mutex_lock(&decklink_sys->lock);
785     IDeckLinkOutput *p_output = decklink_sys->p_output;
786     vlc_mutex_unlock(&decklink_sys->lock);
787     if (!p_output) {
788         block_Release(audio);
789         return;
790     }
791
792     audio->i_pts -= decklink_sys->offset;
793
794     uint32_t sampleFrameCount = audio->i_buffer / (2 * 2 /*decklink_sys->i_channels*/);
795     uint32_t written;
796     HRESULT result = decklink_sys->p_output->ScheduleAudioSamples(
797             audio->p_buffer, sampleFrameCount, audio->i_pts, CLOCK_FREQ, &written);
798
799     if (result != S_OK)
800         msg_Err(aout, "Failed to schedule audio sample: 0x%X", result);
801     else if (sampleFrameCount != written)
802         msg_Err(aout, "Written only %d samples out of %d", written, sampleFrameCount);
803
804     block_Release(audio);
805 }
806
807 static int OpenAudio(vlc_object_t *p_this)
808 {
809     audio_output_t *aout = (audio_output_t *)p_this;
810     struct decklink_sys_t *decklink_sys = GetDLSys(VLC_OBJECT(aout));
811
812     vlc_mutex_lock(&decklink_sys->lock);
813     decklink_sys->aconn = getAConn(aout);
814     //decklink_sys->i_channels = var_InheritInteger(vd, AUDIO_CFG_PREFIX "audio-channels");
815     decklink_sys->i_rate = var_InheritInteger(aout, AUDIO_CFG_PREFIX "audio-rate");
816     decklink_sys->users++;
817     vlc_cond_signal(&decklink_sys->cond);
818     vlc_mutex_unlock(&decklink_sys->lock);
819
820     aout->play      = PlayAudio;
821     aout->start     = Start;
822     aout->flush     = Flush;
823     aout->time_get  = TimeGet;
824
825     aout->pause     = NULL;
826     aout->stop      = NULL;
827     aout->mute_set  = NULL;
828     aout->volume_set= NULL;
829
830     return VLC_SUCCESS;
831 }
832
833 static void CloseAudio(vlc_object_t *p_this)
834 {
835     struct decklink_sys_t *decklink_sys = GetDLSys(p_this);
836     vlc_mutex_lock(&decklink_sys->lock);
837     decklink_sys->aconn = 0;
838     vlc_mutex_unlock(&decklink_sys->lock);
839     ReleaseDLSys(p_this);
840 }