[FFmpeg-devel] [PATCH] Add input support for DeckLink devices.

Deti Fliegl deti at fliegl.de
Tue Sep 2 11:26:13 CEST 2014


On 30.08.14 01:22, Michael Niedermayer wrote:
> moving code from (like from decklink_enc.cpp to decklink_common.cpp)
> should be in a seperate patch
Done. Both patches are attached to this mail.

> If you want to maintain this code, please add yourself to the
> MAINTAINERs file
Added.

> non static functions should have a ff_ prefix (or av if they are
> public but that isnt)
Done, except for CreateDeckLinkIteratorInstance which is a function that 
is part of the DeckLink Mac OS X & Linux API and which is not available 
under Windows. So it has to be recoded to enable code sharing between 
Mac OS X, Linux and Windows.

> also i dont really know windows & decklink stuff, so iam not the
> ideal one to review ...
Well basically it all should be the same. It compiles and works under 
Windows too, but most people would use the direct show input support of 
ffmpeg for capturing from a DeckLink device.

Deti
-------------- next part --------------
Signed-off-by: Deti fliegl <deti at fliegl.de>
---
 MAINTAINERS                  |   1 +
 configure                    |   4 +-
 doc/indevs.texi              |  54 +++++
 libavdevice/Makefile         |   3 +-
 libavdevice/alldevices.c     |   2 +-
 libavdevice/decklink_dec.cpp | 531 +++++++++++++++++++++++++++++++++++++++++++
 libavdevice/decklink_dec.h   |  32 +++
 libavdevice/decklink_dec_c.c |  54 +++++
 8 files changed, 678 insertions(+), 3 deletions(-)
 create mode 100644 libavdevice/decklink_dec.cpp
 create mode 100644 libavdevice/decklink_dec.h
 create mode 100644 libavdevice/decklink_dec_c.c

diff --git a/MAINTAINERS b/MAINTAINERS
index b80fe7a..8674246 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -309,6 +309,7 @@ libavdevice
 
 
   avfoundation.m                        Thilo Borgmann
+  decklink*                             Deti Fliegl
   dshow.c                               Roger Pack (CC rogerdpack at gmail.com)
   fbdev_enc.c                           Lukasz Marek
   gdigrab.c                             Roger Pack (CC rogerdpack at gmail.com)
diff --git a/configure b/configure
index 8f0b915..39c4ef7 100755
--- a/configure
+++ b/configure
@@ -255,7 +255,7 @@ External library support:
   --enable-libzmq          enable message passing via libzmq [no]
   --enable-libzvbi         enable teletext support via libzvbi [no]
   --disable-lzma           disable lzma [autodetect]
-  --enable-decklink        enable Blackmagick DeckLink output [no]
+  --enable-decklink        enable Blackmagick DeckLink I/O support [no]
   --enable-openal          enable OpenAL 1.1 capture support [no]
   --enable-opencl          enable OpenCL code
   --enable-opengl          enable OpenGL rendering [no]
@@ -2447,6 +2447,8 @@ bktr_indev_deps_any="dev_bktr_ioctl_bt848_h machine_ioctl_bt848_h dev_video_bktr
 caca_outdev_deps="libcaca"
 decklink_outdev_deps="decklink pthreads"
 decklink_outdev_extralibs="-lstdc++"
+decklink_indev_deps="decklink pthreads"
+decklink_indev_extralibs="-lstdc++"
 dshow_indev_deps="IBaseFilter"
 dshow_indev_extralibs="-lpsapi -lole32 -lstrmiids -luuid"
 dv1394_indev_deps="dv1394"
diff --git a/doc/indevs.texi b/doc/indevs.texi
index ce409b9..99e1112 100644
--- a/doc/indevs.texi
+++ b/doc/indevs.texi
@@ -922,4 +922,58 @@ Use the MIT-SHM extension for shared memory. Default value is @code{1}.
 It may be necessary to disable it for remote displays.
 @end table
 
+ at section decklink
+
+The decklink input device provides capture capabilities for Blackmagic
+DeckLink devices.
+
+To enable this input device, you need the Blackmagic DeckLink SDK and you
+need to configure with the appropriate @code{--extra-cflags}
+and @code{--extra-ldflags}.
+On Windows, you need to run the IDL files through @command{widl}.
+
+DeckLink is very picky about the formats it supports. Pixel format is always
+uyvy422, framerate and video size must be determined for your device with
+ at command{-list_formats 1}. Audio sample rate is always 48 kHz and the number
+of channels currently is limited to 2 (stereo).
+
+ at subsection Options
+
+ at table @option
+
+ at item list_devices
+If set to @option{true}, print a list of devices and exit.
+Defaults to @option{false}.
+
+ at item list_formats
+If set to @option{true}, print a list of supported formats and exit.
+Defaults to @option{false}.
+
+ at end table
+
+ at subsection Examples
+
+ at itemize
+
+ at item
+List input devices:
+ at example
+ffmpeg -f decklink -list_devices 1 -i dummy
+ at end example
+
+ at item
+List supported formats:
+ at example
+ffmpeg -f decklink -list_formats 1 -i 'Intensity Pro'
+ at end example
+
+ at item
+Capture video clip at 1080i50 (format 11):
+ at example
+ffmpeg -f decklink -i 'Intensity Pro@@11' -acodec copy -vcodec copy output.avi
+ at end example
+
+ at end itemize
+
+
 @c man end INPUT DEVICES
diff --git a/libavdevice/Makefile b/libavdevice/Makefile
index f90c0f7..8cf98f0 100644
--- a/libavdevice/Makefile
+++ b/libavdevice/Makefile
@@ -17,6 +17,7 @@ OBJS-$(CONFIG_AVFOUNDATION_INDEV)        += avfoundation.o
 OBJS-$(CONFIG_BKTR_INDEV)                += bktr.o
 OBJS-$(CONFIG_CACA_OUTDEV)               += caca.o
 OBJS-$(CONFIG_DECKLINK_OUTDEV)           += decklink_enc.o decklink_enc_c.o decklink_common.o
+OBJS-$(CONFIG_DECKLINK_INDEV)            += decklink_dec.o decklink_dec_c.o decklink_common.o
 OBJS-$(CONFIG_DSHOW_INDEV)               += dshow.o dshow_enummediatypes.o \
                                             dshow_enumpins.o dshow_filter.o \
                                             dshow_pin.o dshow_common.o
@@ -57,7 +58,7 @@ OBJS-$(HAVE_LIBC_MSVCRT)                 += file_open.o
 # Windows resource file
 SLIBOBJS-$(HAVE_GNU_WINDRES)             += avdeviceres.o
 
-SKIPHEADERS-$(CONFIG_DECKLINK)           += decklink_enc.h decklink_common.h
+SKIPHEADERS-$(CONFIG_DECKLINK)           += decklink_enc.h decklink_dec.h decklink_common.h
 SKIPHEADERS-$(CONFIG_DSHOW_INDEV)        += dshow_capture.h
 SKIPHEADERS-$(CONFIG_FBDEV_INDEV)        += fbdev_common.h
 SKIPHEADERS-$(CONFIG_FBDEV_OUTDEV)       += fbdev_common.h
diff --git a/libavdevice/alldevices.c b/libavdevice/alldevices.c
index ca34959..b8e7854 100644
--- a/libavdevice/alldevices.c
+++ b/libavdevice/alldevices.c
@@ -50,7 +50,7 @@ void avdevice_register_all(void)
     REGISTER_INDEV   (AVFOUNDATION,     avfoundation);
     REGISTER_INDEV   (BKTR,             bktr);
     REGISTER_OUTDEV  (CACA,             caca);
-    REGISTER_OUTDEV  (DECKLINK,         decklink);
+    REGISTER_INOUTDEV(DECKLINK,         decklink);
     REGISTER_INDEV   (DSHOW,            dshow);
     REGISTER_INDEV   (DV1394,           dv1394);
     REGISTER_INOUTDEV(FBDEV,            fbdev);
diff --git a/libavdevice/decklink_dec.cpp b/libavdevice/decklink_dec.cpp
new file mode 100644
index 0000000..b25a1ba
--- /dev/null
+++ b/libavdevice/decklink_dec.cpp
@@ -0,0 +1,531 @@
+/*
+ * Blackmagic DeckLink output
+ * Copyright (c) 2013-2014 Luca Barbato, Deti Fliegl
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <DeckLinkAPI.h>
+
+#include <pthread.h>
+#include <semaphore.h>
+
+extern "C" {
+#include "libavformat/avformat.h"
+#include "libavformat/internal.h"
+#include "libavutil/imgutils.h"
+}
+
+#include "decklink_common.h"
+#include "decklink_dec.h"
+
+static void avpacket_queue_init(AVFormatContext *avctx, AVPacketQueue *q)
+{
+    memset(q, 0, sizeof(AVPacketQueue));
+    pthread_mutex_init(&q->mutex, NULL);
+    pthread_cond_init(&q->cond, NULL);
+    q->avctx = avctx;
+}
+
+static void avpacket_queue_flush(AVPacketQueue *q)
+{
+    AVPacketList *pkt, *pkt1;
+
+    pthread_mutex_lock(&q->mutex);
+    for (pkt = q->first_pkt; pkt != NULL; pkt = pkt1) {
+        pkt1 = pkt->next;
+        av_free_packet(&pkt->pkt);
+        av_freep(&pkt);
+    }
+    q->last_pkt   = NULL;
+    q->first_pkt  = NULL;
+    q->nb_packets = 0;
+    q->size       = 0;
+    pthread_mutex_unlock(&q->mutex);
+}
+
+static void avpacket_queue_end(AVPacketQueue *q)
+{
+    avpacket_queue_flush(q);
+    pthread_mutex_destroy(&q->mutex);
+    pthread_cond_destroy(&q->cond);
+}
+
+static unsigned long long avpacket_queue_size(AVPacketQueue *q)
+{
+    unsigned long long size;
+    pthread_mutex_lock(&q->mutex);
+    size = q->size;
+    pthread_mutex_unlock(&q->mutex);
+    return size;
+}
+
+static int avpacket_queue_put(AVPacketQueue *q, AVPacket *pkt)
+{
+    AVPacketList *pkt1;
+
+    // Drop Packet if queue size is > 1GB
+    if (avpacket_queue_size(q) >  1024 * 1024 * 1024 ) {
+        av_log(q->avctx, AV_LOG_WARNING,  "Decklink input buffer overrun!\n");
+        return -1;
+    }
+    /* duplicate the packet */
+    if (av_dup_packet(pkt) < 0) {
+        return -1;
+    }
+
+    pkt1 = (AVPacketList *)av_malloc(sizeof(AVPacketList));
+    if (!pkt1) {
+        return -1;
+    }
+    pkt1->pkt  = *pkt;
+    pkt1->next = NULL;
+
+    pthread_mutex_lock(&q->mutex);
+
+    if (!q->last_pkt) {
+        q->first_pkt = pkt1;
+    } else {
+        q->last_pkt->next = pkt1;
+    }
+
+    q->last_pkt = pkt1;
+    q->nb_packets++;
+    q->size += pkt1->pkt.size + sizeof(*pkt1);
+
+    pthread_cond_signal(&q->cond);
+
+    pthread_mutex_unlock(&q->mutex);
+    return 0;
+}
+
+static int avpacket_queue_get(AVPacketQueue *q, AVPacket *pkt, int block)
+{
+    AVPacketList *pkt1;
+    int ret;
+
+    pthread_mutex_lock(&q->mutex);
+
+    for (;; ) {
+        pkt1 = q->first_pkt;
+        if (pkt1) {
+            q->first_pkt = pkt1->next;
+            if (!q->first_pkt) {
+                q->last_pkt = NULL;
+            }
+            q->nb_packets--;
+            q->size -= pkt1->pkt.size + sizeof(*pkt1);
+            *pkt     = pkt1->pkt;
+            av_free(pkt1);
+            ret = 1;
+            break;
+        } else if (!block) {
+            ret = 0;
+            break;
+        } else {
+            pthread_cond_wait(&q->cond, &q->mutex);
+        }
+    }
+    pthread_mutex_unlock(&q->mutex);
+    return ret;
+}
+
+class decklink_input_callback : public IDeckLinkInputCallback
+{
+public:
+        decklink_input_callback(AVFormatContext *_avctx);
+        ~decklink_input_callback();
+
+        virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) { return E_NOINTERFACE; }
+        virtual ULONG STDMETHODCALLTYPE AddRef(void);
+        virtual ULONG STDMETHODCALLTYPE  Release(void);
+        virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged(BMDVideoInputFormatChangedEvents, IDeckLinkDisplayMode*, BMDDetectedVideoInputFormatFlags);
+        virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(IDeckLinkVideoInputFrame*, IDeckLinkAudioInputPacket*);
+
+private:
+        ULONG		m_refCount;
+        pthread_mutex_t	m_mutex;
+        AVFormatContext *avctx;
+        decklink_ctx	*ctx;
+        int no_video;
+        int64_t initial_video_pts;
+        int64_t initial_audio_pts;
+};
+
+decklink_input_callback::decklink_input_callback(AVFormatContext *_avctx) : m_refCount(0)
+{
+    avctx = _avctx;
+    decklink_cctx 	*cctx = (struct decklink_cctx *) avctx->priv_data;
+    ctx = (struct decklink_ctx *) cctx->ctx;
+    initial_audio_pts = initial_video_pts = AV_NOPTS_VALUE;
+    pthread_mutex_init(&m_mutex, NULL);
+}
+
+decklink_input_callback::~decklink_input_callback()
+{
+    pthread_mutex_destroy(&m_mutex);
+}
+
+ULONG decklink_input_callback::AddRef(void)
+{
+    pthread_mutex_lock(&m_mutex);
+    m_refCount++;
+    pthread_mutex_unlock(&m_mutex);
+
+    return (ULONG)m_refCount;
+}
+
+ULONG decklink_input_callback::Release(void)
+{
+    pthread_mutex_lock(&m_mutex);
+    m_refCount--;
+    pthread_mutex_unlock(&m_mutex);
+
+    if (m_refCount == 0) {
+        delete this;
+        return 0;
+    }
+
+    return (ULONG)m_refCount;
+}
+
+HRESULT decklink_input_callback::VideoInputFrameArrived(
+    IDeckLinkVideoInputFrame *videoFrame, IDeckLinkAudioInputPacket *audioFrame)
+{
+    void *frameBytes;
+    void *audioFrameBytes;
+    BMDTimeValue frameTime;
+    BMDTimeValue frameDuration;
+
+    ctx->frameCount++;
+
+    // Handle Video Frame
+    if (videoFrame) {
+        AVPacket pkt;
+        AVCodecContext *c;
+        av_init_packet(&pkt);
+        c = ctx->video_st->codec;
+        if (ctx->frameCount % 25 == 0) {
+            unsigned long long qsize = avpacket_queue_size(&ctx->queue);
+            av_log(avctx, AV_LOG_DEBUG, 
+                    "Frame received (#%lu) - Valid (%liB) - QSize %fMB\n",
+                    ctx->frameCount,
+                    videoFrame->GetRowBytes() * videoFrame->GetHeight(),
+                    (double)qsize / 1024 / 1024);
+        }
+
+        videoFrame->GetBytes(&frameBytes);
+        videoFrame->GetStreamTime(&frameTime, &frameDuration,
+                                  ctx->video_st->time_base.den);
+
+        if (videoFrame->GetFlags() & bmdFrameHasNoInputSource) {
+            unsigned bars[8] = {
+                0xEA80EA80, 0xD292D210, 0xA910A9A5, 0x90229035,
+                0x6ADD6ACA, 0x51EF515A, 0x286D28EF, 0x10801080 };
+            int width  = videoFrame->GetWidth();
+            int height = videoFrame->GetHeight();
+            unsigned *p = (unsigned *)frameBytes;
+
+            for (int y = 0; y < height; y++) {
+                for (int x = 0; x < width; x += 2)
+                    *p++ = bars[(x * 8) / width];
+            }
+
+            if (!no_video) {
+                av_log(avctx, AV_LOG_WARNING, "Frame received (#%lu) - No input signal detected "
+                        "- Frames dropped %u\n", ctx->frameCount, ++ctx->dropped);
+            }
+            no_video = 1;
+        } else {
+            if (no_video) {
+                av_log(avctx, AV_LOG_WARNING, "Frame received (#%lu) - Input returned "
+                        "- Frames dropped %u\n", ctx->frameCount, ++ctx->dropped);
+            }
+            no_video = 0;
+        }
+
+        pkt.pts = frameTime / ctx->video_st->time_base.num;
+
+        if (initial_video_pts == AV_NOPTS_VALUE) {
+            initial_video_pts = pkt.pts;
+        }
+
+        pkt.pts -= initial_video_pts;
+        pkt.dts = pkt.pts;
+
+        pkt.duration = frameDuration;
+        //To be made sure it still applies
+        pkt.flags       |= AV_PKT_FLAG_KEY;
+        pkt.stream_index = ctx->video_st->index;
+        pkt.data         = (uint8_t *)frameBytes;
+        pkt.size         = videoFrame->GetRowBytes() *
+                           videoFrame->GetHeight();
+        //fprintf(stderr,"Video Frame size %d ts %d\n", pkt.size, pkt.pts);
+        c->frame_number++;
+        if (avpacket_queue_put(&ctx->queue, &pkt) < 0) {
+            ++ctx->dropped;
+        }
+    }
+
+    // Handle Audio Frame
+    if (audioFrame) {
+        AVCodecContext *c;
+        AVPacket pkt;
+        BMDTimeValue audio_pts;
+        av_init_packet(&pkt);
+
+        c = ctx->audio_st->codec;
+        //hack among hacks
+        pkt.size = audioFrame->GetSampleFrameCount() * ctx->audio_st->codec->channels * (16 / 8);
+        audioFrame->GetBytes(&audioFrameBytes);
+        audioFrame->GetPacketTime(&audio_pts, ctx->audio_st->time_base.den);
+        pkt.pts = audio_pts / ctx->audio_st->time_base.num;
+
+        if (initial_audio_pts == AV_NOPTS_VALUE) {
+            initial_audio_pts = pkt.pts;
+        }
+
+        pkt.pts -= initial_audio_pts;
+        pkt.dts = pkt.pts;
+
+        //fprintf(stderr,"Audio Frame size %d ts %d\n", pkt.size, pkt.pts);
+        pkt.flags       |= AV_PKT_FLAG_KEY;
+        pkt.stream_index = ctx->audio_st->index;
+        pkt.data         = (uint8_t *)audioFrameBytes;
+
+        c->frame_number++;
+        if (avpacket_queue_put(&ctx->queue, &pkt) < 0) {
+            ++ctx->dropped;
+        }
+    }
+
+    return S_OK;
+}
+
+HRESULT decklink_input_callback::VideoInputFormatChanged(
+    BMDVideoInputFormatChangedEvents events, IDeckLinkDisplayMode *mode,
+    BMDDetectedVideoInputFormatFlags)
+{
+    return S_OK;
+}
+
+static HRESULT decklink_start_input(AVFormatContext *avctx)
+{
+    struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data;
+    struct decklink_ctx *ctx = (struct decklink_ctx *) cctx->ctx;
+
+    ctx->input_callback = new decklink_input_callback(avctx);
+    ctx->dli->SetCallback(ctx->input_callback);
+    return ctx->dli->StartStreams();
+}
+
+extern "C" {
+
+av_cold int ff_decklink_read_close(AVFormatContext *avctx)
+{
+    struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data;
+    struct decklink_ctx *ctx = (struct decklink_ctx *) cctx->ctx;
+
+    if (ctx->capture_started) {
+        ctx->dli->StopStreams();
+        ctx->dli->DisableVideoInput();
+        ctx->dli->DisableAudioInput();
+    }
+
+    if (ctx->dli)
+        ctx->dli->Release();
+    if (ctx->dl)
+        ctx->dl->Release();
+
+    avpacket_queue_end(&ctx->queue);
+
+    av_freep(&cctx->ctx);
+
+    return 0;
+}
+
+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;
+
+    ctx = (struct decklink_ctx *) av_mallocz(sizeof(struct decklink_ctx));
+    if (!ctx)
+        return AVERROR(ENOMEM);
+    ctx->list_devices = cctx->list_devices;
+    ctx->list_formats = cctx->list_formats;
+    ctx->preroll      = cctx->preroll;
+    cctx->ctx = ctx;
+
+    iter = CreateDeckLinkIteratorInstance();
+    if (!iter) {
+        av_log(avctx, AV_LOG_ERROR, "Could not create DeckLink iterator\n");
+        return AVERROR(EIO);
+    }
+
+    /* List available devices. */
+    if (ctx->list_devices) {
+        ff_decklink_list_devices(avctx);
+        return AVERROR_EXIT;
+    }
+
+    strcpy (fname, avctx->filename);
+    tmp=strchr (fname, '@');
+    if (tmp != NULL) {
+        mode_num = atoi (tmp+1);
+        *tmp = 0;
+    }
+
+    /* Open device. */
+    while (iter->Next(&dl) == S_OK) {
+        const char *displayName;
+        ff_decklink_get_display_name(dl, &displayName);
+        if (!strcmp(fname, displayName)) {
+            av_free((void *) displayName);
+            ctx->dl = dl;
+            break;
+        }
+        av_free((void *) displayName);
+        dl->Release();
+    }
+    iter->Release();
+    if (!ctx->dl) {
+        av_log(avctx, AV_LOG_ERROR, "Could not open '%s'\n", fname);
+        return AVERROR(EIO);
+    }
+
+    /* 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",
+               avctx->filename);
+        ctx->dl->Release();
+        return AVERROR(EIO);
+    }
+
+    /* List supported formats. */
+    if (ctx->list_formats) {
+        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 (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);
+            goto error;
+        }
+    }
+
+    itermode->Release();
+
+    /* Setup streams. */
+    st = avformat_new_stream(avctx, NULL);
+    if (!st) {
+        av_log(avctx, AV_LOG_ERROR, "Cannot add stream\n");
+        goto error;
+    }
+    st->codec->codec_type  = AVMEDIA_TYPE_AUDIO;
+    st->codec->codec_id    = AV_CODEC_ID_PCM_S16LE;
+    st->codec->sample_rate = bmdAudioSampleRate48kHz;
+    st->codec->channels    = 2;
+    avpriv_set_pts_info(st, 64, 1, 1000000);  /* 64 bits pts in us */
+    ctx->audio_st=st;
+
+    st = avformat_new_stream(avctx, NULL);
+    if (!st) {
+        av_log(avctx, AV_LOG_ERROR, "Cannot add stream\n");
+        goto error;
+    }
+    st->codec->codec_type  = AVMEDIA_TYPE_VIDEO;
+    st->codec->codec_id    = AV_CODEC_ID_RAWVIDEO;
+    st->codec->width       = ctx->bmd_width;
+    st->codec->height      = ctx->bmd_height;
+
+    st->codec->pix_fmt	   = AV_PIX_FMT_UYVY422;
+    st->codec->time_base.den      = ctx->bmd_tb_den;
+    st->codec->time_base.num      = ctx->bmd_tb_num;
+    st->codec->bit_rate    = avpicture_get_size(st->codec->pix_fmt, ctx->bmd_width, ctx->bmd_height) * 1/av_q2d(st->codec->time_base) * 8;
+    st->codec->codec_tag   = MKTAG('U', 'Y', 'V', 'Y');
+
+    avpriv_set_pts_info(st, 64, 1, 1000000);  /* 64 bits pts in us */
+
+    ctx->video_st=st;
+
+    result = ctx->dli->EnableAudioInput(bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger, 2);
+
+    if (result != S_OK) {
+        av_log(avctx, AV_LOG_ERROR, "Cannot enable audio input\n");
+        goto error;
+    }
+    
+    result = ctx->dli->EnableVideoInput(ctx->bmd_mode, bmdFormat8BitYUV, bmdVideoInputFlagDefault);
+
+    if (result != S_OK) {
+        av_log(avctx, AV_LOG_ERROR, "Cannot enable video input\n");
+        goto error;
+    }
+    
+    avpacket_queue_init (avctx, &ctx->queue);
+
+    if (decklink_start_input (avctx) != S_OK) {
+        av_log(avctx, AV_LOG_ERROR, "Cannot start input stream\n");
+        goto error;
+    }
+
+    return 0;
+
+error:
+
+    ctx->dli->Release();
+    ctx->dl->Release();
+
+    return AVERROR(EIO);
+}
+
+int ff_decklink_read_packet(AVFormatContext *avctx, AVPacket *pkt)
+{
+    struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data;
+    struct decklink_ctx *ctx = (struct decklink_ctx *) cctx->ctx;
+    AVFrame *frame = ctx->video_st->codec->coded_frame;
+
+    avpacket_queue_get(&ctx->queue, pkt, 1);
+    if (frame && (ctx->bmd_field_dominance == bmdUpperFieldFirst || ctx->bmd_field_dominance == bmdLowerFieldFirst)) {
+        frame->interlaced_frame = 1;
+        if (ctx->bmd_field_dominance == bmdUpperFieldFirst) {
+            frame->top_field_first = 1;
+        }
+    }
+
+    return 0;
+}
+
+} /* extern "C" */
diff --git a/libavdevice/decklink_dec.h b/libavdevice/decklink_dec.h
new file mode 100644
index 0000000..6bd9226
--- /dev/null
+++ b/libavdevice/decklink_dec.h
@@ -0,0 +1,32 @@
+/*
+ * Blackmagic DeckLink output
+ * Copyright (c) 2013-2014 Ramiro Polla
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int ff_decklink_read_header(AVFormatContext *avctx);
+int ff_decklink_read_packet(AVFormatContext *avctx, AVPacket *pkt);
+int ff_decklink_read_close(AVFormatContext *avctx);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
diff --git a/libavdevice/decklink_dec_c.c b/libavdevice/decklink_dec_c.c
new file mode 100644
index 0000000..2aea277
--- /dev/null
+++ b/libavdevice/decklink_dec_c.c
@@ -0,0 +1,54 @@
+/*
+ * Blackmagic DeckLink output
+ * Copyright (c) 2014 Deti Fliegl
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavformat/avformat.h"
+#include "libavutil/opt.h"
+
+#include "decklink_common_c.h"
+#include "decklink_dec.h"
+
+#define OFFSET(x) offsetof(struct decklink_cctx, x)
+#define DEC AV_OPT_FLAG_DECODING_PARAM
+
+static const AVOption options[] = {
+    { "list_devices", "list available devices"  , OFFSET(list_devices), AV_OPT_TYPE_INT   , { .i64 = 0   }, 0, 1, DEC },
+    { "list_formats", "list supported formats"  , OFFSET(list_formats), AV_OPT_TYPE_INT   , { .i64 = 0   }, 0, 1, DEC },
+    { NULL },
+};
+
+static const AVClass decklink_demuxer_class = {
+    .class_name = "Blackmagic DeckLink demuxer",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+    .category   = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT,
+};
+
+AVInputFormat ff_decklink_demuxer = {
+    .name           = "decklink",
+    .long_name      = NULL_IF_CONFIG_SMALL("Blackmagic DeckLink input"),
+    .flags          = AVFMT_NOFILE | AVFMT_RAWPICTURE,
+    .priv_class     = &decklink_demuxer_class,
+    .priv_data_size = sizeof(struct decklink_cctx),
+    .read_header   = ff_decklink_read_header,
+    .read_packet   = ff_decklink_read_packet,
+    .read_close    = ff_decklink_read_close,
+};
-- 
1.9.1

-------------- next part --------------
Signed-off-by: Deti fliegl <deti at fliegl.de>
---
 libavdevice/Makefile            |   4 +-
 libavdevice/decklink_common.cpp | 229 ++++++++++++++++++++++++++++++++++++++++
 libavdevice/decklink_common.h   |  98 +++++++++++++++++
 libavdevice/decklink_common_c.h |  32 ++++++
 libavdevice/decklink_enc.cpp    | 198 ++++------------------------------
 libavdevice/decklink_enc.h      |  11 --
 libavdevice/decklink_enc_c.c    |   1 +
 7 files changed, 380 insertions(+), 193 deletions(-)
 create mode 100644 libavdevice/decklink_common.cpp
 create mode 100644 libavdevice/decklink_common.h
 create mode 100644 libavdevice/decklink_common_c.h

diff --git a/libavdevice/Makefile b/libavdevice/Makefile
index db301e7..f90c0f7 100644
--- a/libavdevice/Makefile
+++ b/libavdevice/Makefile
@@ -16,7 +16,7 @@ OBJS-$(CONFIG_ALSA_OUTDEV)               += alsa-audio-common.o \
 OBJS-$(CONFIG_AVFOUNDATION_INDEV)        += avfoundation.o
 OBJS-$(CONFIG_BKTR_INDEV)                += bktr.o
 OBJS-$(CONFIG_CACA_OUTDEV)               += caca.o
-OBJS-$(CONFIG_DECKLINK_OUTDEV)           += decklink_enc.o decklink_enc_c.o
+OBJS-$(CONFIG_DECKLINK_OUTDEV)           += decklink_enc.o decklink_enc_c.o decklink_common.o
 OBJS-$(CONFIG_DSHOW_INDEV)               += dshow.o dshow_enummediatypes.o \
                                             dshow_enumpins.o dshow_filter.o \
                                             dshow_pin.o dshow_common.o
@@ -57,7 +57,7 @@ OBJS-$(HAVE_LIBC_MSVCRT)                 += file_open.o
 # Windows resource file
 SLIBOBJS-$(HAVE_GNU_WINDRES)             += avdeviceres.o
 
-SKIPHEADERS-$(CONFIG_DECKLINK)           += decklink_enc.h
+SKIPHEADERS-$(CONFIG_DECKLINK)           += decklink_enc.h decklink_common.h
 SKIPHEADERS-$(CONFIG_DSHOW_INDEV)        += dshow_capture.h
 SKIPHEADERS-$(CONFIG_FBDEV_INDEV)        += fbdev_common.h
 SKIPHEADERS-$(CONFIG_FBDEV_OUTDEV)       += fbdev_common.h
diff --git a/libavdevice/decklink_common.cpp b/libavdevice/decklink_common.cpp
new file mode 100644
index 0000000..9a9e44b
--- /dev/null
+++ b/libavdevice/decklink_common.cpp
@@ -0,0 +1,229 @@
+/*
+ * Blackmagic DeckLink output
+ * Copyright (c) 2013-2014 Ramiro Polla, Luca Barbato, Deti Fliegl
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <DeckLinkAPI.h>
+#ifdef _WIN32
+#include <DeckLinkAPI_i.c>
+#else
+#include <DeckLinkAPIDispatch.cpp>
+#endif
+
+#include <pthread.h>
+#include <semaphore.h>
+
+extern "C" {
+#include "libavformat/avformat.h"
+#include "libavformat/internal.h"
+#include "libavutil/imgutils.h"
+}
+
+#include "decklink_common.h"
+
+#ifdef _WIN32
+IDeckLinkIterator *CreateDeckLinkIteratorInstance(void)
+{
+    IDeckLinkIterator *iter;
+
+    if (CoInitialize(NULL) != S_OK) {
+        av_log(NULL, 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;
+    }
+
+    return iter;
+}
+#endif
+
+#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
+#else
+#define DECKLINK_STR    const char *
+#define DECKLINK_STRDUP av_strdup
+#endif
+
+HRESULT ff_decklink_get_display_name(IDeckLink *This, const char **displayName)
+{
+    DECKLINK_STR tmpDisplayName;
+    HRESULT hr = This->GetDisplayName(&tmpDisplayName);
+    if (hr != S_OK)
+        return hr;
+    *displayName = DECKLINK_STRDUP(tmpDisplayName);
+    /* free() is needed for a string returned by the DeckLink SDL. */
+    free((void *) tmpDisplayName);
+    return hr;
+}
+
+int ff_decklink_set_format(AVFormatContext *avctx,
+                               int width, int height,
+                               int tb_num, int tb_den,
+                               decklink_direction_t direction, int num)
+{
+    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;
+
+    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 Display Mode Iterator\n");
+            return AVERROR(EIO);
+    }
+
+
+    if (tb_num == 1) {
+        tb_num *= 1000;
+        tb_den *= 1000;
+    }
+    ctx->bmd_mode = bmdModeUnknown;
+    while ((ctx->bmd_mode == bmdModeUnknown) && itermode->Next(&mode) == S_OK) {
+        BMDTimeValue bmd_tb_num, bmd_tb_den;
+        int bmd_width  = mode->GetWidth();
+        int bmd_height = mode->GetHeight();
+
+        mode->GetFrameRate(&bmd_tb_num, &bmd_tb_den);
+
+        if ((bmd_width == width && bmd_height == height &&
+            bmd_tb_num == tb_num && bmd_tb_den == tb_den) || i == num) {
+            ctx->bmd_mode   = mode->GetDisplayMode();
+            ctx->bmd_width  = bmd_width;
+            ctx->bmd_height = bmd_height;
+            ctx->bmd_tb_den = bmd_tb_den;
+            ctx->bmd_tb_num = bmd_tb_num;
+            ctx->bmd_field_dominance = mode->GetFieldDominance();
+            av_log(avctx, AV_LOG_INFO, "Found Decklink mode %d x %d with rate %.2f%s\n",
+                bmd_width, bmd_height, (float)bmd_tb_den/(float)bmd_tb_num,
+                (ctx->bmd_field_dominance==bmdLowerFieldFirst || ctx->bmd_field_dominance==bmdUpperFieldFirst)?"(i)":"");
+        }
+
+        mode->Release();
+        i++;
+    }
+
+    itermode->Release();
+
+    if (ctx->bmd_mode == bmdModeUnknown)
+        return -1;
+    if (direction == DIRECTION_IN) {
+        if (ctx->dli->DoesSupportVideoMode(ctx->bmd_mode, bmdFormat8BitYUV,
+                                           bmdVideoOutputFlagDefault,
+                                           &support, NULL) != S_OK)
+            return -1;
+    } else {
+        if (ctx->dlo->DoesSupportVideoMode(ctx->bmd_mode, bmdFormat8BitYUV,
+                                           bmdVideoOutputFlagDefault,
+                                           &support, NULL) != S_OK)
+        return -1;
+    }
+    if (support == bmdDisplayModeSupported)
+        return 0;
+
+    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, direction, num);
+}
+
+int ff_decklink_list_devices(AVFormatContext *avctx)
+{
+    IDeckLink *dl = NULL;
+    IDeckLinkIterator *iter = CreateDeckLinkIteratorInstance();
+    if (!iter) {
+        av_log(avctx, AV_LOG_ERROR, "Could not create DeckLink iterator\n");
+        return AVERROR(EIO);
+    }
+    av_log(avctx, AV_LOG_INFO, "Blackmagic DeckLink devices:\n");
+    while (iter->Next(&dl) == S_OK) {
+        const char *displayName;
+        ff_decklink_get_display_name(dl, &displayName);
+        av_log(avctx, AV_LOG_INFO, "\t'%s'\n", displayName);
+        av_free((void *) displayName);
+        dl->Release();
+    }
+    iter->Release();
+    return 0;
+}
+
+int ff_decklink_list_formats(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;
+    IDeckLinkDisplayModeIterator *itermode;
+    IDeckLinkDisplayMode *mode;
+    int i=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 Display Mode Iterator\n");
+            return AVERROR(EIO);
+    }
+
+    av_log(avctx, AV_LOG_INFO, "Supported formats for '%s':\n",
+               avctx->filename);
+    while (itermode->Next(&mode) == S_OK) {
+        BMDTimeValue tb_num, tb_den;
+        mode->GetFrameRate(&tb_num, &tb_den);
+        av_log(avctx, AV_LOG_INFO, "\t%d\t%ldx%ld at %d/%d fps",
+                ++i,mode->GetWidth(), mode->GetHeight(),
+                (int) tb_den, (int) tb_num);
+        switch (mode->GetFieldDominance()) {
+        case bmdLowerFieldFirst:
+        av_log(avctx, AV_LOG_INFO, " (interlaced, lower field first)"); break;
+        case bmdUpperFieldFirst:
+        av_log(avctx, AV_LOG_INFO, " (interlaced, upper field first)"); break;
+        }
+        av_log(avctx, AV_LOG_INFO, "\n");
+        mode->Release();
+    }
+
+    itermode->Release();
+
+    return 0;
+}
diff --git a/libavdevice/decklink_common.h b/libavdevice/decklink_common.h
new file mode 100644
index 0000000..544da27
--- /dev/null
+++ b/libavdevice/decklink_common.h
@@ -0,0 +1,98 @@
+/*
+ * Blackmagic DeckLink common code
+ * Copyright (c) 2013-2014 Ramiro Polla, Luca Barbato, Deti Fliegl
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "decklink_common_c.h"
+
+class decklink_output_callback;
+class decklink_input_callback;
+
+typedef struct AVPacketQueue {
+    AVPacketList *first_pkt, *last_pkt;
+    int nb_packets;
+    unsigned long long size;
+    int abort_request;
+    pthread_mutex_t mutex;
+    pthread_cond_t cond;
+    AVFormatContext *avctx;
+} AVPacketQueue;
+
+struct decklink_ctx {
+    /* DeckLink SDK interfaces */
+    IDeckLink *dl;
+    IDeckLinkOutput *dlo;
+    IDeckLinkInput *dli;
+    decklink_output_callback *output_callback;
+    decklink_input_callback *input_callback;
+
+    /* DeckLink mode information */
+    BMDTimeValue bmd_tb_den;
+    BMDTimeValue bmd_tb_num;
+    BMDDisplayMode bmd_mode;
+    int bmd_width;
+    int bmd_height;
+    int bmd_field_dominance;
+
+    /* Capture buffer queue */
+    AVPacketQueue queue;
+
+    /* Streams present */
+    int audio;
+    int video;
+
+    /* Status */
+    int playback_started;
+    int capture_started;
+    int64_t last_pts;
+    unsigned long frameCount;
+    unsigned int dropped;
+    AVStream *audio_st;
+    AVStream *video_st;
+
+    /* Options */
+    int list_devices;
+    int list_formats;
+    double preroll;
+
+    int frames_preroll;
+    int frames_buffer;
+
+    sem_t semaphore;
+
+    int channels;
+};
+
+typedef enum { DIRECTION_IN, DIRECTION_OUT} decklink_direction_t;
+
+#ifdef _WIN32
+typedef unsigned long buffercount_type;
+IDeckLinkIterator *CreateDeckLinkIteratorInstance(void);
+char *dup_wchar_to_utf8(wchar_t *w);
+#else
+typedef uint32_t buffercount_type;
+#endif
+
+
+HRESULT ff_decklink_get_display_name(IDeckLink *This, const char **displayName);
+int ff_decklink_set_format(AVFormatContext *avctx, int width, int height, int tb_num, int tb_den, decklink_direction_t direction = DIRECTION_OUT, int num = 0);
+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);
+
diff --git a/libavdevice/decklink_common_c.h b/libavdevice/decklink_common_c.h
new file mode 100644
index 0000000..861a51a
--- /dev/null
+++ b/libavdevice/decklink_common_c.h
@@ -0,0 +1,32 @@
+/*
+ * Blackmagic DeckLink common code
+ * Copyright (c) 2013-2014 Ramiro Polla
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+struct decklink_cctx {
+    const AVClass *cclass;
+
+    void *ctx;
+
+    /* Options */
+    int list_devices;
+    int list_formats;
+    double preroll;
+};
+
diff --git a/libavdevice/decklink_enc.cpp b/libavdevice/decklink_enc.cpp
index 3d7cbe2..6c5450f 100644
--- a/libavdevice/decklink_enc.cpp
+++ b/libavdevice/decklink_enc.cpp
@@ -20,13 +20,6 @@
  */
 
 #include <DeckLinkAPI.h>
-#ifdef _WIN32
-#include <DeckLinkAPI_i.c>
-typedef unsigned long buffercount_type;
-#else
-#include <DeckLinkAPIDispatch.cpp>
-typedef uint32_t buffercount_type;
-#endif
 
 #include <pthread.h>
 #include <semaphore.h>
@@ -37,44 +30,9 @@ extern "C" {
 #include "libavutil/imgutils.h"
 }
 
+#include "decklink_common.h"
 #include "decklink_enc.h"
 
-class decklink_callback;
-
-struct decklink_ctx {
-    /* DeckLink SDK interfaces */
-    IDeckLink *dl;
-    IDeckLinkOutput *dlo;
-    decklink_callback *callback;
-
-    /* DeckLink mode information */
-    IDeckLinkDisplayModeIterator *itermode;
-    BMDTimeValue bmd_tb_den;
-    BMDTimeValue bmd_tb_num;
-    BMDDisplayMode bmd_mode;
-    int bmd_width;
-    int bmd_height;
-
-    /* Streams present */
-    int audio;
-    int video;
-
-    /* Status */
-    int playback_started;
-    int64_t last_pts;
-
-    /* Options */
-    int list_devices;
-    int list_formats;
-    double preroll;
-
-    int frames_preroll;
-    int frames_buffer;
-
-    sem_t semaphore;
-
-    int channels;
-};
 
 /* DeckLink callback class declaration */
 class decklink_frame : public IDeckLinkVideoFrame
