]> git.sesse.net Git - vlc/blobdiff - modules/video_output/decklink.cpp
Bump OpenGL capability on Windows
[vlc] / modules / video_output / decklink.cpp
index a777660c9424bbf2d0a3ea83011b5996c6741607..95d6cff1912fa8a30930e8421a9204eeaffa4164 100644 (file)
@@ -1,4 +1,4 @@
-/*****************************************************************************
+/*****************************************************************************
  * decklink.cpp: BlackMagic DeckLink SDI output module
  *****************************************************************************
  * Copyright (C) 2012-2013 Rafaël Carré
  *****************************************************************************/
 
 /*
- * TODO:
- *  - test non stereo audio
- *  - inherit aout/vout settings from corresponding module
- *  (allow to change settings between successive runs per instance)
- *  - allow several instances per process
- *  - get rid of process-wide destructor
+ * TODO: test non stereo audio
  */
 
 #define __STDC_FORMAT_MACROS
@@ -48,6 +43,7 @@
 #include <vlc_picture_pool.h>
 
 #include <vlc_block.h>
+#include <vlc_image.h>
 #include <vlc_atomic.h>
 #include <vlc_aout.h>
 #include <arpa/inet.h>
@@ -71,6 +67,15 @@ static const int pi_channels_maps[CHANNELS_MAX+1] =
 };
 #endif
 
+#define NOSIGNAL_INDEX_TEXT N_("Timelength after which we assume there is no signal.")
+#define NOSIGNAL_INDEX_LONGTEXT N_(\
+    "Timelength after which we assume there is no signal.\n"\
+    "After this delay we black out the video."\
+    )
+
+#define NOSIGNAL_IMAGE_TEXT N_("Picture to display on input signal loss.")
+#define NOSIGNAL_IMAGE_LONGTEXT NOSIGNAL_IMAGE_TEXT
+
 #define CARD_INDEX_TEXT N_("Output card")
 #define CARD_INDEX_LONGTEXT N_(\
     "DeckLink output card, if multiple exist. " \
@@ -87,7 +92,7 @@ static const int pi_channels_maps[CHANNELS_MAX+1] =
     "Audio connection for DeckLink output.")
 
 
-#define RATE_TEXT N_("Audio sampling rate in Hz")
+#define RATE_TEXT N_("Audio samplerate (Hz)")
 #define RATE_LONGTEXT N_(\
     "Audio sampling rate (in hertz) for DeckLink output. " \
     "0 disables audio output.")
@@ -118,30 +123,29 @@ static const char *const ppsz_videoconns_text[] = {
     N_("SDI"), N_("HDMI"), N_("Optical SDI"), N_("Component"), N_("Composite"), N_("S-video")
 };
 
-static const char *const ppsz_audioconns[] = {
-    "embedded", "aesebu", "analog"
-};
-static const char *const ppsz_audioconns_text[] = {
-    N_("Embedded"), N_("AES/EBU"), N_("Analog")
-};
-
-
 struct vout_display_sys_t
 {
     picture_pool_t *pool;
     bool tenbits;
+    int nosignal_delay;
+    picture_t *pic_nosignal;
 };
 
 /* Only one audio output module and one video output module
  * can be used per process.
  * We use a static mutex in audio/video submodules entry points.  */
