Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
From: Devin Heitmueller <devin.heitmueller@ltnglobal.com>
To: ffmpeg-devel@ffmpeg.org
Cc: Devin Heitmueller <dheitmueller@ltnglobal.com>
Subject: [FFmpeg-devel] [RFC][PATCH 3/3] decklink: Add support for output of HDR metadata
Date: Fri, 21 Jul 2023 17:30:57 -0400
Message-ID: <1689975057-22226-4-git-send-email-dheitmueller@ltnglobal.com> (raw)
In-Reply-To: <1689975057-22226-1-git-send-email-dheitmueller@ltnglobal.com>

Add HDR support to the decklink output for cards that support such
functionality.  This includes setting the EOTF, the colorspace,
the mastering info, and the content light level info.  Both the
Payload Identification HANC data as well as the SMPTE ST 2108-1
VANC data are being set.

Tested with in-house content as well as samples from 4kmedia.org.
Testing was done with the Decklink 8K Pro and the Duo2 with 12.5.1
firmware, as well as with the Duo2 with 10.11.2 (before it supported
HDR) to ensure there are no regressions.

Signed-off-by: Devin Heitmueller <dheitmueller@ltnglobal.com>
---
 libavdevice/decklink_common.cpp |  12 +++
 libavdevice/decklink_common.h   |   2 +
 libavdevice/decklink_enc.cpp    | 204 ++++++++++++++++++++++++++++++++++++++--
 3 files changed, 211 insertions(+), 7 deletions(-)

diff --git a/libavdevice/decklink_common.cpp b/libavdevice/decklink_common.cpp
index 47de7ef..c1bcb82 100644
--- a/libavdevice/decklink_common.cpp
+++ b/libavdevice/decklink_common.cpp
@@ -251,6 +251,18 @@ int ff_decklink_set_configs(AVFormatContext *avctx,
         }
     }
 
+    DECKLINK_BOOL hdr_supported;
+    if (ctx->attr->GetFlag(BMDDeckLinkSupportsHDRMetadata, &hdr_supported) == S_OK) {
+        if (hdr_supported)
+            ctx->supports_hdr = 1;
+    }
+
+    DECKLINK_BOOL colorspace_supported;
+    if (ctx->attr->GetFlag(BMDDeckLinkSupportsColorspaceMetadata, &colorspace_supported) == S_OK) {
+        if (colorspace_supported)
+            ctx->supports_colorspace = 1;
+    }
+
     return 0;
 }
 
diff --git a/libavdevice/decklink_common.h b/libavdevice/decklink_common.h
index 34ab1b9..d50007f 100644
--- a/libavdevice/decklink_common.h
+++ b/libavdevice/decklink_common.h
@@ -109,6 +109,8 @@ struct decklink_ctx {
     int bmd_height;
     int bmd_field_dominance;
     int supports_vanc;
+    int supports_hdr;
+    int supports_colorspace;
 
     /* Capture buffer queue */
     DecklinkPacketQueue queue;
diff --git a/libavdevice/decklink_enc.cpp b/libavdevice/decklink_enc.cpp
index ffd0ad9..92d6bbe 100644
--- a/libavdevice/decklink_enc.cpp
+++ b/libavdevice/decklink_enc.cpp
@@ -35,6 +35,7 @@ extern "C" {
 #include "libavcodec/bytestream.h"
 #include "libavutil/internal.h"
 #include "libavutil/imgutils.h"
+#include "libavutil/mastering_display_metadata.h"
 #include "avdevice.h"
 }
 
@@ -47,13 +48,13 @@ extern "C" {
 #endif
 
 /* DeckLink callback class declaration */
-class decklink_frame : public IDeckLinkVideoFrame
+class decklink_frame : public IDeckLinkVideoFrame, public IDeckLinkVideoFrameMetadataExtensions
 {
 public:
     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), _ancillary(NULL), _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), _ancillary(NULL), _height(height), _width(width), _refs(1) { }
+        _ctx(ctx), _avframe(NULL), _avpacket(avpacket), _codec_id(codec_id), _ancillary(NULL), _height(height), _width(width), _colorspace(AVCOL_SPC_BT709), _eotf(AVCOL_TRC_BT709), hdr(NULL), lighting(NULL), _refs(1) { }
     virtual long           STDMETHODCALLTYPE GetWidth      (void)          { return _width; }
     virtual long           STDMETHODCALLTYPE GetHeight     (void)          { return _height; }
     virtual long           STDMETHODCALLTYPE GetRowBytes   (void)
@@ -72,10 +73,14 @@ public:
     }
     virtual BMDFrameFlags  STDMETHODCALLTYPE GetFlags      (void)
     {
-       if (_codec_id == AV_CODEC_ID_WRAPPED_AVFRAME)
-           return _avframe->linesize[0] < 0 ? bmdFrameFlagFlipVertical : bmdFrameFlagDefault;
-       else
-           return bmdFrameFlagDefault;
+        if (_codec_id == AV_CODEC_ID_WRAPPED_AVFRAME) {
+            return _avframe->linesize[0] < 0 ? bmdFrameFlagFlipVertical : bmdFrameFlagDefault;
+        } else {
+            if (_ctx->supports_hdr && (hdr || lighting))
+                return bmdFrameFlagDefault | bmdFrameContainsHDRMetadata;
+            else
+                return bmdFrameFlagDefault;
+        }
     }
 
     virtual HRESULT        STDMETHODCALLTYPE GetBytes      (void **buffer)