@@ -109,7 +67,7 @@ private:
     int   _refs;
 };
 
-class decklink_callback : public IDeckLinkVideoOutputCallback
+class decklink_output_callback : public IDeckLinkVideoOutputCallback
 {
 public:
     virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame *_frame, BMDOutputFrameCompletionResult result)
@@ -130,99 +88,6 @@ public:
     virtual ULONG   STDMETHODCALLTYPE Release(void)                           { return 1; }
 };
 
-#ifdef _WIN32
-static IDeckLinkIterator *CreateDeckLinkIteratorInstance(void)
-{
-    IDeckLinkIterator *iter;
-
-    if (CoInitialize(NULL) != S_OK) {
-        av_log(NULL, 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;
-    }
-
-    return iter;
-}
-#endif
-
-/* free() is needed for a string returned by the DeckLink SDL. */
-#undef free
-
-#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
-#else
-#define DECKLINK_STR    const char *
-#define DECKLINK_STRDUP av_strdup
-#endif
-
-static HRESULT IDeckLink_GetDisplayName(IDeckLink *This, const char **displayName)
-{
-    DECKLINK_STR tmpDisplayName;
-    HRESULT hr = This->GetDisplayName(&tmpDisplayName);
-    if (hr != S_OK)
-        return hr;
-    *displayName = DECKLINK_STRDUP(tmpDisplayName);
-    free((void *) tmpDisplayName);
-    return hr;
-}
-
-static int decklink_set_format(struct decklink_ctx *ctx,
-                               int width, int height,
-                               int tb_num, int tb_den)
-{
-    BMDDisplayModeSupport support;
-    IDeckLinkDisplayMode *mode;
-
-    if (tb_num == 1) {
-        tb_num *= 1000;
-        tb_den *= 1000;
-    }
-    ctx->bmd_mode = bmdModeUnknown;
-    while ((ctx->bmd_mode == bmdModeUnknown) && ctx->itermode->Next(&mode) == S_OK) {
-        BMDTimeValue bmd_tb_num, bmd_tb_den;
-        int bmd_width  = mode->GetWidth();
-        int bmd_height = mode->GetHeight();
-
-        mode->GetFrameRate(&bmd_tb_num, &bmd_tb_den);
-
-        if (bmd_width == width && bmd_height == height &&
-            bmd_tb_num == tb_num && bmd_tb_den == tb_den) {
-            ctx->bmd_mode   = mode->GetDisplayMode();
-            ctx->bmd_width  = bmd_width;
-            ctx->bmd_height = bmd_height;
-            ctx->bmd_tb_den = bmd_tb_den;
-            ctx->bmd_tb_num = bmd_tb_num;
-        }
-
-        mode->Release();
-    }
-    if (ctx->bmd_mode == bmdModeUnknown)
-        return -1;
-    if (ctx->dlo->DoesSupportVideoMode(ctx->bmd_mode, bmdFormat8BitYUV,
-                                       bmdVideoOutputFlagDefault,
-                                       &support, NULL) != S_OK)
-        return -1;
-    if (support == bmdDisplayModeSupported)
-        return 0;
-
-    return -1;
-}
-
 static int decklink_setup_video(AVFormatContext *avctx, AVStream *st)
 {
     struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data;
@@ -239,7 +104,7 @@ static int decklink_setup_video(AVFormatContext *avctx, AVStream *st)
                " Only AV_PIX_FMT_UYVY422 is supported.\n");
         return -1;
     }