-static struct
+struct decklink_sys_t
 {
-    IDeckLink *p_card;
     IDeckLinkOutput *p_output;
-    IDeckLinkConfiguration *p_config;
-    IDeckLinkDisplayModeIterator *p_display_iterator;
-    IDeckLinkIterator *decklink_iterator;
+
+    /*
+     * Synchronizes aout and vout modules:
+     * vout module waits until aout has been initialized.
+     * That means video-only output is NOT supported.
+     */
+    vlc_mutex_t lock;
+    vlc_cond_t cond;
+    uint8_t users;
 
     //int i_channels;
     int i_rate;
@@ -154,12 +158,6 @@ static struct
 
     /* XXX: workaround card clock drift */
     mtime_t offset;
-} decklink_sys = {
-    NULL, NULL, NULL, NULL, NULL,
-    0, 0,
-    -1, -1,
-    0, 0,
-    0,
 };
 
 /*****************************************************************************
@@ -196,6 +194,10 @@ vlc_module_begin()
                 MODE_TEXT, MODE_LONGTEXT, true)
     add_bool(VIDEO_CFG_PREFIX "tenbits", false,
                 VIDEO_TENBITS_TEXT, VIDEO_TENBITS_LONGTEXT, true)
+    add_integer(VIDEO_CFG_PREFIX "nosignal-delay", 5,
+                NOSIGNAL_INDEX_TEXT, NOSIGNAL_INDEX_LONGTEXT, true)
+    add_loadfile(VIDEO_CFG_PREFIX "nosignal-image", NULL,
+                NOSIGNAL_IMAGE_TEXT, NOSIGNAL_IMAGE_LONGTEXT, true)
 
 
     add_submodule ()
@@ -205,39 +207,74 @@ vlc_module_begin()
     set_capability("audio output", 0)
     set_callbacks (OpenAudio, CloseAudio)
     set_section(N_("Decklink Audio Options"), NULL)
-    add_string(AUDIO_CFG_PREFIX "audio-connection", "embedded",
-                AUDIO_CONNECTION_TEXT, AUDIO_CONNECTION_LONGTEXT, true)
-                change_string_list(ppsz_audioconns, ppsz_audioconns_text)
+    add_obsolete_string("audio-connection")
     add_integer(AUDIO_CFG_PREFIX "audio-rate", 48000,
                 RATE_TEXT, RATE_LONGTEXT, true)
     add_integer(AUDIO_CFG_PREFIX "audio-channels", 2,
                 CHANNELS_TEXT, CHANNELS_LONGTEXT, true)
 vlc_module_end ()
 
-// Connection mode
-static BMDAudioConnection getAConn(vlc_object_t *p_this)
+/* Protects decklink_sys_t creation/deletion */
+static vlc_mutex_t sys_lock = VLC_STATIC_MUTEX;
+
+static struct decklink_sys_t *GetDLSys(vlc_object_t *obj)
 {
-    BMDAudioConnection conn = bmdAudioConnectionEmbedded;
-    char *psz = var_InheritString(p_this, AUDIO_CFG_PREFIX "audio-connection");
-    if (!psz)
-        goto end;
+    vlc_object_t *libvlc = VLC_OBJECT(obj->p_libvlc);
+    struct decklink_sys_t *sys;
+
+    vlc_mutex_lock(&sys_lock);
+
+    if (var_Type(libvlc, "decklink-sys") == VLC_VAR_ADDRESS)
+        sys = (struct decklink_sys_t*)var_GetAddress(libvlc, "decklink-sys");
+    else {
+        sys = (struct decklink_sys_t*)malloc(sizeof(*sys));
+        if (sys) {
+            sys->p_output = NULL;
+            sys->offset = 0;
+            sys->users = 0;
+            sys->i_rate = -1;
+            vlc_mutex_init(&sys->lock);
+            vlc_cond_init(&sys->cond);
+            var_Create(libvlc, "decklink-sys", VLC_VAR_ADDRESS);
+            var_SetAddress(libvlc, "decklink-sys", (void*)sys);
+        }
+    }
 
-    if (!strcmp(psz, "embedded"))
-        conn = bmdAudioConnectionEmbedded;
-    else if (!strcmp(psz, "aesebu"))
-        conn = bmdAudioConnectionAESEBU;
-    else if (!strcmp(psz, "analog"))
-        conn = bmdAudioConnectionAnalog;
+    vlc_mutex_unlock(&sys_lock);
+    return sys;
+}
 
-end:
-    free(psz);
-    return conn;
+static void ReleaseDLSys(vlc_object_t *obj)
+{
+    vlc_object_t *libvlc = VLC_OBJECT(obj->p_libvlc);
+
+    vlc_mutex_lock(&sys_lock);
+
+    struct decklink_sys_t *sys = (struct decklink_sys_t*)var_GetAddress(libvlc, "decklink-sys");
+
+    if (--sys->users == 0) {
+        msg_Dbg(obj, "Destroying decklink data");
+        vlc_mutex_destroy(&sys->lock);
+        vlc_cond_destroy(&sys->cond);
+
+        if (sys->p_output) {
+            sys->p_output->StopScheduledPlayback(0, NULL, 0);
+            sys->p_output->DisableVideoOutput();
+            sys->p_output->DisableAudioOutput();
+            sys->p_output->Release();
+        }
+
+        free(sys);
+        var_Destroy(libvlc, "decklink-sys");
+    }
+
+    vlc_mutex_unlock(&sys_lock);
 }
 
