-/*****************************************************************************
+/*****************************************************************************
* 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
#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>
};
#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. " \
"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.")
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;
/* XXX: workaround card clock drift */
mtime_t offset;
-} decklink_sys = {
- NULL, NULL, NULL, NULL, NULL,
- 0, 0,
- -1, -1,
- 0, 0,
- 0,
};
/*****************************************************************************
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 ()
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;
*
*****************************************************************************/
-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;
}
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;
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);
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
}
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);
// 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) {
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;
vout_display_SendEventFullscreen(vd, false);
return VLC_SUCCESS;
-
-error:
- free(sys);
- return VLC_EGENERIC;
}
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);
}
/*****************************************************************************
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");
}
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;
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)
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;
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);
}