[FFmpeg-devel] [PATCH] Updated -- Add input mode autodetect to the decklink module.
Felt, Patrick
Patrick.Felt at echostar.com
Thu May 12 23:21:26 CEST 2016
-- Add input mode autodetect to the decklink module. 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..552edc2 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.
@@ -244,6 +244,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 +399,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 +449,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 +500,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 +524,74 @@ 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 */
+ break;
+ }
+ /* sleep for 1 second */
+ av_usleep(100000);
+ }
}
- 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
On 5/12/16, 3:10 PM, "ffmpeg-devel on behalf of Felt, Patrick" <ffmpeg-devel-bounces at ffmpeg.org on behalf of Patrick.Felt at echostar.com> wrote:
>Ah… I see a bug. I should either be setting ctx->video in the if block or I should be breaking instead of continuing. Good catch!
>
>
>On 5/12/16, 1:32 PM, "ffmpeg-devel on behalf of Matthias Hunstock" <ffmpeg-devel-bounces at ffmpeg.org on behalf of atze at fem.tu-ilmenau.de> wrote:
>
>>Am 12.05.2016 um 19:16 schrieb Felt, Patrick:
>>> + while (!ctx->video) {
>>> + if (autodetect_delay--) {
>>> + /* this could indicate we are in the right mode. let's assume so */
>>> + continue;
>>> + }
>>> + sleep(1);
>>> + }
>>
>>I don't get it. How does this loop sleep for autodetect_delay seconds? I
>>read it like "spin-loop for autodetect_delay times and then probe
>>ctx->video each second, possibly forever".
>>
>>
>>
>>_______________________________________________
>>ffmpeg-devel mailing list
>>ffmpeg-devel at ffmpeg.org
>>http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
>_______________________________________________
>ffmpeg-devel mailing list
>ffmpeg-devel at ffmpeg.org
>http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
More information about the ffmpeg-devel
mailing list