[FFmpeg-devel,PATCHv3,2/2] libavdevice/decklink: add support for 10-bit output for Decklink SDI

Submitted by Devin Heitmueller on Oct. 6, 2017, 12:55 p.m.

Details

Message ID 20171006125544.35881-3-dheitmueller@ltnglobal.com
State New
Headers show

Commit Message

Devin Heitmueller Oct. 6, 2017, 12:55 p.m.
Can be tested via the following command:

./ffmpeg -i foo.ts -f decklink -vcodec v210 'DeckLink Duo (1)'

Note that the 8-bit support works as it did before, and setting
the pix_fmt isn't required for 10-bit mode.  The code defaults to
operating in 8-bit mode when no vcodec is specified, for backward
compatibility.

Updated to reflect feedback from Marton Balint <cus@passwd.hu>

Signed-off-by: Devin Heitmueller <dheitmueller@ltnglobal.com>
---
 libavdevice/decklink_enc.cpp | 112 ++++++++++++++++++++++++++++++++-----------
 1 file changed, 83 insertions(+), 29 deletions(-)

Patch hide | download patch | download mbox

diff --git a/libavdevice/decklink_enc.cpp b/libavdevice/decklink_enc.cpp
index 0776741812..81df563b3b 100644
--- a/libavdevice/decklink_enc.cpp
+++ b/libavdevice/decklink_enc.cpp
@@ -44,20 +44,45 @@  extern "C" {
 class decklink_frame : public IDeckLinkVideoFrame
 {
 public:
-    decklink_frame(struct decklink_ctx *ctx, AVFrame *avframe) :
-                   _ctx(ctx), _avframe(avframe),  _refs(1) { }
-
-    virtual long           STDMETHODCALLTYPE GetWidth      (void)          { return _avframe->width; }
-    virtual long           STDMETHODCALLTYPE GetHeight     (void)          { return _avframe->height; }
-    virtual long           STDMETHODCALLTYPE GetRowBytes   (void)          { return _avframe->linesize[0] < 0 ? -_avframe->linesize[0] : _avframe->linesize[0]; }
-    virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat(void)          { return bmdFormat8BitYUV; }
-    virtual BMDFrameFlags  STDMETHODCALLTYPE GetFlags      (void)          { return _avframe->linesize[0] < 0 ? bmdFrameFlagFlipVertical : bmdFrameFlagDefault; }
-    virtual HRESULT        STDMETHODCALLTYPE GetBytes      (void **buffer)
+    decklink_frame(struct decklink_ctx *ctx, AVFrame *avframe, AVCodecID codec_id, int height, int width) :
+        _ctx(ctx), _avframe(avframe), _avpacket(NULL), _codec_id(codec_id), _height(height), _width(width),  _refs(1) { }
+    decklink_frame(struct decklink_ctx *ctx, AVPacket *avpacket, AVCodecID codec_id, int height, int width) :
+        _ctx(ctx), _avframe(NULL), _avpacket(avpacket), _codec_id(codec_id), _height(height), _width(width), _refs(1) { }
+
+    virtual long           STDMETHODCALLTYPE GetWidth      (void)          { return _width; }
+    virtual long           STDMETHODCALLTYPE GetHeight     (void)          { return _height; }
+    virtual long           STDMETHODCALLTYPE GetRowBytes   (void)
+    {
+      if (_codec_id == AV_CODEC_ID_WRAPPED_AVFRAME)
+          return _avframe->linesize[0] < 0 ? -_avframe->linesize[0] : _avframe->linesize[0];
+      else
+          return ((GetWidth() + 47) / 48) * 128;
+    }
+    virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat(void)
     {
-        if (_avframe->linesize[0] < 0)
-            *buffer = (void *)(_avframe->data[0] + _avframe->linesize[0] * (_avframe->height - 1));
+        if (_codec_id == AV_CODEC_ID_WRAPPED_AVFRAME)
+            return bmdFormat8BitYUV;
         else
-            *buffer = (void *)(_avframe->data[0]);
+            return bmdFormat10BitYUV;
+    }
+    virtual BMDFrameFlags  STDMETHODCALLTYPE GetFlags      (void)
+    {
+       if (_codec_id == AV_CODEC_ID_WRAPPED_AVFRAME)
+           return _avframe->linesize[0] < 0 ? bmdFrameFlagFlipVertical : bmdFrameFlagDefault;
+       else
+           return bmdFrameFlagDefault;
+    }
+
+    virtual HRESULT        STDMETHODCALLTYPE GetBytes      (void **buffer)
+    {
+        if (_codec_id == AV_CODEC_ID_WRAPPED_AVFRAME) {
+            if (_avframe->linesize[0] < 0)
+                *buffer = (void *)(_avframe->data[0] + _avframe->linesize[0] * (_avframe->height - 1));
+            else
+                *buffer = (void *)(_avframe->data[0]);
+        } else {
+            *buffer = (void *)(_avpacket->data);
+        }
         return S_OK;
     }
 
@@ -71,6 +96,7 @@  public:
         int ret = --_refs;
         if (!ret) {
             av_frame_free(&_avframe);
+            av_packet_free(&_avpacket);
             delete this;
         }
         return ret;
@@ -78,6 +104,10 @@  public:
 
     struct decklink_ctx *_ctx;
     AVFrame *_avframe;
+    AVPacket *_avpacket;
+    AVCodecID _codec_id;
+    int _height;
+    int _width;
 
 private:
     std::atomic<int>  _refs;
@@ -90,9 +120,11 @@  public:
     {
         decklink_frame *frame = static_cast<decklink_frame *>(_frame);
         struct decklink_ctx *ctx = frame->_ctx;
-        AVFrame *avframe = frame->_avframe;
 
-        av_frame_unref(avframe);
+        if (frame->_avframe)
+            av_frame_unref(frame->_avframe);
+        if (frame->_avpacket)
+            av_packet_unref(frame->_avpacket);
 
         pthread_mutex_lock(&ctx->mutex);
         ctx->frames_buffer_available_spots++;
@@ -118,11 +150,18 @@  static int decklink_setup_video(AVFormatContext *avctx, AVStream *st)
         return -1;
     }
 
-    if (c->format != AV_PIX_FMT_UYVY422) {
-        av_log(avctx, AV_LOG_ERROR, "Unsupported pixel format!"
-               " Only AV_PIX_FMT_UYVY422 is supported.\n");
+    if (c->codec_id == AV_CODEC_ID_WRAPPED_AVFRAME) {
+        if (c->format != AV_PIX_FMT_UYVY422) {
+            av_log(avctx, AV_LOG_ERROR, "Unsupported pixel format!"
+                   " Only AV_PIX_FMT_UYVY422 is supported.\n");
+            return -1;
+        }
+    } else if (c->codec_id != AV_CODEC_ID_V210) {
+        av_log(avctx, AV_LOG_ERROR, "Unsupported codec type!"
+               " Only V210 and wrapped frame with AV_PIX_FMT_UYVY422 are supported.\n");
         return -1;
     }
+
     if (ff_decklink_set_format(avctx, c->width, c->height,
                             st->time_base.num, st->time_base.den, c->field_order)) {
         av_log(avctx, AV_LOG_ERROR, "Unsupported video size, framerate or field order!"
@@ -230,27 +269,42 @@  static int decklink_write_video_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 *avframe, *tmp = (AVFrame *)pkt->data;
+    AVStream *st = avctx->streams[pkt->stream_index];
+    AVFrame *avframe = NULL, *tmp = (AVFrame *)pkt->data;
+    AVPacket *avpacket = NULL;
     decklink_frame *frame;
     buffercount_type buffered;
     HRESULT hr;
 
-    if (tmp->format != AV_PIX_FMT_UYVY422 ||
-        tmp->width  != ctx->bmd_width ||
-        tmp->height != ctx->bmd_height) {
-        av_log(avctx, AV_LOG_ERROR, "Got a frame with invalid pixel format or dimension.\n");
-        return AVERROR(EINVAL);
-    }
-    avframe = av_frame_clone(tmp);
-    if (!avframe) {
-        av_log(avctx, AV_LOG_ERROR, "Could not clone video frame.\n");
-        return AVERROR(EIO);
+    if (st->codecpar->codec_id == AV_CODEC_ID_WRAPPED_AVFRAME) {
+        if (tmp->format != AV_PIX_FMT_UYVY422 ||
+            tmp->width  != ctx->bmd_width ||
+            tmp->height != ctx->bmd_height) {
+            av_log(avctx, AV_LOG_ERROR, "Got a frame with invalid pixel format or dimension.\n");
+            return AVERROR(EINVAL);
+        }
+
+        avframe = av_frame_clone(tmp);
+        if (!avframe) {
+            av_log(avctx, AV_LOG_ERROR, "Could not clone video frame.\n");
+            return AVERROR(EIO);
+        }
+
+        frame = new decklink_frame(ctx, avframe, st->codecpar->codec_id, avframe->height, avframe->width);
+    } else {
+        avpacket = av_packet_clone(pkt);
+        if (!avpacket) {
+            av_log(avctx, AV_LOG_ERROR, "Could not clone video frame.\n");
+            return AVERROR(EIO);
+        }
+
+        frame = new decklink_frame(ctx, avpacket, st->codecpar->codec_id, ctx->bmd_height, ctx->bmd_width);
     }
 
-    frame = new decklink_frame(ctx, avframe);
     if (!frame) {
         av_log(avctx, AV_LOG_ERROR, "Could not create new frame.\n");
         av_frame_free(&avframe);
+        av_packet_free(&avpacket);
         return AVERROR(EIO);
     }