-    if (decklink_set_format(ctx, c->width, c->height,
+    if (ff_decklink_set_format(avctx, c->width, c->height,
                             c->time_base.num, c->time_base.den)) {
         av_log(avctx, AV_LOG_ERROR, "Unsupported video size or framerate!"
                " Check available formats with -list_formats 1.\n");
@@ -252,8 +117,8 @@ static int decklink_setup_video(AVFormatContext *avctx, AVStream *st)
     }
 
     /* Set callback. */
-    ctx->callback = new decklink_callback();
-    ctx->dlo->SetScheduledFrameCompletionCallback(ctx->callback);
+    ctx->output_callback = new decklink_output_callback();
+    ctx->dlo->SetScheduledFrameCompletionCallback(ctx->output_callback);
 
     /* Start video semaphore. */
     ctx->frames_preroll = c->time_base.den * ctx->preroll;
@@ -333,8 +198,8 @@ av_cold int ff_decklink_write_trailer(AVFormatContext *avctx)
     if (ctx->dl)
         ctx->dl->Release();
 
-    if (ctx->callback)
-        delete ctx->callback;
+    if (ctx->output_callback)
+        delete ctx->output_callback;
 
     sem_destroy(&ctx->semaphore);
 
@@ -450,6 +315,7 @@ av_cold int ff_decklink_write_header(AVFormatContext *avctx)
 {
     struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data;
     struct decklink_ctx *ctx;
+    IDeckLinkDisplayModeIterator *itermode;
     IDeckLinkIterator *iter;
     IDeckLink *dl = NULL;
     unsigned int n;
@@ -470,22 +336,14 @@ av_cold int ff_decklink_write_header(AVFormatContext *avctx)
 
     /* List available devices. */
     if (ctx->list_devices) {
-        av_log(avctx, AV_LOG_INFO, "Blackmagic DeckLink devices:\n");
-        while (iter->Next(&dl) == S_OK) {
-            const char *displayName;
-            IDeckLink_GetDisplayName(dl, &displayName);
-            av_log(avctx, AV_LOG_INFO, "\t'%s'\n", displayName);
-            av_free((void *) displayName);
-            dl->Release();
-        }
-        iter->Release();
+        ff_decklink_list_devices(avctx);
         return AVERROR_EXIT;
     }
 
     /* Open device. */
     while (iter->Next(&dl) == S_OK) {
         const char *displayName;
-        IDeckLink_GetDisplayName(dl, &displayName);
+        ff_decklink_get_display_name(dl, &displayName);
         if (!strcmp(avctx->filename, displayName)) {
             av_free((void *) displayName);
             ctx->dl = dl;
@@ -508,40 +366,20 @@ av_cold int ff_decklink_write_header(AVFormatContext *avctx)
         return AVERROR(EIO);
     }
 
-    if (ctx->dlo->GetDisplayModeIterator(&ctx->itermode) != S_OK) {
-        av_log(avctx, AV_LOG_ERROR, "Could not get Display Mode Iterator\n");
-        ctx->dl->Release();
-        return AVERROR(EIO);
-    }
-
     /* List supported formats. */
     if (ctx->list_formats) {
-        IDeckLinkDisplayMode *mode;
-
-        av_log(avctx, AV_LOG_INFO, "Supported formats for '%s':\n",
-               avctx->filename);
-        while (ctx->itermode->Next(&mode) == S_OK) {
-            BMDTimeValue tb_num, tb_den;
-            mode->GetFrameRate(&tb_num, &tb_den);
-            av_log(avctx, AV_LOG_INFO, "\t%ldx%ld at %d/%d fps",
-                   mode->GetWidth(), mode->GetHeight(),
-                   (int) tb_den, (int) tb_num);
-            switch (mode->GetFieldDominance()) {
-            case bmdLowerFieldFirst:
-            av_log(avctx, AV_LOG_INFO, " (interlaced, lower field first)"); break;
-            case bmdUpperFieldFirst:
-            av_log(avctx, AV_LOG_INFO, " (interlaced, upper field first)"); break;
-            }
-            av_log(avctx, AV_LOG_INFO, "\n");
-            mode->Release();
-        }
-
-        ctx->itermode->Release();
+        ff_decklink_list_formats(avctx);
         ctx->dlo->Release();
         ctx->dl->Release();
         return AVERROR_EXIT;
     }
 
+    if (ctx->dlo->GetDisplayModeIterator(&itermode) != S_OK) {
+        av_log(avctx, AV_LOG_ERROR, "Could not get Display Mode Iterator\n");
+        ctx->dl->Release();
+        return AVERROR(EIO);
+    }
+
     /* Setup streams. */
     for (n = 0; n < avctx->nb_streams; n++) {
         AVStream *st = avctx->streams[n];
@@ -557,7 +395,7 @@ av_cold int ff_decklink_write_header(AVFormatContext *avctx)
             goto error;
         }
     }
-    ctx->itermode->Release();
+    itermode->Release();
 
     return 0;
 
diff --git a/libavdevice/decklink_enc.h b/libavdevice/decklink_enc.h
index 0b9ad8f..6086947 100644
--- a/libavdevice/decklink_enc.h
+++ b/libavdevice/decklink_enc.h
@@ -19,17 +19,6 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-struct decklink_cctx {
-    const AVClass *cclass;
-
-    void *ctx;
-
-    /* Options */
-    int list_devices;
-    int list_formats;
-    double preroll;
-};
-
 #ifdef __cplusplus
 extern "C" {
 #endif
diff --git a/libavdevice/decklink_enc_c.c b/libavdevice/decklink_enc_c.c
index 7c18043..c3c9018 100644
--- a/libavdevice/decklink_enc_c.c
+++ b/libavdevice/decklink_enc_c.c
@@ -22,6 +22,7 @@
 #include "libavformat/avformat.h"
 #include "libavutil/opt.h"
 
+#include "decklink_common_c.h"
 #include "decklink_enc.h"
 
 #define OFFSET(x) offsetof(struct decklink_cctx, x)
-- 
1.9.1



More information about the ffmpeg-devel mailing list