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