avdevice/decklink_dec: autodetect the video input format
[ffmpeg.git] / libavdevice / decklink_dec.cpp
index 03a049a..94dae26 100644 (file)
@@ -36,6 +36,7 @@ extern "C" {
 #include "libavutil/avutil.h"
 #include "libavutil/common.h"
 #include "libavutil/imgutils.h"
+#include "libavutil/intreadwrite.h"
 #include "libavutil/time.h"
 #include "libavutil/mathematics.h"
 #include "libavutil/reverse.h"
@@ -49,6 +50,7 @@ extern "C" {
 #include "decklink_dec.h"
 
 #define MAX_WIDTH_VANC 1920
+const BMDDisplayMode AUTODETECT_DEFAULT_MODE = bmdModeNTSC;
 
 typedef struct VANCLineNumber {
     BMDDisplayMode mode;
@@ -634,6 +636,15 @@ HRESULT decklink_input_callback::VideoInputFrameArrived(
     BMDTimeValue frameDuration;
     int64_t wallclock = 0;
 
+    if (ctx->autodetect) {
+        if (videoFrame && !(videoFrame->GetFlags() & bmdFrameHasNoInputSource) &&
+            ctx->bmd_mode == bmdModeUnknown)
+        {
+            ctx->bmd_mode = AUTODETECT_DEFAULT_MODE;
+        }
+        return S_OK;
+    }
+
     ctx->frameCount++;
     if (ctx->audio_pts_source == PTS_SRC_WALLCLOCK || ctx->video_pts_source == PTS_SRC_WALLCLOCK)
         wallclock = av_gettime_relative();
@@ -794,17 +805,56 @@ HRESULT decklink_input_callback::VideoInputFormatChanged(
     BMDVideoInputFormatChangedEvents events, IDeckLinkDisplayMode *mode,
     BMDDetectedVideoInputFormatFlags)
 {
+    ctx->bmd_mode = mode->GetDisplayMode();
     return S_OK;
 }
 
-static HRESULT decklink_start_input(AVFormatContext *avctx)
-{
-    struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data;
+static int decklink_autodetect(struct decklink_cctx *cctx) {
     struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx;
+    DECKLINK_BOOL autodetect_supported = false;
+    int i;
+
+    if (ctx->attr->GetFlag(BMDDeckLinkSupportsInputFormatDetection, &autodetect_supported) != S_OK)
+        return -1;
+    if (autodetect_supported == false)
+        return -1;
+
+    ctx->autodetect = 1;
+    ctx->bmd_mode  = bmdModeUnknown;
+    if (ctx->dli->EnableVideoInput(AUTODETECT_DEFAULT_MODE,
+                                   bmdFormat8BitYUV,
+                                   bmdVideoInputEnableFormatDetection) != S_OK) {
+        return -1;
+    }
+
+    if (ctx->dli->StartStreams() != S_OK) {
+        return -1;
+    }
+
+    // 1 second timeout
+    for (i = 0; i < 10; i++) {
+        av_usleep(100000);
+        /* Sometimes VideoInputFrameArrived is called without the
+         * bmdFrameHasNoInputSource flag before VideoInputFormatChanged.
+         * So don't break for bmd_mode == AUTODETECT_DEFAULT_MODE. */
+        if (ctx->bmd_mode != bmdModeUnknown &&
+            ctx->bmd_mode != AUTODETECT_DEFAULT_MODE)
+            break;
+    }
+
+    ctx->dli->PauseStreams();
+    ctx->dli->FlushStreams();
+    ctx->autodetect = 0;
+    if (ctx->bmd_mode != bmdModeUnknown) {
+        cctx->format_code = (char *)av_mallocz(5);
+        if (!cctx->format_code)
+            return -1;
+        AV_WB32(cctx->format_code, ctx->bmd_mode);
+        return 0;
+    } else {
+        return -1;
+    }
 
-    ctx->input_callback = new decklink_input_callback(avctx);
-    ctx->dli->SetCallback(ctx->input_callback);
-    return ctx->dli->StartStreams();
 }
 
 extern "C" {
@@ -922,13 +972,22 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx)
         goto error;
     }
 
-    if (mode_num > 0 || cctx->format_code) {
-        if (ff_decklink_set_format(avctx, DIRECTION_IN, mode_num) < 0) {
-            av_log(avctx, AV_LOG_ERROR, "Could not set mode number %d or format code %s for %s\n",
-                mode_num, (cctx->format_code) ? cctx->format_code : "(unset)", fname);
+    ctx->input_callback = new decklink_input_callback(avctx);
+    ctx->dli->SetCallback(ctx->input_callback);
+
+    if (mode_num == 0 && !cctx->format_code) {
+        if (decklink_autodetect(cctx) < 0) {
+            av_log(avctx, AV_LOG_ERROR, "Cannot Autodetect input stream or No signal\n");
             ret = AVERROR(EIO);
             goto error;
         }
+        av_log(avctx, AV_LOG_INFO, "Autodetected the input mode\n");
+    }
+    if (ff_decklink_set_format(avctx, DIRECTION_IN, mode_num) < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Could not set mode number %d or format code %s for %s\n",
+            mode_num, (cctx->format_code) ? cctx->format_code : "(unset)", fname);
+        ret = AVERROR(EIO);
+        goto error;
     }
 
 #if !CONFIG_LIBZVBI
@@ -1058,7 +1117,7 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx)
 
     avpacket_queue_init (avctx, &ctx->queue);
 
-    if (decklink_start_input (avctx) != S_OK) {
+    if (ctx->dli->StartStreams() != S_OK) {
         av_log(avctx, AV_LOG_ERROR, "Cannot start input stream\n");
         ret = AVERROR(EIO);
         goto error;