#ifdef _WIN32
#include <DeckLinkAPI_i.c>
#else
+/* The file provided by the SDK is known to be missing prototypes, which doesn't
+ cause issues with GCC since the warning doesn't apply to C++ files. However
+ Clang does complain (and warnings are treated as errors), so suppress the
+ warning just for this one file */
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmissing-prototypes"
+#endif
#include <DeckLinkAPIDispatch.cpp>
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
#endif
extern "C" {
#include "decklink_common.h"
-#ifdef _WIN32
-IDeckLinkIterator *CreateDeckLinkIteratorInstance(void)
+static IDeckLinkIterator *decklink_create_iterator(AVFormatContext *avctx)
{
IDeckLinkIterator *iter;
+#ifdef _WIN32
if (CoInitialize(NULL) < 0) {
- av_log(NULL, AV_LOG_ERROR, "COM initialization failed.\n");
+ av_log(avctx, AV_LOG_ERROR, "COM initialization failed.\n");
return NULL;
}
if (CoCreateInstance(CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL,
IID_IDeckLinkIterator, (void**) &iter) != S_OK) {
- av_log(NULL, AV_LOG_ERROR, "DeckLink drivers not installed.\n");
- return NULL;
+ iter = NULL;
}
-
- return iter;
-}
+#else
+ iter = CreateDeckLinkIteratorInstance();
#endif
-
+ if (!iter) {
+ av_log(avctx, AV_LOG_ERROR, "Could not create DeckLink iterator. "
+ "Make sure you have DeckLink drivers " BLACKMAGIC_DECKLINK_API_VERSION_STRING " or newer installed.\n");
+ } else {
+ IDeckLinkAPIInformation *api;
+ int64_t version;
#ifdef _WIN32
-static char *dup_wchar_to_utf8(wchar_t *w)
-{
- char *s = NULL;
- int l = WideCharToMultiByte(CP_UTF8, 0, w, -1, 0, 0, 0, 0);
- s = (char *) av_malloc(l);
- if (s)
- WideCharToMultiByte(CP_UTF8, 0, w, -1, s, l, 0, 0);
- return s;
-}
-#define DECKLINK_STR OLECHAR *
-#define DECKLINK_STRDUP dup_wchar_to_utf8
-#define DECKLINK_FREE(s) SysFreeString(s)
-#define DECKLINK_BOOL BOOL
-#elif defined(__APPLE__)
-static char *dup_cfstring_to_utf8(CFStringRef w)
-{
- char s[256];
- CFStringGetCString(w, s, 255, kCFStringEncodingUTF8);
- return av_strdup(s);
-}
-#define DECKLINK_STR const __CFString *
-#define DECKLINK_STRDUP dup_cfstring_to_utf8
-#define DECKLINK_FREE(s) CFRelease(s)
-#define DECKLINK_BOOL bool
+ if (CoCreateInstance(CLSID_CDeckLinkAPIInformation, NULL, CLSCTX_ALL,
+ IID_IDeckLinkAPIInformation, (void**) &api) != S_OK) {
+ api = NULL;
+ }
#else
-#define DECKLINK_STR const char *
-#define DECKLINK_STRDUP av_strdup
-/* free() is needed for a string returned by the DeckLink SDL. */
-#define DECKLINK_FREE(s) free((void *) s)
-#define DECKLINK_BOOL bool
+ api = CreateDeckLinkAPIInformationInstance();
#endif
+ if (api && api->GetInt(BMDDeckLinkAPIVersion, &version) == S_OK) {
+ if (version < BLACKMAGIC_DECKLINK_API_VERSION)
+ av_log(avctx, AV_LOG_WARNING, "Installed DeckLink drivers are too old and may be incompatible with the SDK this module was built against. "
+ "Make sure you have DeckLink drivers " BLACKMAGIC_DECKLINK_API_VERSION_STRING " or newer installed.\n");
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "Failed to check installed DeckLink API version.\n");
+ }
+ if (api)
+ api->Release();
+ }
-HRESULT ff_decklink_get_display_name(IDeckLink *This, const char **displayName)
+ return iter;
+}
+
+static int decklink_get_attr_string(IDeckLink *dl, BMDDeckLinkAttributeID cfg_id, const char **s)
{
- DECKLINK_STR tmpDisplayName;
- HRESULT hr = This->GetDisplayName(&tmpDisplayName);
- if (hr != S_OK)
- return hr;
- *displayName = DECKLINK_STRDUP(tmpDisplayName);
- DECKLINK_FREE(tmpDisplayName);
- return hr;
+ DECKLINK_STR tmp;
+ HRESULT hr;
+ IDeckLinkProfileAttributes *attr;
+ *s = NULL;
+ if (dl->QueryInterface(IID_IDeckLinkProfileAttributes, (void **)&attr) != S_OK)
+ return AVERROR_EXTERNAL;
+ hr = attr->GetString(cfg_id, &tmp);
+ attr->Release();
+ if (hr == S_OK) {
+ *s = DECKLINK_STRDUP(tmp);
+ DECKLINK_FREE(tmp);
+ if (!*s)
+ return AVERROR(ENOMEM);
+ } else if (hr == E_FAIL) {
+ return AVERROR_EXTERNAL;
+ }
+ return 0;
}
static int decklink_select_input(AVFormatContext *avctx, BMDDeckLinkConfigurationID cfg_id)
return false;
}
-int ff_decklink_set_format(AVFormatContext *avctx,
- int width, int height,
- int tb_num, int tb_den,
- enum AVFieldOrder field_order,
- decklink_direction_t direction, int num)
-{
+int ff_decklink_set_configs(AVFormatContext *avctx,
+ decklink_direction_t direction) {
struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data;
struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx;
- BMDDisplayModeSupport support;
- IDeckLinkDisplayModeIterator *itermode;
- IDeckLinkDisplayMode *mode;
- int i = 1;
HRESULT res;
- av_log(avctx, AV_LOG_DEBUG, "Trying to find mode for frame size %dx%d, frame timing %d/%d, field order %d, direction %d, mode number %d, format code %s\n",
- width, height, tb_num, tb_den, field_order, direction, num, (cctx->format_code) ? cctx->format_code : "(unset)");
-
if (ctx->duplex_mode) {
DECKLINK_BOOL duplex_supported = false;
+#if BLACKMAGIC_DECKLINK_API_VERSION >= 0x0b000000
+ IDeckLinkProfileManager *manager = NULL;
+ if (ctx->dl->QueryInterface(IID_IDeckLinkProfileManager, (void **)&manager) == S_OK)
+ duplex_supported = true;
+#else
if (ctx->attr->GetFlag(BMDDeckLinkSupportsDuplexModeConfiguration, &duplex_supported) != S_OK)
duplex_supported = false;
+#endif
if (duplex_supported) {
+#if BLACKMAGIC_DECKLINK_API_VERSION >= 0x0b000000
+ IDeckLinkProfile *profile = NULL;
+ BMDProfileID bmd_profile_id = ctx->duplex_mode == 2 ? bmdProfileOneSubDeviceFullDuplex : bmdProfileTwoSubDevicesHalfDuplex;
+ res = manager->GetProfile(bmd_profile_id, &profile);
+ if (res == S_OK) {
+ res = profile->SetActive();
+ profile->Release();
+ }
+ manager->Release();
+#else
res = ctx->cfg->SetInt(bmdDeckLinkConfigDuplexMode, ctx->duplex_mode == 2 ? bmdDuplexModeFull : bmdDuplexModeHalf);
+#endif
if (res != S_OK)
av_log(avctx, AV_LOG_WARNING, "Setting duplex mode failed.\n");
else
av_log(avctx, AV_LOG_WARNING, "Unable to set duplex mode, because it is not supported.\n");
}
}
-
if (direction == DIRECTION_IN) {
int ret;
ret = decklink_select_input(avctx, bmdDeckLinkConfigAudioInputConnection);
ret = decklink_select_input(avctx, bmdDeckLinkConfigVideoInputConnection);
if (ret < 0)
return ret;
+ }
+ if (direction == DIRECTION_OUT && cctx->timing_offset != INT_MIN) {
+ res = ctx->cfg->SetInt(bmdDeckLinkConfigReferenceInputTimingOffset, cctx->timing_offset);
+ if (res != S_OK)
+ av_log(avctx, AV_LOG_WARNING, "Setting timing offset failed.\n");
+ }
+ return 0;
+}
+
+int ff_decklink_set_format(AVFormatContext *avctx,
+ int width, int height,
+ int tb_num, int tb_den,
+ enum AVFieldOrder field_order,
+ decklink_direction_t direction)
+{
+ struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data;
+ struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx;
+#if BLACKMAGIC_DECKLINK_API_VERSION >= 0x0b000000
+ DECKLINK_BOOL support;
+#else
+ BMDDisplayModeSupport support;
+#endif
+ IDeckLinkDisplayModeIterator *itermode;
+ IDeckLinkDisplayMode *mode;
+ int i = 1;
+ HRESULT res;
+
+ av_log(avctx, AV_LOG_DEBUG, "Trying to find mode for frame size %dx%d, frame timing %d/%d, field order %d, direction %d, format code %s\n",
+ width, height, tb_num, tb_den, field_order, direction, cctx->format_code ? cctx->format_code : "(unset)");
+
+ if (direction == DIRECTION_IN) {
res = ctx->dli->GetDisplayModeIterator (&itermode);
} else {
res = ctx->dlo->GetDisplayModeIterator (&itermode);
bmd_height == height &&
!av_cmp_q(mode_tb, target_tb) &&
field_order_eq(field_order, bmd_field_dominance))
- || i == num
|| target_mode == bmd_mode) {
ctx->bmd_mode = bmd_mode;
ctx->bmd_width = bmd_width;
if (ctx->bmd_mode == bmdModeUnknown)
return -1;
+
+#if BLACKMAGIC_DECKLINK_API_VERSION >= 0x0b050000
if (direction == DIRECTION_IN) {
- if (ctx->dli->DoesSupportVideoMode(ctx->bmd_mode, (BMDPixelFormat) cctx->raw_format,
- bmdVideoOutputFlagDefault,
- &support, NULL) != S_OK)
+ BMDDisplayMode actualMode = ctx->bmd_mode;
+ if (ctx->dli->DoesSupportVideoMode(ctx->video_input, ctx->bmd_mode, ctx->raw_format,
+ bmdNoVideoInputConversion, bmdSupportedVideoModeDefault,
+ &actualMode, &support) != S_OK || !support || ctx->bmd_mode != actualMode)
return -1;
} else {
- if (ctx->dlo->DoesSupportVideoMode(ctx->bmd_mode, bmdFormat8BitYUV,
+ BMDDisplayMode actualMode = ctx->bmd_mode;
+ if (ctx->dlo->DoesSupportVideoMode(bmdVideoConnectionUnspecified, ctx->bmd_mode, ctx->raw_format,
+ bmdNoVideoOutputConversion, bmdSupportedVideoModeDefault,
+ &actualMode, &support) != S_OK || !support || ctx->bmd_mode != actualMode)
+ return -1;
+ }
+ return 0;
+#elif BLACKMAGIC_DECKLINK_API_VERSION >= 0x0b000000
+ if (direction == DIRECTION_IN) {
+ if (ctx->dli->DoesSupportVideoMode(ctx->video_input, ctx->bmd_mode, ctx->raw_format,
+ bmdSupportedVideoModeDefault,
+ &support) != S_OK)
+ return -1;
+ } else {
+ BMDDisplayMode actualMode = ctx->bmd_mode;
+ if (ctx->dlo->DoesSupportVideoMode(bmdVideoConnectionUnspecified, ctx->bmd_mode, ctx->raw_format,
+ bmdSupportedVideoModeDefault,
+ &actualMode, &support) != S_OK || !support || ctx->bmd_mode != actualMode) {
+ return -1;
+ }
+
+ }
+ if (support)
+ return 0;
+#else
+ if (direction == DIRECTION_IN) {
+ if (ctx->dli->DoesSupportVideoMode(ctx->bmd_mode, ctx->raw_format,
bmdVideoOutputFlagDefault,
&support, NULL) != S_OK)
- return -1;
+ return -1;
+ } else {
+ if (!ctx->supports_vanc || ctx->dlo->DoesSupportVideoMode(ctx->bmd_mode, ctx->raw_format,
+ bmdVideoOutputVANC,
+ &support, NULL) != S_OK || support != bmdDisplayModeSupported) {
+ /* Try without VANC enabled */
+ if (ctx->dlo->DoesSupportVideoMode(ctx->bmd_mode, ctx->raw_format,
+ bmdVideoOutputFlagDefault,
+ &support, NULL) != S_OK) {
+ return -1;
+ }
+ ctx->supports_vanc = 0;
+ }
+
}
if (support == bmdDisplayModeSupported)
return 0;
+#endif
return -1;
}
-int ff_decklink_set_format(AVFormatContext *avctx, decklink_direction_t direction, int num) {
- return ff_decklink_set_format(avctx, 0, 0, 0, 0, AV_FIELD_UNKNOWN, direction, num);
+int ff_decklink_set_format(AVFormatContext *avctx, decklink_direction_t direction) {
+ return ff_decklink_set_format(avctx, 0, 0, 0, 0, AV_FIELD_UNKNOWN, direction);
}
int ff_decklink_list_devices(AVFormatContext *avctx,
int show_inputs, int show_outputs)
{
IDeckLink *dl = NULL;
- IDeckLinkIterator *iter = CreateDeckLinkIteratorInstance();
+ IDeckLinkIterator *iter = decklink_create_iterator(avctx);
int ret = 0;
- if (!iter) {
- av_log(avctx, AV_LOG_ERROR, "Could not create DeckLink iterator\n");
+ if (!iter)
return AVERROR(EIO);
- }
while (ret == 0 && iter->Next(&dl) == S_OK) {
IDeckLinkOutput *output_config;
IDeckLinkInput *input_config;
- const char *displayName;
+ const char *display_name = NULL;
+ const char *unique_name = NULL;
AVDeviceInfo *new_device = NULL;
int add = 0;
- ff_decklink_get_display_name(dl, &displayName);
+ ret = decklink_get_attr_string(dl, BMDDeckLinkDisplayName, &display_name);
+ if (ret < 0)
+ goto next;
+ ret = decklink_get_attr_string(dl, BMDDeckLinkDeviceHandle, &unique_name);
+ if (ret < 0)
+ goto next;
if (show_outputs) {
if (dl->QueryInterface(IID_IDeckLinkOutput, (void **)&output_config) == S_OK) {
ret = AVERROR(ENOMEM);
goto next;
}
- new_device->device_name = av_strdup(displayName);
- if (!new_device->device_name) {
- ret = AVERROR(ENOMEM);
- goto next;
- }
- new_device->device_description = av_strdup(displayName);
- if (!new_device->device_description) {
- av_freep(&new_device->device_name);
- ret = AVERROR(ENOMEM);
- goto next;
- }
+ new_device->device_name = av_strdup(unique_name ? unique_name : display_name);
+ new_device->device_description = av_strdup(display_name);
- if ((ret = av_dynarray_add_nofree(&device_list->devices,
- &device_list->nb_devices, new_device)) < 0) {
+ if (!new_device->device_name ||
+ !new_device->device_description ||
+ av_dynarray_add_nofree(&device_list->devices, &device_list->nb_devices, new_device) < 0) {
+ ret = AVERROR(ENOMEM);
av_freep(&new_device->device_name);
av_freep(&new_device->device_description);
av_freep(&new_device);
}
next:
- av_freep(&displayName);
+ av_freep(&display_name);
+ av_freep(&unique_name);
dl->Release();
}
iter->Release();
av_log(avctx, AV_LOG_INFO, "Blackmagic DeckLink %s devices:\n",
show_inputs ? "input" : "output");
for (int i = 0; i < device_list->nb_devices; i++) {
- av_log(avctx, AV_LOG_INFO, "\t'%s'\n", device_list->devices[i]->device_name);
+ av_log(avctx, AV_LOG_INFO, "\t'%s'\n", device_list->devices[i]->device_description);
}
}
avdevice_free_list_devices(&device_list);
}
av_log(avctx, AV_LOG_INFO, "Supported formats for '%s':\n\tformat_code\tdescription",
- avctx->filename);
+ avctx->url);
while (itermode->Next(&mode) == S_OK) {
BMDTimeValue tb_num, tb_den;
mode->GetFrameRate(&tb_num, &tb_den);
struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data;
struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx;
IDeckLink *dl = NULL;
- IDeckLinkIterator *iter = CreateDeckLinkIteratorInstance();
- if (!iter) {
- av_log(avctx, AV_LOG_ERROR, "Could not create DeckLink iterator\n");
+ IDeckLinkIterator *iter = decklink_create_iterator(avctx);
+ if (!iter)
return AVERROR_EXTERNAL;
- }
while (iter->Next(&dl) == S_OK) {
- const char *displayName;
- ff_decklink_get_display_name(dl, &displayName);
- if (!strcmp(name, displayName)) {
- av_free((void *)displayName);
+ const char *display_name = NULL;
+ const char *unique_name = NULL;
+ decklink_get_attr_string(dl, BMDDeckLinkDisplayName, &display_name);
+ decklink_get_attr_string(dl, BMDDeckLinkDeviceHandle, &unique_name);
+ if (display_name && !strcmp(name, display_name) || unique_name && !strcmp(name, unique_name)) {
+ av_free((void *)unique_name);
+ av_free((void *)display_name);
ctx->dl = dl;
break;
}
- av_free((void *)displayName);
+ av_free((void *)display_name);
+ av_free((void *)unique_name);
dl->Release();
}
iter->Release();
return AVERROR_EXTERNAL;
}
- if (ctx->dl->QueryInterface(IID_IDeckLinkAttributes, (void **)&ctx->attr) != S_OK) {
+ if (ctx->dl->QueryInterface(IID_IDeckLinkProfileAttributes, (void **)&ctx->attr) != S_OK) {
av_log(avctx, AV_LOG_ERROR, "Could not get attributes interface for '%s'\n", name);
ff_decklink_cleanup(avctx);
return AVERROR_EXTERNAL;