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