-static BMDVideoConnection getVConn(vlc_object_t *p_this)
+static BMDVideoConnection getVConn(vout_display_t *vd)
 {
     BMDVideoConnection conn = bmdVideoConnectionSDI;
-    char *psz = var_InheritString(p_this, VIDEO_CFG_PREFIX "video-connection");
+    char *psz = var_InheritString(vd, VIDEO_CFG_PREFIX "video-connection");
     if (!psz)
         goto end;
 
@@ -263,69 +300,40 @@ end:
  *
  *****************************************************************************/
 
-static atomic_uint initialized = ATOMIC_VAR_INIT(0);
-
-static void CloseDecklink(void) __attribute__((destructor));
-static void CloseDecklink(void)
-{
-    if (!atomic_load(&initialized))
-        return;
-
-    decklink_sys.p_output->StopScheduledPlayback(0, NULL, 0);
-    decklink_sys.p_output->DisableVideoOutput();
-    decklink_sys.p_output->DisableAudioOutput();
-
-    if (decklink_sys.decklink_iterator)
-        decklink_sys.decklink_iterator->Release();
-
-    if (decklink_sys.p_display_iterator)
-        decklink_sys.p_display_iterator->Release();
-
-    if (decklink_sys.p_config)
-        decklink_sys.p_config->Release();
-
-    if (decklink_sys.p_output)
-        decklink_sys.p_output->Release();
-
-    if (decklink_sys.p_card)
-        decklink_sys.p_card->Release();
-}
-
-static int OpenDecklink(vlc_object_t *p_this)
+static struct decklink_sys_t *OpenDecklink(vout_display_t *vd)
 {
-    vout_display_t *vd = (vout_display_t *)p_this;
     vout_display_sys_t *sys = vd->sys;
 #define CHECK(message) do { \
     if (result != S_OK) \
     { \
-        msg_Err(p_this, message ": 0x%X", result); \
+        msg_Err(vd, message ": 0x%X", result); \
         goto error; \
     } \
 } while(0)
 
     HRESULT result;
+    IDeckLinkIterator *decklink_iterator = NULL;
     IDeckLinkDisplayMode *p_display_mode = NULL;
-    static vlc_mutex_t lock = VLC_STATIC_MUTEX;
+    IDeckLinkDisplayModeIterator *p_display_iterator = NULL;
+    IDeckLinkConfiguration *p_config = NULL;
+    IDeckLink *p_card = NULL;
 
-    vlc_mutex_lock(&lock);
+    struct decklink_sys_t *decklink_sys = GetDLSys(VLC_OBJECT(vd));
+    vlc_mutex_lock(&decklink_sys->lock);
+    decklink_sys->users++;
 
-    if (atomic_load(&initialized)) {
-        /* already initialized */
-        vlc_mutex_unlock(&lock);
-        return VLC_SUCCESS;
-    }
+    /* wait until aout is ready */
+    while (decklink_sys->i_rate == -1)
+        vlc_cond_wait(&decklink_sys->cond, &decklink_sys->lock);
 
-    //decklink_sys.i_channels = var_InheritInteger(p_this, AUDIO_CFG_PREFIX "audio-channels");
-    decklink_sys.i_rate = var_InheritInteger(p_this, AUDIO_CFG_PREFIX "audio-rate");
-    int i_card_index = var_InheritInteger(p_this, CFG_PREFIX "card-index");
-    BMDVideoConnection vconn = getVConn(p_this);
-    BMDAudioConnection aconn = getAConn(p_this);
-    char *mode = var_InheritString(p_this, VIDEO_CFG_PREFIX "mode");
+    int i_card_index = var_InheritInteger(vd, CFG_PREFIX "card-index");
+    BMDVideoConnection vconn = getVConn(vd);
+    char *mode = var_InheritString(vd, VIDEO_CFG_PREFIX "mode");
     size_t len = mode ? strlen(mode) : 0;
     if (!mode || len > 4)
     {
         free(mode);
-        msg_Err(p_this, "Missing or invalid mode");
+        msg_Err(vd, "Missing or invalid mode");
         goto error;
     }
 
@@ -336,60 +344,53 @@ static int OpenDecklink(vlc_object_t *p_this)
 
     if (i_card_index < 0)
     {
-        msg_Err(p_this, "Invalid card index %d", i_card_index);
+        msg_Err(vd, "Invalid card index %d", i_card_index);
         goto error;
     }
 
-    decklink_sys.decklink_iterator = CreateDeckLinkIteratorInstance();
-    if (!decklink_sys.decklink_iterator)
+    decklink_iterator = CreateDeckLinkIteratorInstance();
+    if (!decklink_iterator)
     {
-        msg_Err(p_this, "DeckLink drivers not found.");
+        msg_Err(vd, "DeckLink drivers not found.");
         goto error;
     }
 
     for(int i = 0; i <= i_card_index; ++i)
     {
-        if (decklink_sys.p_card)
-            decklink_sys.p_card->Release();
-        result = decklink_sys.decklink_iterator->Next(&decklink_sys.p_card);
+        if (p_card)
+            p_card->Release();
+        result = decklink_iterator->Next(&p_card);
         CHECK("Card not found");
     }
 
     const char *psz_model_name;
-    result = decklink_sys.p_card->GetModelName(&psz_model_name);
+    result = p_card->GetModelName(&psz_model_name);
     CHECK("Unknown model name");
 
-    msg_Dbg(p_this, "Opened DeckLink PCI card %s", psz_model_name);
+    msg_Dbg(vd, "Opened DeckLink PCI card %s", psz_model_name);
 
-    result = decklink_sys.p_card->QueryInterface(IID_IDeckLinkOutput,
-        (void**)&decklink_sys.p_output);
+    result = p_card->QueryInterface(IID_IDeckLinkOutput,
+        (void**)&decklink_sys->p_output);
     CHECK("No outputs");
 
-    result = decklink_sys.p_card->QueryInterface(IID_IDeckLinkConfiguration,
-        (void**)&decklink_sys.p_config);
+    result = p_card->QueryInterface(IID_IDeckLinkConfiguration,
+        (void**)&p_config);
     CHECK("Could not get config interface");
 
     if (vconn)
     {
-        result = decklink_sys.p_config->SetInt(
+        result = p_config->SetInt(
             bmdDeckLinkConfigVideoOutputConnection, vconn);
         CHECK("Could not set video output connection");
     }
 
-    if (aconn)
-    {
-        result = decklink_sys.p_config->SetInt(
-            bmdDeckLinkConfigAudioInputConnection, aconn);
-        CHECK("Could not set audio output connection");
-    }
-
-    result = decklink_sys.p_output->GetDisplayModeIterator(&decklink_sys.p_display_iterator);
+    result = decklink_sys->p_output->GetDisplayModeIterator(&p_display_iterator);
     CHECK("Could not enumerate display modes");
 
     for (; ; p_display_mode->Release())
     {
         int w, h;
-        result = decklink_sys.p_display_iterator->Next(&p_display_mode);
+        result = p_display_iterator->Next(&p_display_mode);
         if (result != S_OK)
             break;
 
@@ -399,26 +400,23 @@ static int OpenDecklink(vlc_object_t *p_this)
         result = p_display_mode->GetName(&psz_mode_name);
         CHECK("Could not get display mode name");
 
-        result = p_display_mode->GetFrameRate(&decklink_sys.frameduration,
-            &decklink_sys.timescale);
+        result = p_display_mode->GetFrameRate(&decklink_sys->frameduration,
+            &decklink_sys->timescale);
         CHECK("Could not get frame rate");
 
         w = p_display_mode->GetWidth();
         h = p_display_mode->GetHeight();
-        msg_Dbg(p_this, "Found mode '%4.4s': %s (%dx%d, %.3f fps)",
+        msg_Dbg(vd, "Found mode '%4.4s': %s (%dx%d, %.3f fps)",
                 (char*)&mode_id, psz_mode_name, w, h,
-                double(decklink_sys.timescale) / decklink_sys.frameduration);
-        msg_Dbg(p_this, "scale %d dur %d", (int)decklink_sys.timescale,
-            (int)decklink_sys.frameduration);
+                double(decklink_sys->timescale) / decklink_sys->frameduration);
+        msg_Dbg(vd, "scale %d dur %d", (int)decklink_sys->timescale,
+            (int)decklink_sys->frameduration);
 
         if (wanted_mode_id != mode_id)
             continue;
 
-        decklink_sys.i_width = w;
-        decklink_sys.i_height = h;
-
-        p_display_mode->Release();
-        p_display_mode = NULL;
+        decklink_sys->i_width = w;
+        decklink_sys->i_height = h;
 
         mode_id = htonl(mode_id);
 
@@ -433,65 +431,75 @@ static int OpenDecklink(vlc_object_t *p_this)
         BMDDisplayModeSupport support;
         IDeckLinkDisplayMode *resultMode;
 
-        result = decklink_sys.p_output->DoesSupportVideoMode(mode_id,
+        result = decklink_sys->p_output->DoesSupportVideoMode(mode_id,
             sys->tenbits ? bmdFormat10BitYUV : bmdFormat8BitYUV,
             flags, &support, &resultMode);
         CHECK("Does not support video mode");
         if (support == bmdDisplayModeNotSupported)
         {
-            msg_Err(p_this, "Video mode not supported");
+            msg_Err(vd, "Video mode not supported");
                 goto error;
         }
 
-        result = decklink_sys.p_output->EnableVideoOutput(mode_id, flags);
+        result = decklink_sys->p_output->EnableVideoOutput(mode_id, flags);
         CHECK("Could not enable video output");
 
         break;
     }
 
-    if (decklink_sys.i_width < 0)
+    if (decklink_sys->i_width < 0 || decklink_sys->i_width & 1)
     {
-        msg_Err(p_this, "Unknown video mode specified.");
+        msg_Err(vd, "Unknown video mode specified.");
         goto error;
     }
 
-    /* audio */
-    if (/*decklink_sys.i_channels > 0 &&*/ decklink_sys.i_rate > 0)
+    if (/*decklink_sys->i_channels > 0 &&*/ decklink_sys->i_rate > 0)
     {
-        result = decklink_sys.p_output->EnableAudioOutput(
-            decklink_sys.i_rate,
+        result = decklink_sys->p_output->EnableAudioOutput(
+            decklink_sys->i_rate,
             bmdAudioSampleType16bitInteger,
-            /*decklink_sys.i_channels*/ 2,
+            /*decklink_sys->i_channels*/ 2,
             bmdAudioOutputStreamTimestamped);
     }
-    else
-    {
-        result = decklink_sys.p_output->DisableAudioOutput();
-    }
-    CHECK("Could not enable audio output");
-
+    CHECK("Could not start audio output");
 
     /* start */
-    result = decklink_sys.p_output->StartScheduledPlayback(
-        (mdate() * decklink_sys.timescale) / CLOCK_FREQ, decklink_sys.timescale, 1.0);
+    result = decklink_sys->p_output->StartScheduledPlayback(
+        (mdate() * decklink_sys->timescale) / CLOCK_FREQ, decklink_sys->timescale, 1.0);
     CHECK("Could not start playback");
 
-    atomic_store(&initialized, 1);
+    p_config->Release();
+    p_display_mode->Release();
+    p_display_iterator->Release();
+    p_card->Release();
+    decklink_iterator->Release();
 
-    vlc_mutex_unlock(&lock);
-    return VLC_SUCCESS;
+    vlc_mutex_unlock(&decklink_sys->lock);
+
+    vout_display_DeleteWindow(vd, NULL);
 
+    return decklink_sys;
 
 error:
-    if (decklink_sys.decklink_iterator)
-        decklink_sys.decklink_iterator->Release();
-    if (decklink_sys.p_display_iterator)
-        decklink_sys.p_display_iterator->Release();
+    if (decklink_sys->p_output) {
+        decklink_sys->p_output->Release();
+        decklink_sys->p_output = NULL;
+    }
+    if (p_card)
+        p_card->Release();
+    if (p_config)
+        p_config->Release();
+    if (p_display_iterator)
+        p_display_iterator->Release();
+    if (decklink_iterator)
+        decklink_iterator->Release();
     if (p_display_mode)
         p_display_mode->Release();
 
-    vlc_mutex_unlock(&lock);
-    return VLC_EGENERIC;
+    vlc_mutex_unlock(&decklink_sys->lock);
+    ReleaseDLSys(VLC_OBJECT(vd));
+
+    return NULL;
 #undef CHECK
 }
 
@@ -576,18 +584,47 @@ static void v210_convert(void *frame_bytes, picture_t *pic, int dst_stride)
 static void DisplayVideo(vout_display_t *vd, picture_t *picture, subpicture_t *)
 {
     vout_display_sys_t *sys = vd->sys;
+    struct decklink_sys_t *decklink_sys = GetDLSys(VLC_OBJECT(vd));
+    mtime_t now = mdate();
 
     if (!picture)
         return;
 
+    picture_t *orig_picture = picture;
+
+    if (now - picture->date > sys->nosignal_delay * CLOCK_FREQ) {
+        msg_Dbg(vd, "no signal");
+        if (sys->pic_nosignal) {
+            picture = sys->pic_nosignal;
+        } else {
+            if (sys->tenbits) { // I422_10L
+                plane_t *y = &picture->p[0];
+                memset(y->p_pixels, 0x0, y->i_lines * y->i_pitch);
+                for (int i = 1; i < picture->i_planes; i++) {
+                    plane_t *p = &picture->p[i];
+                    size_t len = p->i_lines * p->i_pitch / 2;
+                    int16_t *data = (int16_t*)p->p_pixels;
+                    for (size_t j = 0; j < len; j++) // XXX: SIMD
+                        data[j] = 0x200;
+                }
+            } else { // UYVY
+                size_t len = picture->p[0].i_lines * picture->p[0].i_pitch;
+                for (size_t i = 0; i < len; i+= 2) { // XXX: SIMD
+                    picture->p[0].p_pixels[i+0] = 0x80;
+                    picture->p[0].p_pixels[i+1] = 0;
+                }
+            }
+        }
+        picture->date = now;
+    }
+
     HRESULT result;
     int w, h, stride, length;
-    mtime_t now;
-    w = decklink_sys.i_width;
-    h = decklink_sys.i_height;
+    w = decklink_sys->i_width;
+    h = decklink_sys->i_height;
 
     IDeckLinkMutableVideoFrame *pDLVideoFrame;
-    result = decklink_sys.p_output->CreateVideoFrame(w, h, w*3,
+    result = decklink_sys->p_output->CreateVideoFrame(w, h, w*3,
         sys->tenbits ? bmdFormat10BitYUV : bmdFormat8BitYUV,
         bmdFrameFlagDefault, &pDLVideoFrame);
 
@@ -612,10 +649,10 @@ static void DisplayVideo(vout_display_t *vd, picture_t *picture, subpicture_t *)
 
 
     // compute frame duration in CLOCK_FREQ units
-    length = (decklink_sys.frameduration * CLOCK_FREQ) / decklink_sys.timescale;
+    length = (decklink_sys->frameduration * CLOCK_FREQ) / decklink_sys->timescale;
 
-    picture->date -= decklink_sys.offset;
-    result = decklink_sys.p_output->ScheduleVideoFrame(pDLVideoFrame,
+    picture->date -= decklink_sys->offset;
+    result = decklink_sys->p_output->ScheduleVideoFrame(pDLVideoFrame,
         picture->date, length, CLOCK_FREQ);
 
     if (result != S_OK) {
@@ -624,72 +661,93 @@ static void DisplayVideo(vout_display_t *vd, picture_t *picture, subpicture_t *)
         goto end;
     }
 
-    now = mdate() - decklink_sys.offset;
+    now = mdate() - decklink_sys->offset;
 
     BMDTimeValue decklink_now;
     double speed;
-    decklink_sys.p_output->GetScheduledStreamTime (CLOCK_FREQ, &decklink_now, &speed);
+    decklink_sys->p_output->GetScheduledStreamTime (CLOCK_FREQ, &decklink_now, &speed);
 
     if ((now - decklink_now) > 400000) {
         /* XXX: workaround card clock drift */
-        decklink_sys.offset += 50000;
-        msg_Err(vd, "Delaying: offset now %"PRId64"", decklink_sys.offset);
+        decklink_sys->offset += 50000;
+        msg_Err(vd, "Delaying: offset now %"PRId64"", decklink_sys->offset);
     }
 
 end:
     if (pDLVideoFrame)
         pDLVideoFrame->Release();
-    picture_Release(picture);
+    picture_Release(orig_picture);
 }
 
 static int ControlVideo(vout_display_t *vd, int query, va_list args)
 {
-    VLC_UNUSED(vd);
-    const vout_display_cfg_t *cfg;
-
-    switch (query) {
-    case VOUT_DISPLAY_CHANGE_FULLSCREEN:
-        cfg = va_arg(args, const vout_display_cfg_t *);
-        return cfg->is_fullscreen ? VLC_EGENERIC : VLC_SUCCESS;
-    default:
-        return VLC_EGENERIC;
-    }
+    (void) vd; (void) query; (void) args;
+    return VLC_EGENERIC;
 }
 
-static atomic_uint video_lock = ATOMIC_VAR_INIT(0);
 static int OpenVideo(vlc_object_t *p_this)
 {
     vout_display_t *vd = (vout_display_t *)p_this;
     vout_display_sys_t *sys;
-
-    if (atomic_exchange(&video_lock, 1)) {
-        msg_Err(vd, "Decklink video module already busy");
-        return VLC_EGENERIC;
-    }
+    struct decklink_sys_t *decklink_sys;
 
     vd->sys = sys = (vout_display_sys_t*)malloc(sizeof(*sys));
     if (!sys)
         return VLC_ENOMEM;
 
-    if (OpenDecklink(p_this) != VLC_SUCCESS)
-        goto error;
-
-    if (decklink_sys.i_width & 1) {
-        msg_Err(vd, "Invalid width %d", decklink_sys.i_width);
-        goto error;
+    sys->tenbits = var_InheritBool(p_this, VIDEO_CFG_PREFIX "tenbits");
+    sys->nosignal_delay = var_InheritInteger(p_this, VIDEO_CFG_PREFIX "nosignal-delay");
+    sys->pic_nosignal = NULL;
+
+    decklink_sys = OpenDecklink(vd);
+    if (!decklink_sys) {
+        if (sys->pic_nosignal)
+            picture_Release(sys->pic_nosignal);
+        free(sys);
+        return VLC_EGENERIC;
     }
 
     sys->pool = NULL;
 
-    sys->tenbits = var_InheritBool(p_this, VIDEO_CFG_PREFIX "tenbits");
     vd->fmt.i_chroma = sys->tenbits
         ? VLC_CODEC_I422_10L /* we will convert to v210 */
         : VLC_CODEC_UYVY;
     //video_format_FixRgb(&(vd->fmt));
 
-    vd->fmt.i_width = decklink_sys.i_width;
-    vd->fmt.i_height = decklink_sys.i_height;
+    vd->fmt.i_width = decklink_sys->i_width;
+    vd->fmt.i_height = decklink_sys->i_height;
+
+    char *pic_file = var_InheritString(p_this, VIDEO_CFG_PREFIX "nosignal-image");
+    if (pic_file) {
+        image_handler_t *img = image_HandlerCreate(p_this);
+        if (!img) {
+            msg_Err(p_this, "Could not create image converter");
+        } else {
+            video_format_t in, dummy;
+
+            video_format_Init(&in, 0);
+            video_format_Setup(&in, 0, vd->fmt.i_width, vd->fmt.i_height,
+                    vd->fmt.i_width, vd->fmt.i_height, 1, 1);
+
+            video_format_Init(&dummy, 0);
+
+            picture_t *png = image_ReadUrl(img, pic_file, &dummy, &in);
+            if (png) {
+                msg_Err(p_this, "Converting");
+                sys->pic_nosignal = image_Convert(img, png, &in, &vd->fmt);
+                picture_Release(png);
+            }
 
+            image_HandlerDelete(img);
+        }
+
+        free(pic_file);
+        if (!sys->pic_nosignal) {
+            CloseVideo(p_this);
+            msg_Err(p_this, "Could not create no signal picture");
+            return VLC_EGENERIC;
+        }
+    }
     vd->info.has_hide_mouse = true;
     vd->pool    = PoolVideo;
     vd->prepare = NULL;
@@ -699,10 +757,6 @@ static int OpenVideo(vlc_object_t *p_this)
     vout_display_SendEventFullscreen(vd, false);
 
     return VLC_SUCCESS;
-
-error:
-    free(sys);
-    return VLC_EGENERIC;
 }
 
 static void CloseVideo(vlc_object_t *p_this)
@@ -711,11 +765,14 @@ static void CloseVideo(vlc_object_t *p_this)
     vout_display_sys_t *sys = vd->sys;
 
     if (sys->pool)
-        picture_pool_Delete(sys->pool);
+        picture_pool_Release(sys->pool);
+
+    if (sys->pic_nosignal)
+        picture_Release(sys->pic_nosignal);
 
     free(sys);
 
-    atomic_fetch_sub(&video_lock, 1);
+    ReleaseDLSys(p_this);
 }
 
 /*****************************************************************************
@@ -724,14 +781,18 @@ static void CloseVideo(vlc_object_t *p_this)
 
 static void Flush (audio_output_t *aout, bool drain)
 {
-    if (!atomic_load(&initialized))
+    struct decklink_sys_t *decklink_sys = GetDLSys(VLC_OBJECT(aout));
+    vlc_mutex_lock(&decklink_sys->lock);
+    IDeckLinkOutput *p_output = decklink_sys->p_output;
+    vlc_mutex_unlock(&decklink_sys->lock);
+    if (!p_output)
         return;
 
     if (drain) {
         uint32_t samples;
-        decklink_sys.p_output->GetBufferedAudioSampleFrameCount(&samples);
-        msleep(CLOCK_FREQ * samples / decklink_sys.i_rate);
-    } else if (decklink_sys.p_output->FlushBufferedAudioSamples() == E_FAIL)
+        decklink_sys->p_output->GetBufferedAudioSampleFrameCount(&samples);
+        msleep(CLOCK_FREQ * samples / decklink_sys->i_rate);
+    } else if (decklink_sys->p_output->FlushBufferedAudioSamples() == E_FAIL)
         msg_Err(aout, "Flush failed");
 }
 
@@ -741,12 +802,17 @@ static int TimeGet(audio_output_t *, mtime_t* restrict)
     return -1;
 }
 
-static int Start(audio_output_t *, audio_sample_format_t *restrict fmt)
+static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
 {
+    struct decklink_sys_t *decklink_sys = GetDLSys(VLC_OBJECT(aout));
+
+    if (decklink_sys->i_rate == 0)
+        return VLC_EGENERIC;
+
     fmt->i_format = VLC_CODEC_S16N;
-    fmt->i_channels = 2; //decklink_sys.i_channels;
+    fmt->i_channels = 2; //decklink_sys->i_channels;
     fmt->i_physical_channels = AOUT_CHANS_STEREO; //pi_channels_maps[fmt->i_channels];
-    fmt->i_rate = decklink_sys.i_rate;
+    fmt->i_rate = decklink_sys->i_rate;
     fmt->i_bitspersample = 16;
     fmt->i_blockalign = fmt->i_channels * fmt->i_bitspersample /8 ;
     fmt->i_frame_length  = FRAME_SIZE;
@@ -756,14 +822,20 @@ static int Start(audio_output_t *, audio_sample_format_t *restrict fmt)
 
 static void PlayAudio(audio_output_t *aout, block_t *audio)
 {
-    if (!atomic_load(&initialized))
+    struct decklink_sys_t *decklink_sys = GetDLSys(VLC_OBJECT(aout));
+    vlc_mutex_lock(&decklink_sys->lock);
+    IDeckLinkOutput *p_output = decklink_sys->p_output;
+    vlc_mutex_unlock(&decklink_sys->lock);
+    if (!p_output) {
+        block_Release(audio);
         return;
+    }
 
-    audio->i_pts -= decklink_sys.offset;
+    audio->i_pts -= decklink_sys->offset;
 
-    uint32_t sampleFrameCount = audio->i_buffer / (2 * 2 /*decklink_sys.i_channels*/);
+    uint32_t sampleFrameCount = audio->i_buffer / (2 * 2 /*decklink_sys->i_channels*/);
     uint32_t written;
-    HRESULT result = decklink_sys.p_output->ScheduleAudioSamples(
+    HRESULT result = decklink_sys->p_output->ScheduleAudioSamples(
             audio->p_buffer, sampleFrameCount, audio->i_pts, CLOCK_FREQ, &written);
 
     if (result != S_OK)
@@ -774,15 +846,17 @@ static void PlayAudio(audio_output_t *aout, block_t *audio)
     block_Release(audio);
 }
 
-static atomic_uint audio_lock = ATOMIC_VAR_INIT(0);
 static int OpenAudio(vlc_object_t *p_this)
 {
     audio_output_t *aout = (audio_output_t *)p_this;
+    struct decklink_sys_t *decklink_sys = GetDLSys(VLC_OBJECT(aout));
 
-    if (atomic_exchange(&audio_lock, 1)) {
-        msg_Err(aout, "Decklink audio module already busy");
-        return VLC_EGENERIC;
-    }
+    vlc_mutex_lock(&decklink_sys->lock);
+    //decklink_sys->i_channels = var_InheritInteger(vd, AUDIO_CFG_PREFIX "audio-channels");
+    decklink_sys->i_rate = var_InheritInteger(aout, AUDIO_CFG_PREFIX "audio-rate");
+    decklink_sys->users++;
+    vlc_cond_signal(&decklink_sys->cond);
+    vlc_mutex_unlock(&decklink_sys->lock);
 
     aout->play      = PlayAudio;
     aout->start     = Start;
@@ -797,7 +871,10 @@ static int OpenAudio(vlc_object_t *p_this)
     return VLC_SUCCESS;
 }
 
-static void CloseAudio(vlc_object_t *)
+static void CloseAudio(vlc_object_t *p_this)
 {
-    atomic_fetch_sub(&audio_lock, 1);
+    struct decklink_sys_t *decklink_sys = GetDLSys(p_this);
+    vlc_mutex_lock(&decklink_sys->lock);
+    vlc_mutex_unlock(&decklink_sys->lock);
+    ReleaseDLSys(p_this);
 }