-- Add input mode autodetect to the decklink module. Previously users had to supply the input format like this 'DeckLink Device@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-boun...@ffmpeg.org on behalf of patrick.f...@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-boun...@ffmpeg.org on behalf of a...@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@ffmpeg.org >>http://ffmpeg.org/mailman/listinfo/ffmpeg-devel > >_______________________________________________ >ffmpeg-devel mailing list >ffmpeg-devel@ffmpeg.org >http://ffmpeg.org/mailman/listinfo/ffmpeg-devel _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel