* decklink.cpp: BlackMagic DeckLink SDI input module
*****************************************************************************
* Copyright (C) 2010 Steinar H. Gunderson
- * Copyright (C) 2009 Michael Niedermayer <michaelni@gmx.at>
- * Copyright (c) 2009 Baptiste Coudurier <baptiste dot coudurier at gmail dot com>
+ * Copyright (C) 2012-2014 Rafaël Carré
*
* Authors: Steinar H. Gunderson <steinar+vlc@gunderson.no>
+ Rafaël Carré <funman@videolanorg>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
#include <DeckLinkAPI.h>
#include <DeckLinkAPIDispatch.cpp>
+#include "sdi.h"
+
static int Open (vlc_object_t *);
static void Close(vlc_object_t *);
"DeckLink capture card to use, if multiple exist. " \
"The cards are numbered from 0.")
-#define MODE_TEXT N_("Desired input video mode")
+#define MODE_TEXT N_("Desired input video mode. Leave empty for autodetection.")
#define MODE_LONGTEXT N_( \
"Desired input video mode for DeckLink captures. " \
"This value should be a FOURCC code in textual " \
add_integer("decklink-card-index", 0,
CARD_INDEX_TEXT, CARD_INDEX_LONGTEXT, true)
- add_string("decklink-mode", "pal ",
+ add_string("decklink-mode", NULL,
MODE_TEXT, MODE_LONGTEXT, true)
add_string("decklink-audio-connection", 0,
AUDIO_CONNECTION_TEXT, AUDIO_CONNECTION_LONGTEXT, true)
change_string_list(ppsz_videoconns, ppsz_videoconns_text)
add_string("decklink-aspect-ratio", NULL,
ASPECT_RATIO_TEXT, ASPECT_RATIO_LONGTEXT, true)
- add_bool("decklink-tenbits", true, N_("10 bits"), N_("10 bits"), true)
+ add_bool("decklink-tenbits", false, N_("10 bits"), N_("10 bits"), true)
add_shortcut("decklink")
set_capability("access_demux", 10)
/* We need to hold onto the IDeckLinkConfiguration object, or our settings will not apply.
See section 2.4.15 of the Blackmagic Decklink SDK documentation. */
IDeckLinkConfiguration *config;
+ IDeckLinkAttributes *attributes;
+
+ bool autodetect;
es_out_id_t *video_es;
es_out_id_t *audio_es;
bool tenbits;
};
+static const char *GetFieldDominance(BMDFieldDominance dom, uint32_t *flags)
+{
+ switch(dom)
+ {
+ case bmdProgressiveFrame:
+ return "";
+ case bmdProgressiveSegmentedFrame:
+ return ", segmented";
+ case bmdLowerFieldFirst:
+ *flags = BLOCK_FLAG_BOTTOM_FIELD_FIRST;
+ return ", interlaced [BFF]";
+ case bmdUpperFieldFirst:
+ *flags = BLOCK_FLAG_TOP_FIELD_FIRST;
+ return ", interlaced [TFF]";
+ case bmdUnknownFieldDominance:
+ default:
+ return ", unknown field dominance";
+ }
+}
+
+static es_format_t GetModeSettings(demux_t *demux, IDeckLinkDisplayMode *m)
+{
+ demux_sys_t *sys = demux->p_sys;
+ uint32_t flags = 0;
+ (void)GetFieldDominance(m->GetFieldDominance(), &flags);
+
+ BMDTimeValue frame_duration, time_scale;
+ if (m->GetFrameRate(&frame_duration, &time_scale) != S_OK) {
+ time_scale = 0;
+ frame_duration = 1;
+ }
+
+ es_format_t video_fmt;
+ vlc_fourcc_t chroma; chroma = sys->tenbits ? VLC_CODEC_I422_10L : VLC_CODEC_UYVY;
+ es_format_Init(&video_fmt, VIDEO_ES, chroma);
+
+ video_fmt.video.i_width = m->GetWidth();
+ video_fmt.video.i_height = m->GetHeight();
+ video_fmt.video.i_sar_num = 1;
+ video_fmt.video.i_sar_den = 1;
+ video_fmt.video.i_frame_rate = time_scale;
+ video_fmt.video.i_frame_rate_base = frame_duration;
+ video_fmt.i_bitrate = video_fmt.video.i_width * video_fmt.video.i_height * video_fmt.video.i_frame_rate * 2 * 8;
+
+ unsigned aspect_num, aspect_den;
+ if (!var_InheritURational(demux, &aspect_num, &aspect_den, "decklink-aspect-ratio") &&
+ aspect_num > 0 && aspect_den > 0) {
+ video_fmt.video.i_sar_num = aspect_num * video_fmt.video.i_height;
+ video_fmt.video.i_sar_den = aspect_den * video_fmt.video.i_width;
+ }
+
+ sys->dominance_flags = flags;
+
+ return video_fmt;
+}
+
class DeckLinkCaptureDelegate : public IDeckLinkInputCallback
{
public:
DeckLinkCaptureDelegate(demux_t *demux) : demux_(demux)
{
- vlc_atomic_set(&m_ref_, 1);
+ atomic_store(&m_ref_, 1);
}
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID *) { return E_NOINTERFACE; }
virtual ULONG STDMETHODCALLTYPE AddRef(void)
{
- return vlc_atomic_inc(&m_ref_);
+ return atomic_fetch_add(&m_ref_, 1);
}
virtual ULONG STDMETHODCALLTYPE Release(void)
{
- uintptr_t new_ref = vlc_atomic_dec(&m_ref_);
+ uintptr_t new_ref = atomic_fetch_sub(&m_ref_, 1);
if (new_ref == 0)
delete this;
return new_ref;
}
- virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged(BMDVideoInputFormatChangedEvents, IDeckLinkDisplayMode*, BMDDetectedVideoInputFormatFlags)
+ virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged(BMDVideoInputFormatChangedEvents events, IDeckLinkDisplayMode *mode, BMDDetectedVideoInputFormatFlags)
{
- msg_Dbg(demux_, "Video input format changed");
- return S_OK;
- }
-
- virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(IDeckLinkVideoInputFrame*, IDeckLinkAudioInputPacket*);
+ demux_sys_t *sys = demux_->p_sys;
-private:
- vlc_atomic_t m_ref_;
- demux_t *demux_;
-};
+ if( !(events & bmdVideoInputDisplayModeChanged ))
+ return S_OK;
-static inline uint32_t av_le2ne32(uint32_t val)
-{
- union {
- uint32_t v;
- uint8_t b[4];
- } u;
- u.v = val;
- return (u.b[0] << 0) | (u.b[1] << 8) | (u.b[2] << 16) | (u.b[3] << 24);
-}
+ const char *mode_name;
+ if (mode->GetName(&mode_name) != S_OK)
+ mode_name = "unknown";
-static void v210_convert(uint16_t *dst, const uint32_t *bytes, const int width, const int height)
-{
- const int stride = ((width + 47) / 48) * 48 * 8 / 3 / 4;
- uint16_t *y = &dst[0];
- uint16_t *u = &dst[width * height * 2 / 2];
- uint16_t *v = &dst[width * height * 3 / 2];
-
-#define READ_PIXELS(a, b, c) \
- do { \
- val = av_le2ne32(*src++); \
- *a++ = val & 0x3FF; \
- *b++ = (val >> 10) & 0x3FF; \
- *c++ = (val >> 20) & 0x3FF; \
- } while (0)
-
- for (int h = 0; h < height; h++) {
- const uint32_t *src = bytes;
- uint32_t val = 0;
- int w;
- for (w = 0; w < width - 5; w += 6) {
- READ_PIXELS(u, y, v);
- READ_PIXELS(y, u, y);
- READ_PIXELS(v, y, u);
- READ_PIXELS(y, v, y);
+ msg_Dbg(demux_, "Video input format changed to %s", mode_name);
+ if (!sys->autodetect) {
+ msg_Err(demux_, "Video format detection disabled");
+ return S_OK;
}
- if (w < width - 1) {
- READ_PIXELS(u, y, v);
- val = av_le2ne32(*src++);
- *y++ = val & 0x3FF;
- }
- if (w < width - 3) {
- *u++ = (val >> 10) & 0x3FF;
- *y++ = (val >> 20) & 0x3FF;
+ es_out_Del(demux_->out, sys->video_es);
+ es_format_t video_fmt = GetModeSettings(demux_, mode);
+ sys->video_es = es_out_Add(demux_->out, &video_fmt);
- val = av_le2ne32(*src++);
- *v++ = val & 0x3FF;
- *y++ = (val >> 10) & 0x3FF;
- }
+ BMDPixelFormat fmt = sys->tenbits ? bmdFormat10BitYUV : bmdFormat8BitYUV;
+ sys->input->PauseStreams();
+ sys->input->EnableVideoInput( mode->GetDisplayMode(), fmt, bmdVideoInputEnableFormatDetection );
+ sys->input->FlushStreams();
+ sys->input->StartStreams();
- bytes += stride;
+ return S_OK;
}
-}
+
+ virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(IDeckLinkVideoInputFrame*, IDeckLinkAudioInputPacket*);
+
+private:
+ atomic_uint m_ref_;
+ demux_t *demux_;
+};
HRESULT DeckLinkCaptureDelegate::VideoInputFrameArrived(IDeckLinkVideoInputFrame* videoFrame, IDeckLinkAudioInputPacket* audioFrame)
{
break;
uint16_t dec[width * 2];
v210_convert(&dec[0], buf, width, 1);
- static const uint16_t vanc_header[3] = { 0, 0x3ff, 0x3ff };
- if (!memcmp(vanc_header, dec, sizeof(vanc_header))) {
- int len = (dec[5] & 0xff) + 6 + 1;
- uint16_t vanc_sum = 0;
- bool parity_ok = true;
- for (int i = 3; i < len - 1; i++) {
- uint16_t v = dec[i];
- int np = v >> 8;
- int p = parity(v & 0xff);
- if ((!!p ^ !!(v & 0x100)) || (np != 1 && np != 2)) {
- parity_ok = false;
- break;
- }
- vanc_sum += v;
- vanc_sum &= 0x1ff;
- dec[i] &= 0xff;
- }
-
- if (!parity_ok)
- continue;
-
- vanc_sum |= ((~vanc_sum & 0x100) << 1);
- if (dec[len - 1] != vanc_sum)
- continue;
-
- if (dec[3] != 0x61 /* DID */ ||
- dec[4] != 0x01 /* SDID = CEA-708 */)
- continue;
-
- /* CDP follows */
- uint16_t *cdp = &dec[6];
- if (cdp[0] != 0x96 || cdp[1] != 0x69)
- continue;
-
- len -= 7; // remove VANC header and checksum
-
- if (cdp[2] != len)
- continue;
-
- uint8_t cdp_sum = 0;
- for (int i = 0; i < len - 1; i++)
- cdp_sum += cdp[i];
- cdp_sum = cdp_sum ? 256 - cdp_sum : 0;
- if (cdp[len - 1] != cdp_sum)
- continue;
-
- uint8_t rate = cdp[3];
- if (!(rate & 0x0f))
- continue;
- rate >>= 4;
- if (rate > 8)
- continue;
-
- if (!(cdp[4] & 0x43)) /* ccdata_present | caption_service_active | reserved */
- continue;
-
- uint16_t hdr = (cdp[5] << 8) | cdp[6];
- if (cdp[7] != 0x72) /* ccdata_id */
- continue;
-
- int cc_count = cdp[8];
- if (!(cc_count & 0xe0))
- continue;
- cc_count &= 0x1f;
-
- /* FIXME: parse additional data (CC language?) */
- if ((len - 13) < cc_count * 3)
- continue;
-
- if (cdp[len - 4] != 0x74) /* footer id */
- continue;
-
- uint16_t ftr = (cdp[len - 3] << 8) | cdp[len - 2];
- if (ftr != hdr)
- continue;
-
- block_t *cc = block_Alloc(cc_count * 3);
-
- for (int i = 0; i < cc_count; i++) {
- cc->p_buffer[3*i+0] = cdp[9 + 3*i+0] & 3;
- cc->p_buffer[3*i+1] = cdp[9 + 3*i+1];
- cc->p_buffer[3*i+2] = cdp[9 + 3*i+2];
- }
-
- cc->i_pts = cc->i_dts = VLC_TS_0 + stream_time;
-
- if (!sys->cc_es) {
- es_format_t fmt;
-
- es_format_Init( &fmt, SPU_ES, VLC_FOURCC('c', 'c', '1' , ' ') );
- fmt.psz_description = strdup("Closed captions 1");
- if (fmt.psz_description) {
- sys->cc_es = es_out_Add(demux_->out, &fmt);
- msg_Dbg(demux_, "Adding Closed captions stream");
- }
+ block_t *cc = vanc_to_cc(demux_, dec, width * 2);
+ if (!cc)
+ continue;
+ cc->i_pts = cc->i_dts = VLC_TS_0 + stream_time;
+
+ if (!sys->cc_es) {
+ es_format_t fmt;
+
+ es_format_Init( &fmt, SPU_ES, VLC_FOURCC('c', 'c', '1' , ' ') );
+ fmt.psz_description = strdup(N_("Closed captions 1"));
+ if (fmt.psz_description) {
+ sys->cc_es = es_out_Add(demux_->out, &fmt);
+ msg_Dbg(demux_, "Adding Closed captions stream");
}
- if (sys->cc_es)
- es_out_Send(demux_->out, sys->cc_es, cc);
- else
- block_Release(cc);
- break; // we found the line with Closed Caption data
}
+ if (sys->cc_es)
+ es_out_Send(demux_->out, sys->cc_es, cc);
+ else
+ block_Release(cc);
+ break; // we found the line with Closed Caption data
}
vanc->Release();
}
return VLC_SUCCESS;
}
-static const char *GetFieldDominance(BMDFieldDominance dom, uint32_t *flags)
-{
- switch(dom)
- {
- case bmdProgressiveFrame:
- return "";
- case bmdProgressiveSegmentedFrame:
- return ", segmented";
- case bmdLowerFieldFirst:
- *flags = BLOCK_FLAG_BOTTOM_FIELD_FIRST;
- return ", interlaced [BFF]";
- case bmdUpperFieldFirst:
- *flags = BLOCK_FLAG_TOP_FIELD_FIRST;
- return ", interlaced [TFF]";
- case bmdUnknownFieldDominance:
- default:
- return ", unknown field dominance";
- }
-}
-
static int Open(vlc_object_t *p_this)
{
demux_t *demux = (demux_t*)p_this;
demux_sys_t *sys;
int ret = VLC_EGENERIC;
int card_index;
- int width = 0, height, fps_num, fps_den;
int physical_channels = 0;
int rate;
- unsigned aspect_num, aspect_den;
+ BMDVideoInputFlags flags = bmdVideoInputFlagDefault;
/* Only when selected */
if (*demux->psz_access == '\0')
goto finish;
}
+ if (sys->card->QueryInterface(IID_IDeckLinkAttributes, (void**)&sys->attributes) != S_OK) {
+ msg_Err(demux, "Failed to get attributes interface");
+ goto finish;
+ }
+
if (GetVideoConn(demux) || GetAudioConn(demux))
goto finish;
- char *mode;
- mode = var_CreateGetNonEmptyString(demux, "decklink-mode");
- if (!mode || strlen(mode) < 3 || strlen(mode) > 4) {
- msg_Err(demux, "Invalid mode: `%s\'", mode ? mode : "");
+ BMDPixelFormat fmt;
+ fmt = sys->tenbits ? bmdFormat10BitYUV : bmdFormat8BitYUV;
+ if (sys->attributes->GetFlag(BMDDeckLinkSupportsInputFormatDetection, &sys->autodetect) != S_OK) {
+ msg_Err(demux, "Failed to query card attribute");
goto finish;
}
IDeckLinkDisplayModeIterator *mode_it;
if (sys->input->GetDisplayModeIterator(&mode_it) != S_OK) {
msg_Err(demux, "Failed to enumerate display modes");
- free(mode);
goto finish;
}
BMDDisplayMode id;
char str[4];
} u;
- memcpy(u.str, mode, 4);
- if (u.str[3] == '\0')
- u.str[3] = ' '; /* 'pal'\0 -> 'pal ' */
- free(mode);
+
+ u.id = 0;
+
+ char *mode;
+ mode = var_CreateGetNonEmptyString(demux, "decklink-mode");
+ if (mode)
+ sys->autodetect = false; // disable autodetection if mode was set
+
+ if (sys->autodetect) {
+ msg_Dbg(demux, "Card supports input format detection");
+ flags |= bmdVideoInputEnableFormatDetection;
+ /* Enable a random format, we will reconfigure on format detection */
+ u.id = htonl(bmdModeHD1080p2997);
+ } else {
+ if (!mode || strlen(mode) < 3 || strlen(mode) > 4) {
+ msg_Err(demux, "Invalid mode: \'%s\'", mode ? mode : "");
+ free(mode);
+ goto finish;
+ }
+
+ msg_Dbg(demux, "Looking for mode \'%s\'", mode);
+ memcpy(u.str, mode, 4);
+ if (u.str[3] == '\0')
+ u.str[3] = ' '; /* 'pal'\0 -> 'pal ' */
+ free(mode);
+ }
+
+ es_format_t video_fmt;
+ video_fmt.video.i_width = 0;
for (IDeckLinkDisplayMode *m;; m->Release()) {
if ((mode_it->Next(&m) != S_OK) || !m)
const char *mode_name;
BMDTimeValue frame_duration, time_scale;
- uint32_t flags = 0;
- const char *field = GetFieldDominance(m->GetFieldDominance(), &flags);
+ uint32_t field_flags;
+ const char *field = GetFieldDominance(m->GetFieldDominance(), &field_flags);
BMDDisplayMode id = ntohl(m->GetDisplayMode());
if (m->GetName(&mode_name) != S_OK)
double(time_scale) / frame_duration, field);
if (u.id == id) {
- width = m->GetWidth();
- height = m->GetHeight();
- fps_num = time_scale;
- fps_den = frame_duration;
- sys->dominance_flags = flags;
+ video_fmt = GetModeSettings(demux, m);
+ msg_Dbg(demux, "Using that mode");
}
}
mode_it->Release();
- if (width == 0) {
+ if (video_fmt.video.i_width == 0) {
msg_Err(demux, "Unknown video mode `%4.4s\' specified.", (char*)&u.id);
goto finish;
}
- BMDPixelFormat fmt; fmt = sys->tenbits ? bmdFormat10BitYUV : bmdFormat8BitYUV;
- if (sys->input->EnableVideoInput(htonl(u.id), fmt, 0) != S_OK) {
+ if (sys->input->EnableVideoInput(htonl(u.id), fmt, flags) != S_OK) {
msg_Err(demux, "Failed to enable video input");
goto finish;
}
goto finish;
}
- /* Declare elementary streams */
- es_format_t video_fmt;
- vlc_fourcc_t chroma; chroma = sys->tenbits ? VLC_CODEC_I422_10L : VLC_CODEC_UYVY;
- es_format_Init(&video_fmt, VIDEO_ES, chroma);
- video_fmt.video.i_width = width;
- video_fmt.video.i_height = height;
- video_fmt.video.i_sar_num = 1;
- video_fmt.video.i_sar_den = 1;
- video_fmt.video.i_frame_rate = fps_num;
- video_fmt.video.i_frame_rate_base = fps_den;
- video_fmt.i_bitrate = video_fmt.video.i_width * video_fmt.video.i_height * video_fmt.video.i_frame_rate * 2 * 8;
-
- if (!var_InheritURational(demux, &aspect_num, &aspect_den, "decklink-aspect-ratio") &&
- aspect_num > 0 && aspect_den > 0) {
- video_fmt.video.i_sar_num = aspect_num * video_fmt.video.i_height;
- video_fmt.video.i_sar_den = aspect_den * video_fmt.video.i_width;
- }
-
msg_Dbg(demux, "added new video es %4.4s %dx%d",
(char*)&video_fmt.i_codec, video_fmt.video.i_width, video_fmt.video.i_height);
sys->video_es = es_out_Add(demux->out, &video_fmt);
demux_t *demux = (demux_t *)p_this;
demux_sys_t *sys = demux->p_sys;
+ if (sys->attributes)
+ sys->attributes->Release();
+
if (sys->config)
sys->config->Release();