@@ -110,7 +115,176 @@ public:
         _ancillary->AddRef();
         return S_OK;
     }
-    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) { return E_NOINTERFACE; }
+
+    virtual HRESULT STDMETHODCALLTYPE SetMetadata(enum AVColorSpace colorspace, enum AVColorTransferCharacteristic eotf)
+    {
+        _colorspace = colorspace;
+        _eotf = eotf;
+        return S_OK;
+    }
+
+    // IDeckLinkVideoFrameMetadataExtensions interface
+    virtual HRESULT GetInt(BMDDeckLinkFrameMetadataID metadataID, int64_t* value)
+    {
+        HRESULT result = S_OK;
+
+        switch (metadataID) {
+        case bmdDeckLinkFrameMetadataHDRElectroOpticalTransferFunc:
+            /* See CTA-861-G Sec 6.9 Dynamic Range and Mastering */
+
+            switch(_eotf) {
+            case AVCOL_TRC_SMPTEST2084:
+                /* PQ */
+                *value = 2;
+               break;
+            case AVCOL_TRC_ARIB_STD_B67:
+                /* Also known as "HLG" */
+                *value = 3;
+                break;
+            case AVCOL_TRC_SMPTE170M:
+            case AVCOL_TRC_SMPTE240M:
+            case AVCOL_TRC_BT709:
+            default:
+                /* SDR */
+                *value = 0;
+               break;
+            }
+            break;
+
+        case bmdDeckLinkFrameMetadataColorspace:
+            if (!_ctx->supports_colorspace) {
+                result = E_NOTIMPL;
+                break;
+            }
+            switch(_colorspace) {
+            case AVCOL_SPC_BT470BG:
+            case AVCOL_SPC_SMPTE170M:
+            case AVCOL_SPC_SMPTE240M:
+                *value = bmdColorspaceRec601;
+                break;
+            case AVCOL_SPC_BT2020_CL:
+            case AVCOL_SPC_BT2020_NCL:
+                *value = bmdColorspaceRec2020;
+                break;
+            case AVCOL_SPC_BT709:
+            default:
+                *value = bmdColorspaceRec709;
+                break;
+            }
+            break;
+        default:
+            result = E_INVALIDARG;
+        }
+
+        return result;
+    }
+    virtual HRESULT GetFloat(BMDDeckLinkFrameMetadataID metadataID, double* value)
+    {
+        *value = 0;
+
+        switch (metadataID) {
+        case bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedX:
+            if (hdr && hdr->has_primaries)
+                *value = av_q2d(hdr->display_primaries[0][0]);
+            break;
+        case bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedY:
+            if (hdr && hdr->has_primaries)
+                *value = av_q2d(hdr->display_primaries[0][1]);
+            break;
+        case bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenX:
+            if (hdr && hdr->has_primaries)
+                *value = av_q2d(hdr->display_primaries[1][0]);
+            break;
+        case bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenY:
+            if (hdr && hdr->has_primaries)
+                *value = av_q2d(hdr->display_primaries[1][1]);
+            break;
+        case bmdDeckLinkFrameMetadataHDRDisplayPrimariesBlueX:
+            if (hdr && hdr->has_primaries)
+                *value = av_q2d(hdr->display_primaries[2][0]);
+            break;
+        case bmdDeckLinkFrameMetadataHDRDisplayPrimariesBlueY:
+            if (hdr && hdr->has_primaries)
+                *value = av_q2d(hdr->display_primaries[2][1]);
+            break;
+        case bmdDeckLinkFrameMetadataHDRWhitePointX:
+            if (hdr && hdr->has_primaries)
+                *value = av_q2d(hdr->white_point[0]);
+            break;
+        case bmdDeckLinkFrameMetadataHDRWhitePointY:
+            if (hdr && hdr->has_primaries)
+                *value = av_q2d(hdr->white_point[1]);
+            break;
+        case bmdDeckLinkFrameMetadataHDRMaxDisplayMasteringLuminance:
+            if (hdr && hdr->has_luminance)
+                *value = av_q2d(hdr->max_luminance);
+            break;
+        case bmdDeckLinkFrameMetadataHDRMinDisplayMasteringLuminance:
+            if (hdr && hdr->has_luminance)
+                *value = av_q2d(hdr->min_luminance);
+            break;
+        case bmdDeckLinkFrameMetadataHDRMaximumContentLightLevel:
+            if (lighting)
+                *value = (float) lighting->MaxCLL;
+            else
+                *value = 0;
+            break;
+        case bmdDeckLinkFrameMetadataHDRMaximumFrameAverageLightLevel:
+            if (lighting)
+                *value = (float) lighting->MaxFALL;
+            else
+                *value = 0;
+            break;
+        default:
+            return E_INVALIDARG;
+        }
+
+        return S_OK;
+    }
+
+    virtual HRESULT GetFlag(BMDDeckLinkFrameMetadataID metadataID, bool* value)
+    {
+        *value = false;
+        return E_INVALIDARG;
+    }
+    virtual HRESULT GetString(BMDDeckLinkFrameMetadataID metadataID, const char** value)
+    {
+        *value = nullptr;
+        return E_INVALIDARG;
+    }
+    virtual HRESULT GetBytes(BMDDeckLinkFrameMetadataID metadataID, void* buffer, uint32_t* bufferSize)
+    {
+        *bufferSize = 0;
+        return E_INVALIDARG;
+    }
+
+    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv)
+    {
+        CFUUIDBytes             iunknown;
+        HRESULT                 result          = S_OK;
+
+        if (!ppv)
+            return E_INVALIDARG;
+
+        *ppv = NULL;
+
+        iunknown = CFUUIDGetUUIDBytes(IUnknownUUID);
+        if (memcmp(&iid, &iunknown, sizeof(REFIID)) == 0) {
+            *ppv = this;
+            AddRef();
+        } else if (memcmp(&iid, &IID_IDeckLinkVideoFrame, sizeof(REFIID)) == 0) {
+            *ppv = static_cast<IDeckLinkVideoFrame*>(this);
+            AddRef();
+        } else if (memcmp(&iid, &IID_IDeckLinkVideoFrameMetadataExtensions, sizeof(REFIID)) == 0) {
+            *ppv = static_cast<IDeckLinkVideoFrameMetadataExtensions*>(this);
+            AddRef();
+        } else {
+            result = E_NOINTERFACE;
+        }
+
+        return result;
+    }
+
     virtual ULONG   STDMETHODCALLTYPE AddRef(void)                            { return ++_refs; }
     virtual ULONG   STDMETHODCALLTYPE Release(void)
     {
@@ -132,6 +306,10 @@ public:
     IDeckLinkVideoFrameAncillary *_ancillary;
     int _height;
     int _width;
+    enum AVColorSpace _colorspace;
+    enum AVColorTransferCharacteristic _eotf;
+    const AVMasteringDisplayMetadata *hdr;
+    const AVContentLightMetadata *lighting;
 
 private:
     std::atomic<int>  _refs;
@@ -726,6 +904,18 @@ static int decklink_write_video_packet(AVFormatContext *avctx, AVPacket *pkt)
         return AVERROR(EIO);
     }
 
