X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faccess%2Fdecklink.cpp;h=25e7800ac85466af0bc5d134b30268bb0f2f773c;hb=a303b7826216f713ec49cf0286f09052fda4d7f3;hp=149920d7510815d9af247132aa07841d8f999bf7;hpb=01b0eaf884a341c34f1c8c171f3891dc7f93a6ba;p=vlc diff --git a/modules/access/decklink.cpp b/modules/access/decklink.cpp index 149920d751..25e7800ac8 100644 --- a/modules/access/decklink.cpp +++ b/modules/access/decklink.cpp @@ -2,10 +2,10 @@ * decklink.cpp: BlackMagic DeckLink SDI input module ***************************************************************************** * Copyright (C) 2010 Steinar H. Gunderson - * Copyright (C) 2009 Michael Niedermayer - * Copyright (c) 2009 Baptiste Coudurier + * Copyright (C) 2012-2014 Rafaël Carré * * Authors: Steinar H. Gunderson + Rafaël Carré * * 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 @@ -38,6 +38,8 @@ #include #include +#include "sdi.h" + static int Open (vlc_object_t *); static void Close(vlc_object_t *); @@ -46,7 +48,7 @@ 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 " \ @@ -101,7 +103,7 @@ vlc_module_begin () 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) @@ -115,7 +117,7 @@ vlc_module_begin () 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) @@ -135,6 +137,9 @@ struct demux_sys_t /* 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; @@ -149,95 +154,121 @@ struct demux_sys_t 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) { @@ -276,108 +307,26 @@ HRESULT DeckLinkCaptureDelegate::VideoInputFrameArrived(IDeckLinkVideoInputFrame 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(); } @@ -491,36 +440,15 @@ static int GetVideoConn(demux_t *demux) 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') @@ -578,13 +506,18 @@ static int Open(vlc_object_t *p_this) 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; } @@ -592,7 +525,6 @@ static int Open(vlc_object_t *p_this) IDeckLinkDisplayModeIterator *mode_it; if (sys->input->GetDisplayModeIterator(&mode_it) != S_OK) { msg_Err(demux, "Failed to enumerate display modes"); - free(mode); goto finish; } @@ -600,10 +532,35 @@ static int Open(vlc_object_t *p_this) 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) @@ -611,8 +568,8 @@ static int Open(vlc_object_t *p_this) 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) @@ -628,23 +585,19 @@ static int Open(vlc_object_t *p_this) 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; } @@ -682,24 +635,6 @@ static int Open(vlc_object_t *p_this) 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); @@ -734,6 +669,9 @@ static void Close(vlc_object_t *p_this) 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();