[FFmpeg-devel] [PATCH] -- Add input mode autodetect to the decklink module.
Felt, Patrick
Patrick.Felt at echostar.com
Thu May 12 19:16:00 CEST 2016
Previously users had to supply the input format like this 'DeckLink Device at modenum'. This patch allows users to either leave it off completely, or supply 0 or negative number to indicate autodetect is requested.
Autodetect only works the first time so if the mode changes mid stream you'll die.
---
libavdevice/decklink_common.cpp | 110 ++++++++++++++++++++++++++++++++++++++++
libavdevice/decklink_common.h | 5 ++
libavdevice/decklink_common_c.h | 1 +
libavdevice/decklink_dec.cpp | 85 +++++++++++++++++++++++++------
libavdevice/decklink_dec_c.c | 1 +
5 files changed, 187 insertions(+), 15 deletions(-)
diff --git a/libavdevice/decklink_common.cpp b/libavdevice/decklink_common.cpp
index ac7964c..1d51294 100644
--- a/libavdevice/decklink_common.cpp
+++ b/libavdevice/decklink_common.cpp
@@ -98,6 +98,90 @@ HRESULT ff_decklink_get_display_name(IDeckLink *This, const char **displayName)
return hr;
}
+long ff_decklink_mode_to_bm(AVFormatContext *avctx,
+ decklink_direction_t direction,
+ int ffmpeg_mode,
+ IDeckLinkDisplayMode **mode)
+{
+ struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data;
+ struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx;
+ IDeckLinkDisplayModeIterator *itermode;
+ IDeckLinkDisplayMode *internal_mode;
+
+ int result=0;
+ HRESULT res;
+
+ if (direction == DIRECTION_IN) {
+ res = ctx->dli->GetDisplayModeIterator (&itermode);
+ } else {
+ res = ctx->dlo->GetDisplayModeIterator (&itermode);
+ }
+
+ if (res != S_OK) {
+ av_log(avctx, AV_LOG_ERROR, "Could not get the mode iterator\n");
+ return -1;
+ }
+
+ while (itermode->Next(&internal_mode) == S_OK) {
+ if (++result == ffmpeg_mode) {
+ break;
+ }
+
+ internal_mode->Release();
+ }
+
+ itermode->Release();
+ if (internal_mode) {
+ result = internal_mode->GetDisplayMode();
+ if (mode) {
+ *mode = internal_mode;
+ } else {
+ internal_mode->Release();
+ }
+
+ return result;
+ }
+
+ return 0;
+}
+
+int ff_decklink_mode_to_ffmpeg(AVFormatContext *avctx,
+ decklink_direction_t direction,
+ IDeckLinkDisplayMode **mode)
+{
+ struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data;
+ struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx;
+ IDeckLinkDisplayModeIterator *itermode;
+ IDeckLinkDisplayMode *internal_mode;
+ long bdm_mode_number = (*mode)->GetDisplayMode();
+ int result=1, found=0;
+ HRESULT res;
+
+ if (direction == DIRECTION_IN) {
+ res = ctx->dli->GetDisplayModeIterator (&itermode);
+ } else {
+ res = ctx->dlo->GetDisplayModeIterator (&itermode);
+ }
+
+ if (res != S_OK) {
+ av_log(avctx, AV_LOG_ERROR, "Could not get the mode iterator\n");
+ return -1;
+ }
+
+ while (itermode->Next(&internal_mode) == S_OK) {
+ if (internal_mode->GetDisplayMode() == bdm_mode_number) {
+ internal_mode->Release();
+ found = 1;
+ break;
+ }
+ internal_mode->Release();
+ result++;
+ }
+
+ itermode->Release();
+ return (found) ? result : -1;
+}
+
int ff_decklink_set_format(AVFormatContext *avctx,
int width, int height,
int tb_num, int tb_den,
@@ -197,6 +281,29 @@ int ff_decklink_list_devices(AVFormatContext *avctx)
return 0;
}
+int ff_decklink_device_autodetect(AVFormatContext *avctx)
+{
+ HRESULT res;
+ struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data;
+ struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx;
+ IDeckLinkAttributes *attrs = NULL;
+ bool auto_detect;
+
+ res = ctx->dl->QueryInterface(IID_IDeckLinkAttributes, (void**)&attrs);
+ if (res != S_OK) {
+ av_log(avctx, AV_LOG_ERROR, "Could not get decklink attributes\n");
+ return -1;
+ }
+
+ res = attrs->GetFlag(BMDDeckLinkSupportsInputFormatDetection, &auto_detect);
+ if (res != S_OK) {
+ av_log(avctx, AV_LOG_ERROR, "Attribute fetch failed\n");
+ return -1;
+ }
+
+ return (auto_detect) ? 1 : 0;
+}
+
int ff_decklink_list_formats(AVFormatContext *avctx, decklink_direction_t direction)
{
struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data;
@@ -219,6 +326,9 @@ int ff_decklink_list_formats(AVFormatContext *avctx, decklink_direction_t direct
av_log(avctx, AV_LOG_INFO, "Supported formats for '%s':\n",
avctx->filename);
+ if (ff_decklink_device_autodetect(avctx)) {
+ av_log(avctx, AV_LOG_INFO, "\t-1\tAuto detection supported\n");
+ }
while (itermode->Next(&mode) == S_OK) {
BMDTimeValue tb_num, tb_den;
mode->GetFrameRate(&tb_num, &tb_den);
diff --git a/libavdevice/decklink_common.h b/libavdevice/decklink_common.h
index dff4fc1..cbe8de2 100644
--- a/libavdevice/decklink_common.h
+++ b/libavdevice/decklink_common.h
@@ -84,6 +84,8 @@ struct decklink_ctx {
sem_t semaphore;
int channels;
+ int mode_num;
+ int auto_detect;
};
typedef enum { DIRECTION_IN, DIRECTION_OUT} decklink_direction_t;
@@ -105,5 +107,8 @@ int ff_decklink_set_format(AVFormatContext *avctx, int width, int height, int tb
int ff_decklink_set_format(AVFormatContext *avctx, decklink_direction_t direction, int num);
int ff_decklink_list_devices(AVFormatContext *avctx);
int ff_decklink_list_formats(AVFormatContext *avctx, decklink_direction_t direction = DIRECTION_OUT);
+int ff_decklink_device_autodetect(AVFormatContext *avctx);
+int ff_decklink_mode_to_ffmpeg(AVFormatContext *avctx, decklink_direction_t direction, IDeckLinkDisplayMode **mode);
+long ff_decklink_mode_to_bm(AVFormatContext *avctx, decklink_direction_t direction, int ffmpeg_mode, IDeckLinkDisplayMode **mode);
#endif /* AVDEVICE_DECKLINK_COMMON_H */
diff --git a/libavdevice/decklink_common_c.h b/libavdevice/decklink_common_c.h
index 2b5d92f..0d365be 100644
--- a/libavdevice/decklink_common_c.h
+++ b/libavdevice/decklink_common_c.h
@@ -34,6 +34,7 @@ struct decklink_cctx {
double preroll;
int v210;
int audio_channels;
+ int autodetect_delay;
};
#endif /* AVDEVICE_DECKLINK_COMMON_C_H */
diff --git a/libavdevice/decklink_dec.cpp b/libavdevice/decklink_dec.cpp
index 1c305f3..8eb557c 100644
--- a/libavdevice/decklink_dec.cpp
+++ b/libavdevice/decklink_dec.cpp
@@ -1,5 +1,5 @@
/*
- * Blackmagic DeckLink output
+ * Blackmagic DeckLink input
* Copyright (c) 2013-2014 Luca Barbato, Deti Fliegl
*
* This file is part of FFmpeg.
@@ -32,6 +32,7 @@ extern "C" {
#if CONFIG_LIBZVBI
#include <libzvbi.h>
#endif
+#include <unistd.h>
}
#include "decklink_common.h"
@@ -244,6 +245,12 @@ HRESULT decklink_input_callback::VideoInputFrameArrived(
BMDTimeValue frameTime;
BMDTimeValue frameDuration;
+ /* if we don't have video, we are in the autodetect phase. skip everything and let
+ * autodetect do its magic */
+ if (!ctx->video) {
+ return S_OK;
+ }
+
ctx->frameCount++;
// Handle Video Frame
@@ -393,6 +400,14 @@ HRESULT decklink_input_callback::VideoInputFormatChanged(
BMDVideoInputFormatChangedEvents events, IDeckLinkDisplayMode *mode,
BMDDetectedVideoInputFormatFlags)
{
+
+ /* undo all the autodetect stuff so we can move on with life */
+ ctx->dli->PauseStreams();
+ ctx->dli->FlushStreams();
+
+ ctx->mode_num = ff_decklink_mode_to_ffmpeg(avctx, DIRECTION_IN, &mode);
+ ctx->video = 1;
+
return S_OK;
}
@@ -435,14 +450,14 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx)
{
struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data;
struct decklink_ctx *ctx;
- IDeckLinkDisplayModeIterator *itermode;
IDeckLinkIterator *iter;
IDeckLink *dl = NULL;
AVStream *st;
HRESULT result;
char fname[1024];
char *tmp;
- int mode_num = 0;
+ int auto_detect = 0;
+ unsigned int autodetect_delay = cctx->autodetect_delay;
ctx = (struct decklink_ctx *) av_mallocz(sizeof(struct decklink_ctx));
if (!ctx)
@@ -486,7 +501,7 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx)
strcpy (fname, avctx->filename);
tmp=strchr (fname, '@');
if (tmp != NULL) {
- mode_num = atoi (tmp+1);
+ ctx->mode_num = atoi (tmp+1);
*tmp = 0;
}
@@ -510,34 +525,73 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx)
/* Get input device. */
if (ctx->dl->QueryInterface(IID_IDeckLinkInput, (void **) &ctx->dli) != S_OK) {
- av_log(avctx, AV_LOG_ERROR, "Could not open output device from '%s'\n",
+ av_log(avctx, AV_LOG_ERROR, "Could not open input device from '%s'\n",
avctx->filename);
ctx->dl->Release();
return AVERROR(EIO);
}
+ auto_detect = ff_decklink_device_autodetect(avctx);
+
/* List supported formats. */
- if (ctx->list_formats) {
+ if (ctx->list_formats || (ctx->mode_num <= 0 && !auto_detect)) {
ff_decklink_list_formats(avctx, DIRECTION_IN);
ctx->dli->Release();
ctx->dl->Release();
return AVERROR_EXIT;
}
- if (ctx->dli->GetDisplayModeIterator(&itermode) != S_OK) {
- av_log(avctx, AV_LOG_ERROR, "Could not get Display Mode Iterator\n");
- ctx->dl->Release();
- return AVERROR(EIO);
- }
+ if (ctx->mode_num <= 0 && auto_detect) {
+ /* the user is wanting to auto detect the mode. we need to fake out the api and not start ffmpeg fully in this mode!*/
+ ctx->auto_detect = 1;
+ ctx->mode_num = 1; /* it is assumed that there is at least one mode supported on a decklink card. */
+
+ if (ff_decklink_set_format(avctx, DIRECTION_IN, ctx->mode_num) < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Could not set mode %d\n", ctx->mode_num);
+ goto error;
+ }
- if (mode_num > 0) {
- if (ff_decklink_set_format(avctx, DIRECTION_IN, mode_num) < 0) {
- av_log(avctx, AV_LOG_ERROR, "Could not set mode %d for %s\n", mode_num, fname);
+ ctx->bmd_mode = ff_decklink_mode_to_bm(avctx, DIRECTION_IN, ctx->mode_num, NULL);
+ result = ctx->dli->EnableVideoInput(ctx->bmd_mode,
+ cctx->v210 ? bmdFormat10BitYUV : bmdFormat8BitYUV,
+ bmdVideoInputFlagDefault | bmdVideoInputEnableFormatDetection);
+ if (result != S_OK) {
+ av_log(avctx, AV_LOG_ERROR, "Could not enable video in the pre-startup autodectect mode\n");
goto error;
}
+
+ result = ctx->dli->EnableAudioInput(bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger, cctx->audio_channels);
+ if (result != S_OK) {
+ av_log(avctx, AV_LOG_ERROR, "Could not enable audio in the pre-startup autodetect mode\n");
+ goto error;
+ }
+
+ result = decklink_start_input(avctx);
+ if (result != S_OK) {
+ av_log(avctx, AV_LOG_ERROR, "Could not start input in the pre-startup autodetect mode\n");
+ goto error;
+ }
+
+ /* we need to give the auto detect code some time to figure out what mode we are really in */
+ autodetect_delay = cctx->autodetect_delay;
+ while (!ctx->video) {
+ if (autodetect_delay--) {
+ /* this could indicate we are in the right mode. let's assume so */
+ continue;
+ }
+ sleep(1);
+ }
}
- itermode->Release();
+ /* regardless as to if we did autodetect or not, we should now be in a position to
+ * continue on as before with all the right setup to ensure we get the right mode */
+ ctx->video = 1;
+ if (ctx->mode_num > 0) {
+ if (ff_decklink_set_format(avctx, DIRECTION_IN, ctx->mode_num) < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Could not set mode %d for %s\n", ctx->mode_num, fname);
+ goto error;
+ }
+ }
/* Setup streams. */
st = avformat_new_stream(avctx, NULL);
@@ -618,6 +672,7 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx)
return 0;
+
error:
ctx->dli->Release();
diff --git a/libavdevice/decklink_dec_c.c b/libavdevice/decklink_dec_c.c
index 40c21a7..3f83f8f 100644
--- a/libavdevice/decklink_dec_c.c
+++ b/libavdevice/decklink_dec_c.c
@@ -36,6 +36,7 @@ static const AVOption options[] = {
{ "standard", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0x7fff9fffeLL}, 0, 0, DEC, "teletext_lines"},
{ "all", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0x7ffffffffLL}, 0, 0, DEC, "teletext_lines"},
{ "channels", "number of audio channels", OFFSET(audio_channels), AV_OPT_TYPE_INT , { .i64 = 2 }, 2, 16, DEC },
+ { "autodetect_delay", "number of seconds to wait for autodetect to complete", OFFSET(autodetect_delay), AV_OPT_TYPE_INT , { .i64 = 5 }, 1, 30, DEC },
{ NULL },
};
--
2.7.4
More information about the ffmpeg-devel
mailing list