+    /* Set frame metadata properties */
+    size_t size;
+    const AVMasteringDisplayMetadata *hdr = (const AVMasteringDisplayMetadata *) av_packet_get_side_data(pkt, AV_PKT_DATA_MASTERING_DISPLAY_METADATA, &size);
+    if (hdr && size > 0)
+        frame->hdr = hdr;
+
+    const AVContentLightMetadata *lighting = (const AVContentLightMetadata *) av_packet_get_side_data(pkt, AV_PKT_DATA_CONTENT_LIGHT_LEVEL, &size);
+    if (hdr && size > 0)
+        frame->lighting = lighting;
+
+    frame->SetMetadata(st->codecpar->color_space, st->codecpar->color_trc);
+
     /* Always keep at most one second of frames buffered. */
     pthread_mutex_lock(&ctx->mutex);
     while (ctx->frames_buffer_available_spots == 0) {
-- 
1.8.3.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

      parent reply	other threads:[~2023-07-21 21:31 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-07-21 21:30 [FFmpeg-devel] [RFC][PATCH 0/3] Add support for decklink HDR metadata output Devin Heitmueller
2023-07-21 21:30 ` [FFmpeg-devel] [RFC][PATCH 1/3] v210enc: Refactor side data passthrough Devin Heitmueller
2023-07-21 21:30 ` [FFmpeg-devel] [RFC][PATCH 2/3] v210enc: Add HDR metadata passthrough Devin Heitmueller
2023-07-21 21:30 ` Devin Heitmueller [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1689975057-22226-4-git-send-email-dheitmueller@ltnglobal.com \
    --to=devin.heitmueller@ltnglobal.com \
    --cc=dheitmueller@ltnglobal.com \
    --cc=ffmpeg-devel@ffmpeg.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git