From c79d987c4db172637160e410e4c1f49acdde57a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9drik=20Fuoco?= Date: Wed, 29 Oct 2025 08:43:16 -0400 Subject: [PATCH 1/2] test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédrik Fuoco --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 614a2f1e5..6e0a0ca0c 100644 --- a/README.md +++ b/README.md @@ -65,3 +65,5 @@ Also note that support for other *current* operating systems (typically, other L See [THIRD-PARTY.md](THIRD-PARTY.md) for license information about portions of Open RV that have been imported from other projects. +## Random change to test CI + From 48b42f014fbe01051696213d09d93af7a249c456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89lo=C3=AFse=20Brosseau?= <54746458+eloisebrosseau@users.noreply.github.com> Date: Wed, 29 Oct 2025 11:06:35 -0400 Subject: [PATCH 2/2] SG-37413: Update BMD DeckLink SDK to 15.0 (#925) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### [SG-37413](https://jira.autodesk.com/browse/SG-37413): Update BMD DeckLink SDK to 15.0 ### Summarize your change. Some important changes were made to the SDK, which means some refactoring in RV was necessary. All changes were made following the examples and instructions provided in the [14.3 Migration Guide](https://documents.blackmagicdesign.com/UserManuals/DeckLinkSDKMigrationGuide.pdf?_v=1732780810000): - [X] Replace bytesPerRow with the new available helper method `IDeckLinkOutput::RowBytesForPixelFormat` to calculate row bytes for pixel format - [X] Remove HDRVideoFrame and replace it with the new available interface `IDeckLinkVideoFrameMutableMetadataExtensions` directly in DeckLinkVideoFrame - [X] Migrate from IDeckLinkVideoFrame::GetBytes to access the underlying video frame buffer with the IDeckLinkVideoBuffer interface - [X] Refactor StereoVideoFrame to be use through a Provider class with the new method `IDeckLinkMutableVideoFrame::SetInterfaceProvider`. The provider object is able to provide access to the custom interface when its IUnknown::QueryInterface method is called. We now only need to define our StereoVideoFrame as a custom class of IDeckLinkVideoFrame3DExtensions ### Describe the reason for the change. RV should be able to use the latest version of the BMD SDK. ### Describe what you have tested and on which operating system. I tested broadcasting some media files from my macOS with the BMD UltraStudio 4K and BMD SmartScope Duo 4K monitor. --------- Signed-off-by: Éloïse Brosseau --- .../BlackMagicDevices/DeckLinkVideoDevice.h | 70 ++- .../BlackMagicDevices/HDRVideoFrame.h | 167 ------ .../BlackMagicDevices/StereoVideoFrame.h | 139 ++--- .../output/BlackMagicDevices/CMakeLists.txt | 9 +- .../BlackMagicDevices/DeckLinkVideoDevice.cpp | 554 ++++++++++++++---- .../BlackMagicDevices/HDRVideoFrame.cpp | 406 ------------- .../BlackMagicDevices/StereoVideoFrame.cpp | 206 +++---- 7 files changed, 651 insertions(+), 900 deletions(-) delete mode 100644 src/plugins/output/BlackMagicDevices/BlackMagicDevices/HDRVideoFrame.h delete mode 100644 src/plugins/output/BlackMagicDevices/HDRVideoFrame.cpp diff --git a/src/plugins/output/BlackMagicDevices/BlackMagicDevices/DeckLinkVideoDevice.h b/src/plugins/output/BlackMagicDevices/BlackMagicDevices/DeckLinkVideoDevice.h index 641808aa4..8284ff1cc 100644 --- a/src/plugins/output/BlackMagicDevices/BlackMagicDevices/DeckLinkVideoDevice.h +++ b/src/plugins/output/BlackMagicDevices/BlackMagicDevices/DeckLinkVideoDevice.h @@ -27,16 +27,10 @@ #include #endif -#if defined(PLATFORM_DARWIN) -#include -#endif - #include #include -#include #include #include -#include #include namespace BlackMagicDevices @@ -44,9 +38,9 @@ namespace BlackMagicDevices class BlackMagicModule; class PinnedMemoryAllocator; - typedef boost::mutex::scoped_lock ScopedLock; - typedef boost::mutex Mutex; - typedef boost::condition_variable Condition; + using ScopedLock = boost::mutex::scoped_lock; + using Mutex = boost::mutex; + using Condition = boost::condition_variable; struct DeckLinkDataFormat { @@ -78,8 +72,8 @@ namespace BlackMagicDevices const char* desc{nullptr}; }; - typedef std::vector DeckLinkVideoFormatVector; - typedef std::vector DeckLinkDataFormatVector; + using DeckLinkVideoFormatVector = std::vector; + using DeckLinkDataFormatVector = std::vector; struct DeckLinkVideo4KTransport { @@ -99,15 +93,16 @@ namespace BlackMagicDevices // Types // - typedef TwkUtil::Timer Timer; - typedef TwkGLF::GLFence GLFence; - typedef TwkGLF::GLFBO GLFBO; - typedef std::vector BufferVector; - typedef stl_ext::thread_group ThreadGroup; - typedef std::vector AudioBuffer; - typedef std::map StereoFrameMap; - typedef std::map HDRVideoFrameMap; - typedef std::deque DLVideoFrameDeque; + using Timer = TwkUtil::Timer; + using GLFence = TwkGLF::GLFence; + using GLFBO = TwkGLF::GLFBO; + using BufferVector = std::vector; + using ThreadGroup = stl_ext::thread_group; + using AudioBuffer = std::vector; + using StereoFrameMap = + std::map>; + using DLVideoFrameDeque = std::deque; struct PBOData { @@ -119,7 +114,7 @@ namespace BlackMagicDevices Ready }; - PBOData(GLuint g); + explicit PBOData(GLuint g); ~PBOData(); void lockData(); @@ -152,7 +147,29 @@ namespace BlackMagicDevices IDeckLinkVideoFrame* videoFrame{nullptr}; }; - typedef std::deque PBOQueue; + struct ChromaticityCoordinates + { + double RedX{0.0}; + double RedY{0.0}; + double GreenX{0.0}; + double GreenY{0.0}; + double BlueX{0.0}; + double BlueY{0.0}; + double WhiteX{0.0}; + double WhiteY{0.0}; + }; + + struct HDRMetadata + { + ChromaticityCoordinates referencePrimaries; + double minDisplayMasteringLuminance{0.0}; + double maxDisplayMasteringLuminance{0.0}; + double maxContentLightLevel{0.0}; + double maxFrameAverageLightLevel{0.0}; + int64_t electroOpticalTransferFunction{0}; + }; + + using PBOQueue = std::deque; // // Constructors @@ -259,7 +276,10 @@ namespace BlackMagicDevices IDeckLinkMutableVideoFrame*, IDeckLinkMutableVideoFrame*) const; void ScheduleFrame() const; - size_t bytesPerRow(BMDPixelFormat bmdFormat, size_t width) const; + + std::string dumpHDRMetadata() const; + bool parseHDRMetadata(const std::string& data); + void setHDRMetadataOnFrame(IDeckLinkVideoFrame* frame) const; private: DeckLinkVideoFormatVector m_decklinkVideoFormats; @@ -267,7 +287,7 @@ namespace BlackMagicDevices bool m_supportsStereo; mutable size_t m_firstThreeCounter; mutable IDeckLinkMutableVideoFrame* m_readyFrame; - mutable StereoVideoFrame* m_readyStereoFrame; + mutable StereoVideoFrame::Provider* m_readyStereoFrame; mutable DLVideoFrameDeque m_DLOutputVideoFrameQueue; mutable DLVideoFrameDeque m_DLReadbackVideoFrameQueue; // only rgb formats @@ -275,8 +295,8 @@ namespace BlackMagicDevices mutable bool m_hasAudio; mutable StereoFrameMap m_rightEyeToStereoFrameMap; // indexed by the left eye - mutable HDRVideoFrameMap m_FrameToHDRFrameMap; mutable PBOQueue m_pboQueue; + HDRMetadata s_hdrMetadata; IDeckLinkOutput* m_outputAPI; IDeckLink* m_deviceAPI; IDeckLinkConfiguration* m_configuration; diff --git a/src/plugins/output/BlackMagicDevices/BlackMagicDevices/HDRVideoFrame.h b/src/plugins/output/BlackMagicDevices/BlackMagicDevices/HDRVideoFrame.h deleted file mode 100644 index 04c05c826..000000000 --- a/src/plugins/output/BlackMagicDevices/BlackMagicDevices/HDRVideoFrame.h +++ /dev/null @@ -1,167 +0,0 @@ -/* -LICENSE-START- -** Copyright (c) 2024 Blackmagic Design -** -** Permission is hereby granted, free of charge, to any person or organization -** obtaining a copy of the software and accompanying documentation (the -** "Software") to use, reproduce, display, distribute, sub-license, execute, -** and transmit the Software, and to prepare derivative works of the Software, -** and to permit third-parties to whom the Software is furnished to do so, in -** accordance with: -** -** (1) if the Software is obtained from Blackmagic Design, the End User License -** Agreement for the Software Development Kit ("EULA") available at -** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or -** -** (2) if the Software is obtained from any third party, such licensing terms -** as notified by that third party, -** -** and all subject to the following: -** -** (3) the copyright notices in the Software and this entire statement, -** including the above license grant, this restriction and the following -** disclaimer, must be included in all copies of the Software, in whole or in -** part, and all derivative works of the Software, unless such copies or -** derivative works are solely in the form of machine-executable object code -** generated by a source language processor. -** -** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -** DEALINGS IN THE SOFTWARE. -** -** A copy of the Software is available free of charge at -** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA. -** -** -LICENSE-END- -*/ -// -// HDRVideoFrame.h -// - -#pragma once - -#include - -#ifdef PLATFORM_LINUX -typedef const char* IDeckLinkVideoFrameMetadataExtensions_GetStringValueType; -typedef bool IDeckLinkVideoFrameMetadataExtensions_GetFlagValueType; -#endif - -#ifdef PLATFORM_WINDOWS -typedef BSTR IDeckLinkVideoFrameMetadataExtensions_GetStringValueType; -typedef BOOL IDeckLinkVideoFrameMetadataExtensions_GetFlagValueType; -#endif - -#ifdef PLATFORM_DARWIN -typedef CFStringRef IDeckLinkVideoFrameMetadataExtensions_GetStringValueType; -typedef bool IDeckLinkVideoFrameMetadataExtensions_GetFlagValueType; -#endif - -#include -#include -#include -#include - -struct ChromaticityCoordinates -{ - double RedX{0.0}; - double RedY{0.0}; - double GreenX{0.0}; - double GreenY{0.0}; - double BlueX{0.0}; - double BlueY{0.0}; - double WhiteX{0.0}; - double WhiteY{0.0}; -}; - -struct HDRMetadata -{ - ChromaticityCoordinates referencePrimaries; - double minDisplayMasteringLuminance{0.0}; - double maxDisplayMasteringLuminance{0.0}; - double maxContentLightLevel{0.0}; - double maxFrameAverageLightLevel{0.0}; - int64_t electroOpticalTransferFunction{0}; -}; - -class HDRVideoFrame - : public IDeckLinkVideoFrame - , public IDeckLinkVideoFrameMetadataExtensions -{ -public: - HDRVideoFrame(IDeckLinkMutableVideoFrame* frame); - - virtual ~HDRVideoFrame() {} - - // IUnknown interface - virtual HRESULT QueryInterface(REFIID iid, LPVOID* ppv); - virtual ULONG AddRef(void); - virtual ULONG Release(void); - - // IDeckLinkVideoFrame interface - virtual long GetWidth(void) { return m_videoFrame->GetWidth(); } - - virtual long GetHeight(void) { return m_videoFrame->GetHeight(); } - - virtual long GetRowBytes(void) { return m_videoFrame->GetRowBytes(); } - - virtual BMDPixelFormat GetPixelFormat(void) - { - return m_videoFrame->GetPixelFormat(); - } - - virtual BMDFrameFlags GetFlags(void) - { - return m_videoFrame->GetFlags() | bmdFrameContainsHDRMetadata; - } - - virtual HRESULT GetBytes(void** buffer) - { - return m_videoFrame->GetBytes(buffer); - } - - virtual HRESULT GetTimecode(BMDTimecodeFormat format, - IDeckLinkTimecode** timecode) - { - return m_videoFrame->GetTimecode(format, timecode); - } - - virtual HRESULT GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary) - { - return m_videoFrame->GetAncillaryData(ancillary); - } - - // IDeckLinkVideoFrameMetadataExtensions interface - virtual HRESULT GetInt(BMDDeckLinkFrameMetadataID metadataID, - int64_t* value); - virtual HRESULT GetFloat(BMDDeckLinkFrameMetadataID metadataID, - double* value); - virtual HRESULT - GetFlag(BMDDeckLinkFrameMetadataID metadataID, - IDeckLinkVideoFrameMetadataExtensions_GetFlagValueType* value); - virtual HRESULT - GetString(BMDDeckLinkFrameMetadataID metadataID, - IDeckLinkVideoFrameMetadataExtensions_GetStringValueType* value); - virtual HRESULT GetBytes(BMDDeckLinkFrameMetadataID metadataID, - void* buffer, uint32_t* bufferSize); - - static std::string DumpHDRMetadata(); - - static void SetHDRMetadata(const HDRMetadata& metadata) - { - s_metadata = metadata; - } - - // Set the HDR metadata after parsing the HDR metadata passed as comma - // separated values in a string Returns false if parsing error, true if - // sucessful - static bool SetHDRMetadata(const std::string& data); - -private: - IDeckLinkMutableVideoFrame* m_videoFrame; - static HDRMetadata s_metadata; - std::atomic m_refCount; -}; diff --git a/src/plugins/output/BlackMagicDevices/BlackMagicDevices/StereoVideoFrame.h b/src/plugins/output/BlackMagicDevices/BlackMagicDevices/StereoVideoFrame.h index 0d19a553b..348da1652 100644 --- a/src/plugins/output/BlackMagicDevices/BlackMagicDevices/StereoVideoFrame.h +++ b/src/plugins/output/BlackMagicDevices/BlackMagicDevices/StereoVideoFrame.h @@ -1,42 +1,7 @@ -/* -LICENSE-START- -** Copyright (c) 2024 Blackmagic Design -** -** Permission is hereby granted, free of charge, to any person or organization -** obtaining a copy of the software and accompanying documentation (the -** "Software") to use, reproduce, display, distribute, sub-license, execute, -** and transmit the Software, and to prepare derivative works of the Software, -** and to permit third-parties to whom the Software is furnished to do so, in -** accordance with: -** -** (1) if the Software is obtained from Blackmagic Design, the End User License -** Agreement for the Software Development Kit ("EULA") available at -** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or -** -** (2) if the Software is obtained from any third party, such licensing terms -** as notified by that third party, -** -** and all subject to the following: -** -** (3) the copyright notices in the Software and this entire statement, -** including the above license grant, this restriction and the following -** disclaimer, must be included in all copies of the Software, in whole or in -** part, and all derivative works of the Software, unless such copies or -** derivative works are solely in the form of machine-executable object code -** generated by a source language processor. -** -** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -** DEALINGS IN THE SOFTWARE. -** -** A copy of the Software is available free of charge at -** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA. -** -** -LICENSE-END- -*/ +// +// Copyright (C) 2025 Autodesk, Inc. All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 // // StereoVideoFrame.h // Signal Generator @@ -51,64 +16,88 @@ typedef __int32 int32_t; #endif #include +#include /* * An example class which may be used to output a frame or pair of frames to * a 3D capable output. * - * This class implements the IDeckLinkVideoFrame interface which can - * be used to operate on the left frame. + * This class implements the IDeckLinkVideoFrame3DExtensions interface which can + * be used to operate on the left frame following the BMD provider pattern + * + * The Provider class manages the relationship between the video frame and + * the 3D extensions, and is associated with the video frame using + * SetInterfaceProvider(). * * Access to the right frame through the IDeckLinkVideoFrame3DExtensions * interface: * - * IDeckLinkVideoFrame *rightEyeFrame; - * hr = threeDimensionalFrame->GetFrameForRightEye(&rightEyeFrame); + * IDeckLinkVideoFrame3DExtensions *threeDimensionalFrame; + * result = leftEyeFrame->QueryInterface(IID_IDeckLinkVideoFrame3DExtensions, + * reinterpret_cast(&threeDimensionalFrame); + * result = threeDimensionalFrame->GetFrameForRightEye(&rightEyeFrame); * * After which IDeckLinkVideoFrame operations are performed directly * on the rightEyeFrame object. */ -class StereoVideoFrame - : public IDeckLinkVideoFrame - , public IDeckLinkVideoFrame3DExtensions +class StereoVideoFrame : public IDeckLinkVideoFrame3DExtensions { public: - typedef boost::mutex::scoped_lock ScopedLock; - typedef boost::mutex Mutex; - typedef boost::condition_variable Condition; + using ScopedLock = boost::mutex::scoped_lock; + using Mutex = boost::mutex; + using Condition = boost::condition_variable; + + virtual ~StereoVideoFrame(); + + StereoVideoFrame(const StereoVideoFrame&) = delete; + StereoVideoFrame& operator=(const StereoVideoFrame&) = delete; + StereoVideoFrame(StereoVideoFrame&&) = delete; + StereoVideoFrame& operator=(StereoVideoFrame&&) = delete; // IUnknown methods - virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID* ppv); - virtual ULONG STDMETHODCALLTYPE AddRef(void); - virtual ULONG STDMETHODCALLTYPE Release(void); - - // IDeckLinkVideoFrame methods - virtual long STDMETHODCALLTYPE GetWidth(void); - virtual long STDMETHODCALLTYPE GetHeight(void); - virtual long STDMETHODCALLTYPE GetRowBytes(void); - virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat(void); - virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags(void); - virtual HRESULT STDMETHODCALLTYPE GetBytes(/* out */ void** buffer); - - virtual HRESULT STDMETHODCALLTYPE - GetTimecode(/* in */ BMDTimecodeFormat format, - /* out */ IDeckLinkTimecode** timecode); - virtual HRESULT STDMETHODCALLTYPE - GetAncillaryData(/* out */ IDeckLinkVideoFrameAncillary** ancillary); + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID* ppv) override; + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; // IDeckLinkVideoFrame3DExtensions methods - virtual BMDVideo3DPackingFormat STDMETHODCALLTYPE Get3DPackingFormat(void); - virtual HRESULT STDMETHODCALLTYPE - GetFrameForRightEye(/* out */ IDeckLinkVideoFrame** rightEyeFrame); + BMDVideo3DPackingFormat STDMETHODCALLTYPE Get3DPackingFormat() override; + HRESULT STDMETHODCALLTYPE + GetFrameForRightEye(/* out */ IDeckLinkVideoFrame** rightEyeFrame) override; - StereoVideoFrame(IDeckLinkMutableVideoFrame* left, - IDeckLinkMutableVideoFrame* right = 0); - virtual ~StereoVideoFrame(); + class Provider : public IUnknown + { + public: + Provider(IDeckLinkMutableVideoFrame* parent, + IDeckLinkMutableVideoFrame* right); + virtual ~Provider(); + + Provider(const Provider&) = delete; + Provider& operator=(const Provider&) = delete; + Provider(Provider&&) = delete; + Provider& operator=(Provider&&) = delete; + + // IUnknown methods + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, + LPVOID* ppv) override; + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; + + IDeckLinkVideoFrame* GetLeftFrame() const { return m_parentFrame; } + + private: + IDeckLinkMutableVideoFrame* m_parentFrame; + IDeckLinkMutableVideoFrame* m_rightFrame; + std::atomic m_refCount; + }; + +private: + friend class Provider; + + StereoVideoFrame(IDeckLinkMutableVideoFrame* owner, + IDeckLinkMutableVideoFrame* right); -protected: IDeckLinkMutableVideoFrame* m_frameLeft; IDeckLinkMutableVideoFrame* m_frameRight; - int32_t m_refCount; - Mutex m_refMutex; + std::atomic m_refCount; }; diff --git a/src/plugins/output/BlackMagicDevices/CMakeLists.txt b/src/plugins/output/BlackMagicDevices/CMakeLists.txt index dbe7580a4..847fe9e04 100644 --- a/src/plugins/output/BlackMagicDevices/CMakeLists.txt +++ b/src/plugins/output/BlackMagicDevices/CMakeLists.txt @@ -4,7 +4,6 @@ # SPDX-License-Identifier: Apache-2.0 # - IF(NOT RV_DEPS_BMD_DECKLINK_SDK_ZIP_PATH) # Warning message if the Blackmagic Decklink SDK path is not specified should already have been reported in bmd.cmake RETURN() @@ -15,13 +14,7 @@ SET(_target ) SET(_sources - BlackMagicModule.cpp - DeckLinkVideoDevice.cpp - BlackMagicOutput.cpp - HDRVideoFrame.cpp - StereoVideoFrame.cpp - DeckLinkSDKAPIDispatch.cpp - DeckLinkProfileCallback.cpp + BlackMagicModule.cpp DeckLinkVideoDevice.cpp BlackMagicOutput.cpp StereoVideoFrame.cpp DeckLinkSDKAPIDispatch.cpp DeckLinkProfileCallback.cpp ) ADD_LIBRARY( diff --git a/src/plugins/output/BlackMagicDevices/DeckLinkVideoDevice.cpp b/src/plugins/output/BlackMagicDevices/DeckLinkVideoDevice.cpp index 3964f8fa2..143c5d75e 100644 --- a/src/plugins/output/BlackMagicDevices/DeckLinkVideoDevice.cpp +++ b/src/plugins/output/BlackMagicDevices/DeckLinkVideoDevice.cpp @@ -18,6 +18,9 @@ #include // This has to come before the ByteSwap.h #include #include +#include +#include +#include #include #include #include @@ -37,6 +40,39 @@ namespace #define BM_CHECK(T) checkForFalse(T, __LINE__); + HRESULT getVideoFrameBuffer(IDeckLinkMutableVideoFrame* frame, + void** buffer) + { + IDeckLinkVideoBuffer* videoBuffer = nullptr; + HRESULT result = frame->QueryInterface( + IID_IDeckLinkVideoBuffer, reinterpret_cast(&videoBuffer)); + + if (result != S_OK) + { + return result; + } + + result = videoBuffer->StartAccess(bmdBufferAccessReadAndWrite); + if (result != S_OK) + { + videoBuffer->Release(); + return result; + } + + result = videoBuffer->GetBytes(buffer); + + result = videoBuffer->EndAccess(bmdBufferAccessReadAndWrite); + if (result != S_OK) + { + videoBuffer->Release(); + return result; + } + + videoBuffer->Release(); + + return result; + } + } // namespace namespace BlackMagicDevices @@ -884,33 +920,28 @@ namespace BlackMagicDevices if (m_infoFeedback) { - cout << "INFO: BMD HDR Metadata input = " << hdrMetadata - << endl; + std::cout << "INFO: BMD HDR Metadata input = " << hdrMetadata + << '\n'; } - // Parse the HDR metadata provided and save it as a static in the - // HDRVideoFrame class + // Parse the HDR metadata provided // Note that the same HDR metadata will be used for all the video // frames in the queue - bool parsingSuccessful = HDRVideoFrame::SetHDRMetadata(hdrMetadata); - if (parsingSuccessful) + bool parsingSuccessful = parseHDRMetadata(hdrMetadata); + if (parsingSuccessful && m_infoFeedback) { - if (m_infoFeedback) - { - cout << "INFO: BMD HDR Metadata parsing successful =" - << endl - << HDRVideoFrame::DumpHDRMetadata() << endl; - } + std::cout << "INFO: BMD HDR Metadata parsing successful =\n" + << dumpHDRMetadata() << '\n'; } else { m_useHDRMetadata = false; if (m_infoFeedback) { - cout << "INFO: BMD HDR Metadata parsing error. HDR " - "metadata will not " - "be used" - << endl; + std::cout << "INFO: BMD HDR Metadata parsing error. HDR " + "metadata will not " + "be used" + << '\n'; } } } @@ -919,8 +950,8 @@ namespace BlackMagicDevices m_firstThreeCounter = 0; m_frameCount = 0; m_totalPlayoutFrames = 0; - m_lastPboData = NULL; - m_secondLastPboData = NULL; + m_lastPboData = nullptr; + m_secondLastPboData = nullptr; IDeckLinkDisplayModeIterator* pDLDisplayModeIterator = NULL; IDeckLinkDisplayMode* pDLDisplayMode = NULL; @@ -1100,9 +1131,19 @@ namespace BlackMagicDevices IDeckLinkMutableVideoFrame* outputFrame = nullptr; const DeckLinkDataFormat& dataFormat = m_decklinkDataFormats[m_internalDataFormat]; + + int32_t frameRowBytes = 0; + HRESULT hr = deckLinkOutput()->RowBytesForPixelFormat( + dataFormat.value, static_cast(m_frameWidth), + &frameRowBytes); + if (hr != S_OK) + { + throw runtime_error( + "Cannot get the row bytes for the pixel format"); + } if (m_outputAPI->CreateVideoFrame( - m_frameWidth, m_frameHeight, - bytesPerRow(dataFormat.value, m_frameWidth), + static_cast(m_frameWidth), + static_cast(m_frameHeight), frameRowBytes, dataFormat.value, bmdFrameFlagFlipVertical, &outputFrame) != S_OK) { @@ -1146,25 +1187,23 @@ namespace BlackMagicDevices { for (int i = 1; i < m_DLOutputVideoFrameQueue.size(); i += 2) { - m_rightEyeToStereoFrameMap[m_DLOutputVideoFrameQueue.at(i)] = - new StereoVideoFrame(m_DLOutputVideoFrameQueue.at(i - 1), - m_DLOutputVideoFrameQueue.at(i)); - } - } + auto stereoVideoFrameProvider = + std::make_unique( + m_DLOutputVideoFrameQueue.at(i - 1), + m_DLOutputVideoFrameQueue.at(i)); + + HRESULT result = + m_DLOutputVideoFrameQueue.at(i - 1)->SetInterfaceProvider( + IID_IDeckLinkVideoFrame3DExtensions, + stereoVideoFrameProvider.get()); + if (result != S_OK) + { + throw runtime_error("Failed to set stereo interface " + "provider on left frame."); + } - // - // Create the HDRVideoFrames - // Note that no video frame memory will be allocated here, only a COM - // object structure. An HDRVideoFrame is a COM object implementing the - // following COM interfaces: IUnknown, IDeckLinkVideoFrame, and - // IDeckLinkVideoFrameMetadataExtensions - // - if (m_useHDRMetadata) - { - for (int i = 0; i < m_DLOutputVideoFrameQueue.size(); i++) - { - m_FrameToHDRFrameMap[m_DLOutputVideoFrameQueue.at(i)] = - new HDRVideoFrame(m_DLOutputVideoFrameQueue.at(i)); + m_rightEyeToStereoFrameMap[m_DLOutputVideoFrameQueue.at(i)] = + std::move(stereoVideoFrameProvider); } } @@ -1192,7 +1231,8 @@ namespace BlackMagicDevices m_readyFrame = m_DLOutputVideoFrameQueue.at(0); m_readyStereoFrame = - m_rightEyeToStereoFrameMap[m_DLOutputVideoFrameQueue.at(1)]; + m_rightEyeToStereoFrameMap[m_DLOutputVideoFrameQueue.at(1)].get(); + m_rightEyeToStereoFrameMap[m_DLOutputVideoFrameQueue.at(1)].get(); m_open = true; } @@ -1239,25 +1279,8 @@ namespace BlackMagicDevices if (m_stereo) { - StereoFrameMap::iterator it; - for (it = m_rightEyeToStereoFrameMap.begin(); - it != m_rightEyeToStereoFrameMap.end(); ++it) - { - delete it->second; - } m_rightEyeToStereoFrameMap.clear(); } - - if (m_useHDRMetadata) - { - HDRVideoFrameMap::iterator it; - for (it = m_FrameToHDRFrameMap.begin(); - it != m_FrameToHDRFrameMap.end(); ++it) - { - delete it->second; - } - m_FrameToHDRFrameMap.clear(); - } } TwkGLF::GLBindableVideoDevice::close(); @@ -1328,7 +1351,8 @@ namespace BlackMagicDevices else if (n == 1) // in case of stereo, ready frame is updated when right // eye is transfered { - m_readyStereoFrame = m_rightEyeToStereoFrameMap[outputVideoFrame]; + m_readyStereoFrame = + m_rightEyeToStereoFrameMap[outputVideoFrame].get(); } return true; @@ -1380,10 +1404,16 @@ namespace BlackMagicDevices void* p = (void*)glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB); TWK_GLDEBUG; - if (p) + + if (p != nullptr) { - void* pFrame; - outputVideoFrame->GetBytes(&pFrame); + void* pFrame = nullptr; + HRESULT result = getVideoFrameBuffer(outputVideoFrame, &pFrame); + if (result != S_OK) + { + throw std::runtime_error( + "Failed to get video frame buffer"); + } if (d.iformat >= VideoDevice::Y0CbY1Cr_8_422) { @@ -1503,14 +1533,24 @@ namespace BlackMagicDevices { HOP_PROF_FUNC(); - void* pFrame; + void* pFrame = nullptr; if (m_needsFrameConverter) { - readbackVideoFrame->GetBytes(&pFrame); + HRESULT result = getVideoFrameBuffer(readbackVideoFrame, &pFrame); + if (result != S_OK) + { + throw std::runtime_error( + "Failed to get readback video frame buffer"); + } } else { - outputVideoFrame->GetBytes(&pFrame); + HRESULT result = getVideoFrameBuffer(outputVideoFrame, &pFrame); + if (result != S_OK) + { + throw std::runtime_error( + "Failed to get output video frame buffer"); + } } const DeckLinkDataFormat& d = @@ -1531,8 +1571,13 @@ namespace BlackMagicDevices TwkUtil::Timer timer; timer.start(); - void* outData; - outputVideoFrame->GetBytes(&outData); + void* outData = nullptr; + HRESULT result = getVideoFrameBuffer(outputVideoFrame, &outData); + if (result != S_OK) + { + throw std::runtime_error( + "Failed to get output video frame buffer for conversion"); + } if (d.iformat == VideoDevice::Y0CbY1Cr_8_422) { subsample422_8bit_UYVY_MP(m_frameWidth, m_frameHeight, @@ -1734,26 +1779,26 @@ namespace BlackMagicDevices if (isOpen()) { IDeckLinkVideoFrame* outputVideoFrame = - m_stereo ? (IDeckLinkVideoFrame*)m_readyStereoFrame + m_stereo ? m_readyStereoFrame->GetLeftFrame() : (IDeckLinkVideoFrame*)m_readyFrame; - if (m_useHDRMetadata) + if (m_useHDRMetadata && (outputVideoFrame != nullptr)) { - outputVideoFrame = - (IDeckLinkVideoFrame*)m_FrameToHDRFrameMap[m_readyFrame]; + setHDRMetadataOnFrame(outputVideoFrame); } - if (outputVideoFrame) + if (outputVideoFrame != nullptr) { - // Prevent undesirable audio artifacts when repeating the same - // frame Note that some conditions might prevent RV from - // fetching a new frame in time. In this case the same frame has - // to be sent to the output. Repeating one frame has a minimal - // impact on the review session. However repeating the same - // audio snippet might cause an undesirable audio artifact. This - // is what we want to prevent here. Example of such a condition: - // when adding media to a sequence while a playback is already - // in progress for example, + // Prevent undesirable audio artifacts when repeating the + // same frame Note that some conditions might prevent RV + // from fetching a new frame in time. In this case the same + // frame has to be sent to the output. Repeating one frame + // has a minimal impact on the review session. However + // repeating the same audio snippet might cause an + // undesirable audio artifact. This is what we want to + // prevent here. Example of such a condition: when adding + // media to a sequence while a playback is already in + // progress for example, bool skipAudioIfAny = false; static IDeckLinkVideoFrame* previouslyScheduledOutputVideoFrame = nullptr; @@ -1797,26 +1842,6 @@ namespace BlackMagicDevices } } - size_t DeckLinkVideoDevice::bytesPerRow(BMDPixelFormat bmdFormat, - size_t width) const - { - switch (bmdFormat) - { - case bmdFormat8BitYUV: - return m_frameWidth * 16 / 8; - case bmdFormat8BitBGRA: - return m_frameWidth * 32 / 8; - case bmdFormat10BitYUV: - return ((m_frameWidth + 47) / 48) * 128; - case bmdFormat10BitRGBXLE: - return ((m_frameWidth + 63) / 64) * 256; - case bmdFormat12BitRGBLE: - return (m_frameWidth * 36) / 8; - default: - TWK_THROW_EXC_STREAM("Unknown pixel format."); - } - } - HRESULT DeckLinkVideoDevice::ScheduledFrameCompleted( IDeckLinkVideoFrame* completedFrame, @@ -1849,4 +1874,335 @@ namespace BlackMagicDevices return S_OK; } + std::string DeckLinkVideoDevice::dumpHDRMetadata() const + { + std::ostringstream hdrMetedata; + + hdrMetedata << "redPrimaryX: " + << s_hdrMetadata.referencePrimaries.RedX << '\n' + << "redPrimaryY: " + << s_hdrMetadata.referencePrimaries.RedY << '\n' + << "greenPrimaryX: " + << s_hdrMetadata.referencePrimaries.GreenX << '\n' + << "greenPrimaryY: " + << s_hdrMetadata.referencePrimaries.GreenY << '\n' + << "bluePrimaryX: " + << s_hdrMetadata.referencePrimaries.BlueX << '\n' + << "bluePrimaryY: " + << s_hdrMetadata.referencePrimaries.BlueY << '\n' + << "whitePointX: " + << s_hdrMetadata.referencePrimaries.WhiteX << '\n' + << "whitePointY: " + << s_hdrMetadata.referencePrimaries.WhiteY << '\n' + << "minMasteringLuminance: " + << s_hdrMetadata.minDisplayMasteringLuminance << '\n' + << "maxMasteringLuminance: " + << s_hdrMetadata.maxDisplayMasteringLuminance << '\n' + << "maxContentLightLevel: " + << s_hdrMetadata.maxContentLightLevel << '\n' + << "maxFrameAverageLightLevel: " + << s_hdrMetadata.maxFrameAverageLightLevel << '\n' + << "electroOpticalTransferFunction: " + << s_hdrMetadata.electroOpticalTransferFunction << '\n'; + return hdrMetedata.str(); + } + + bool DeckLinkVideoDevice::parseHDRMetadata(const std::string& data) + { + // Very, very basic and brutal parsing routine for the hdr + // argument string. + // No error checking, no validation, no plan B, data must be + // exactly as expected or it won't work. + // + // Sample values : + // redPrimaryX: 0.708000004 + // redPrimaryY: 0.291999996 + // greenPrimaryX: 0.170000002 + // greenPrimaryY: 0.79699999 + // bluePrimaryX: 0.130999997 + // bluePrimaryY: 0.0460000001 + // whitePointX: 0.312700003 + // whitePointY: 0.328999996 + // minMasteringLuminance: 0.00499999989 + // maxMasteringLuminance: 10000.0 + // maxContentLightLevel: 0.0 + // maxFrameAverageLightLevel: 0.0 + // electroOpticalTransferFunction: 2 + // + // Expected format (to be entered in the "Additional Options" + // box of the Video preferences panel): + // + // --hdmi-hdr-metadata=0.708000004,0.291999996,0.170000002,0.79699999,0.130999997,0.0460000001,0.312700003,0.328999996,0.00499999989,10000.0,0.0,0.0,2 + // + // or via env var: + // + // setenv TWK_BLACKMAGIC_HDMI_HDR_METADATA + // "0.708000004,0.291999996,0.170000002,0.79699999,0.130999997,0.0460000001,0.312700003,0.328999996,0.00499999989,10000.0,0.0,0.0,2" + // + // (the types - float vs int - are important!) + + size_t pos0 = 0; + size_t pos1; + + // redPrimaryX + pos1 = data.find(",", pos0); + if (pos1 == std::string::npos) + return false; + s_hdrMetadata.referencePrimaries.RedX = + atof(data.substr(pos0, pos1 - pos0).c_str()); + pos0 = pos1 + 1; + + // redPrimaryY + pos1 = data.find(",", pos0); + if (pos1 == std::string::npos) + return false; + s_hdrMetadata.referencePrimaries.RedY = + atof(data.substr(pos0, pos1 - pos0).c_str()); + pos0 = pos1 + 1; + + // greenPrimaryX + pos1 = data.find(",", pos0); + if (pos1 == std::string::npos) + return false; + s_hdrMetadata.referencePrimaries.GreenX = + atof(data.substr(pos0, pos1 - pos0).c_str()); + pos0 = pos1 + 1; + + // greenPrimaryY + pos1 = data.find(",", pos0); + if (pos1 == std::string::npos) + return false; + s_hdrMetadata.referencePrimaries.GreenY = + atof(data.substr(pos0, pos1 - pos0).c_str()); + pos0 = pos1 + 1; + + // bluePrimaryX + pos1 = data.find(",", pos0); + if (pos1 == std::string::npos) + return false; + s_hdrMetadata.referencePrimaries.BlueX = + atof(data.substr(pos0, pos1 - pos0).c_str()); + pos0 = pos1 + 1; + + // bluePrimaryY + pos1 = data.find(",", pos0); + if (pos1 == std::string::npos) + return false; + s_hdrMetadata.referencePrimaries.BlueY = + atof(data.substr(pos0, pos1 - pos0).c_str()); + pos0 = pos1 + 1; + + // whitePointX + pos1 = data.find(",", pos0); + if (pos1 == std::string::npos) + return false; + s_hdrMetadata.referencePrimaries.WhiteX = + atof(data.substr(pos0, pos1 - pos0).c_str()); + pos0 = pos1 + 1; + + // whitePointY + pos1 = data.find(",", pos0); + if (pos1 == std::string::npos) + return false; + s_hdrMetadata.referencePrimaries.WhiteY = + atof(data.substr(pos0, pos1 - pos0).c_str()); + pos0 = pos1 + 1; + + // minMasteringLuminance + pos1 = data.find(",", pos0); + if (pos1 == std::string::npos) + return false; + s_hdrMetadata.minDisplayMasteringLuminance = + atof(data.substr(pos0, pos1 - pos0).c_str()); + pos0 = pos1 + 1; + + // maxMasteringLuminance + pos1 = data.find(",", pos0); + if (pos1 == std::string::npos) + return false; + s_hdrMetadata.maxDisplayMasteringLuminance = + atof(data.substr(pos0, pos1 - pos0).c_str()); + pos0 = pos1 + 1; + + // maxContentLightLevel + pos1 = data.find(",", pos0); + if (pos1 == std::string::npos) + return false; + s_hdrMetadata.maxContentLightLevel = + atof(data.substr(pos0, pos1 - pos0).c_str()); + pos0 = pos1 + 1; + + // maxFrameAverageLightLevel + pos1 = data.find(",", pos0); + if (pos1 == std::string::npos) + return false; + s_hdrMetadata.maxFrameAverageLightLevel = + atof(data.substr(pos0, pos1 - pos0).c_str()); + pos0 = pos1 + 1; + + // electroOpticalTransferFunction (last value, no comma) + s_hdrMetadata.electroOpticalTransferFunction = + atoll(data.substr(pos0).c_str()); + + return true; + } + + void + DeckLinkVideoDevice::setHDRMetadataOnFrame(IDeckLinkVideoFrame* frame) const + { + if (!frame || !m_useHDRMetadata) + return; + + IDeckLinkVideoFrameMutableMetadataExtensions* metadataExtensions = + nullptr; + auto result = frame->QueryInterface( + IID_IDeckLinkVideoFrameMutableMetadataExtensions, + reinterpret_cast(&metadataExtensions)); + + if (result != S_OK || metadataExtensions == nullptr) + { + if (m_infoFeedback) + { + cout << "WARNING: Failed to query HDR metadata extensions " + "interface\n"; + } + return; + } + + // Set colorspace to Rec.2020 + result = metadataExtensions->SetInt(bmdDeckLinkFrameMetadataColorspace, + bmdColorspaceRec2020); + if (result != S_OK && m_infoFeedback) + { + std::cout << "WARNING: Failed to set colorspace metadata\n"; + } + + // Set EOTF + result = metadataExtensions->SetInt( + bmdDeckLinkFrameMetadataHDRElectroOpticalTransferFunc, + s_hdrMetadata.electroOpticalTransferFunction); + + if (result != S_OK && m_infoFeedback) + { + std::cout << "WARNING: Failed to set EOTF metadata\n"; + } + + // Set display primaries + result = metadataExtensions->SetFloat( + bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedX, + s_hdrMetadata.referencePrimaries.RedX); + + if (result != S_OK && m_infoFeedback) + { + std::cout << "WARNING: Failed to set primaries red X metadata\n"; + } + + result = metadataExtensions->SetFloat( + bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedY, + s_hdrMetadata.referencePrimaries.RedY); + + if (result != S_OK && m_infoFeedback) + { + std::cout << "WARNING: Failed to set primaries red Y metadata\n"; + } + + result = metadataExtensions->SetFloat( + bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenX, + s_hdrMetadata.referencePrimaries.GreenX); + + if (result != S_OK && m_infoFeedback) + { + std::cout << "WARNING: Failed to set primaries green X metadata\n"; + } + + result = metadataExtensions->SetFloat( + bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenY, + s_hdrMetadata.referencePrimaries.GreenY); + + if (result != S_OK && m_infoFeedback) + { + std::cout << "WARNING: Failed to set primaries green Y metadata\n"; + } + + result = metadataExtensions->SetFloat( + bmdDeckLinkFrameMetadataHDRDisplayPrimariesBlueX, + s_hdrMetadata.referencePrimaries.BlueX); + + if (result != S_OK && m_infoFeedback) + { + std::cout << "WARNING: Failed to set primaries blue X metadata\n"; + } + + result = metadataExtensions->SetFloat( + bmdDeckLinkFrameMetadataHDRDisplayPrimariesBlueY, + s_hdrMetadata.referencePrimaries.BlueY); + + if (result != S_OK && m_infoFeedback) + { + std::cout << "WARNING: Failed to set primaries blue Y metadata\n"; + } + + // Set white point + result = metadataExtensions->SetFloat( + bmdDeckLinkFrameMetadataHDRWhitePointX, + s_hdrMetadata.referencePrimaries.WhiteX); + + if (result != S_OK && m_infoFeedback) + { + std::cout << "WARNING: Failed to set primaries white X metadata\n"; + } + + result = metadataExtensions->SetFloat( + bmdDeckLinkFrameMetadataHDRWhitePointY, + s_hdrMetadata.referencePrimaries.WhiteY); + + if (result != S_OK && m_infoFeedback) + { + std::cout << "WARNING: Failed to set primaries white Y metadata\n"; + } + + // Set luminance levels + result = metadataExtensions->SetFloat( + bmdDeckLinkFrameMetadataHDRMaxDisplayMasteringLuminance, + s_hdrMetadata.maxDisplayMasteringLuminance); + + if (result != S_OK && m_infoFeedback) + { + std::cout << "WARNING: Failed to set max display mastering " + "luminance metadata\n"; + } + + result = metadataExtensions->SetFloat( + bmdDeckLinkFrameMetadataHDRMinDisplayMasteringLuminance, + s_hdrMetadata.minDisplayMasteringLuminance); + + if (result != S_OK && m_infoFeedback) + { + std::cout << "WARNING: Failed to set min display mastering " + "luminance metadata\n"; + } + + result = metadataExtensions->SetFloat( + bmdDeckLinkFrameMetadataHDRMaximumContentLightLevel, + s_hdrMetadata.maxContentLightLevel); + + if (result != S_OK && m_infoFeedback) + { + std::cout << "WARNING: Failed to set maximum content light level " + "metadata\n"; + } + + result = metadataExtensions->SetFloat( + bmdDeckLinkFrameMetadataHDRMaximumFrameAverageLightLevel, + s_hdrMetadata.maxFrameAverageLightLevel); + + if (result != S_OK && m_infoFeedback) + { + std::cout << "WARNING: Failed to set maximum frame average light " + "level metadata\n"; + } + + metadataExtensions->Release(); + } + } // namespace BlackMagicDevices diff --git a/src/plugins/output/BlackMagicDevices/HDRVideoFrame.cpp b/src/plugins/output/BlackMagicDevices/HDRVideoFrame.cpp deleted file mode 100644 index 0b97e0e64..000000000 --- a/src/plugins/output/BlackMagicDevices/HDRVideoFrame.cpp +++ /dev/null @@ -1,406 +0,0 @@ -/* -LICENSE-START- -** Copyright (c) 2024 Blackmagic Design -** -** Permission is hereby granted, free of charge, to any person or organization -** obtaining a copy of the software and accompanying documentation (the -** "Software") to use, reproduce, display, distribute, sub-license, execute, -** and transmit the Software, and to prepare derivative works of the Software, -** and to permit third-parties to whom the Software is furnished to do so, in -** accordance with: -** -** (1) if the Software is obtained from Blackmagic Design, the End User License -** Agreement for the Software Development Kit ("EULA") available at -** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or -** -** (2) if the Software is obtained from any third party, such licensing terms -** as notified by that third party, -** -** and all subject to the following: -** -** (3) the copyright notices in the Software and this entire statement, -** including the above license grant, this restriction and the following -** disclaimer, must be included in all copies of the Software, in whole or in -** part, and all derivative works of the Software, unless such copies or -** derivative works are solely in the form of machine-executable object code -** generated by a source language processor. -** -** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -** DEALINGS IN THE SOFTWARE. -** -** A copy of the Software is available free of charge at -** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA. -** -** -LICENSE-END- -*/ - -// Note: This class is a derivative of a class bearing the same name provided -// by Blackmagic Design as an example of how to add HDR metadata support for -// broadcast. -// This is why the Blackmagic coppyright notice haas been included to comply -// with the terms of the copyright. - -// -// HDRVideoFrame.cpp -// - -#include - -#include -#include - -HDRMetadata HDRVideoFrame::s_metadata = HDRMetadata(); - -HDRVideoFrame::HDRVideoFrame(IDeckLinkMutableVideoFrame* frame) - : m_videoFrame(frame) - , m_refCount(1) -{ -} - -/// IUnknown methods - -HRESULT HDRVideoFrame::QueryInterface(REFIID iid, LPVOID* ppv) -{ - HRESULT result = S_OK; - -#ifdef PLATFORM_WINDOWS - REFIID iunknown = IID_IUnknown; -#else - CFUUIDBytes iunknown = CFUUIDGetUUIDBytes(IUnknownUUID); -#endif - - // Initialise the return result - *ppv = nullptr; - - if (memcmp(&iid, &iunknown, sizeof(REFIID)) == 0) - { - *ppv = this; - AddRef(); - } - else if (memcmp(&iid, &IID_IDeckLinkVideoFrame, sizeof(REFIID)) == 0) - { - *ppv = static_cast(this); - AddRef(); - } - else if (memcmp(&iid, &IID_IDeckLinkVideoFrameMetadataExtensions, - sizeof(REFIID)) - == 0) - { - *ppv = static_cast(this); - AddRef(); - } - else - { - result = E_NOINTERFACE; - } - - return result; -} - -ULONG HDRVideoFrame::AddRef(void) { return ++m_refCount; } - -ULONG HDRVideoFrame::Release(void) -{ - ULONG refCount = --m_refCount; - if (refCount == 0) - { - delete this; - return 0; - } - return refCount; -} - -/// IDeckLinkVideoFrameMetadataExtensions methods - -HRESULT HDRVideoFrame::GetInt(BMDDeckLinkFrameMetadataID metadataID, - int64_t* value) -{ - HRESULT result = S_OK; - - switch (metadataID) - { - case bmdDeckLinkFrameMetadataHDRElectroOpticalTransferFunc: - *value = s_metadata.electroOpticalTransferFunction; - break; - - case bmdDeckLinkFrameMetadataColorspace: - *value = bmdColorspaceRec2020; - break; - - default: - value = nullptr; - result = E_INVALIDARG; - } - - return result; -} - -HRESULT HDRVideoFrame::GetFloat(BMDDeckLinkFrameMetadataID metadataID, - double* value) -{ - HRESULT result = S_OK; - - switch (metadataID) - { - case bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedX: - *value = s_metadata.referencePrimaries.RedX; - break; - - case bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedY: - *value = s_metadata.referencePrimaries.RedY; - break; - - case bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenX: - *value = s_metadata.referencePrimaries.GreenX; - break; - - case bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenY: - *value = s_metadata.referencePrimaries.GreenY; - break; - - case bmdDeckLinkFrameMetadataHDRDisplayPrimariesBlueX: - *value = s_metadata.referencePrimaries.BlueX; - break; - - case bmdDeckLinkFrameMetadataHDRDisplayPrimariesBlueY: - *value = s_metadata.referencePrimaries.BlueY; - break; - - case bmdDeckLinkFrameMetadataHDRWhitePointX: - *value = s_metadata.referencePrimaries.WhiteX; - break; - - case bmdDeckLinkFrameMetadataHDRWhitePointY: - *value = s_metadata.referencePrimaries.WhiteY; - break; - - case bmdDeckLinkFrameMetadataHDRMaxDisplayMasteringLuminance: - *value = s_metadata.maxDisplayMasteringLuminance; - break; - - case bmdDeckLinkFrameMetadataHDRMinDisplayMasteringLuminance: - *value = s_metadata.minDisplayMasteringLuminance; - break; - - case bmdDeckLinkFrameMetadataHDRMaximumContentLightLevel: - *value = s_metadata.maxContentLightLevel; - break; - - case bmdDeckLinkFrameMetadataHDRMaximumFrameAverageLightLevel: - *value = s_metadata.maxFrameAverageLightLevel; - break; - - default: - value = nullptr; - result = E_INVALIDARG; - } - - return result; -} - -HRESULT HDRVideoFrame::GetFlag( - BMDDeckLinkFrameMetadataID metadataID, - IDeckLinkVideoFrameMetadataExtensions_GetFlagValueType* value) -{ - // Not expecting GetFlag - return E_INVALIDARG; -} - -HRESULT HDRVideoFrame::GetString( - BMDDeckLinkFrameMetadataID metadataID, - IDeckLinkVideoFrameMetadataExtensions_GetStringValueType* value) -{ - // Not expecting GetString - return E_INVALIDARG; -} - -HRESULT HDRVideoFrame::GetBytes(BMDDeckLinkFrameMetadataID metadataID, - void* buffer, uint32_t* bufferSize) -{ - // Not expecting GetString - return E_INVALIDARG; -} - -std::string HDRVideoFrame::DumpHDRMetadata() -{ - std::ostringstream hdrMetedata; - - hdrMetedata << "redPrimaryX: " - << s_metadata.referencePrimaries.RedX << std::endl - << "redPrimaryY: " - << s_metadata.referencePrimaries.RedY << std::endl - << "greenPrimaryX: " - << s_metadata.referencePrimaries.GreenX << std::endl - << "greenPrimaryY: " - << s_metadata.referencePrimaries.GreenY << std::endl - << "bluePrimaryX: " - << s_metadata.referencePrimaries.BlueX << std::endl - << "bluePrimaryY: " - << s_metadata.referencePrimaries.BlueY << std::endl - << "whitePointX: " - << s_metadata.referencePrimaries.WhiteX << std::endl - << "whitePointY: " - << s_metadata.referencePrimaries.WhiteY << std::endl - << "minMasteringLuminance: " - << s_metadata.minDisplayMasteringLuminance << std::endl - << "maxMasteringLuminance: " - << s_metadata.maxDisplayMasteringLuminance << std::endl - << "maxContentLightLevel: " - << s_metadata.maxContentLightLevel << std::endl - << "maxFrameAverageLightLevel: " - << s_metadata.maxFrameAverageLightLevel << std::endl - << "electroOpticalTransferFunction: " - << s_metadata.electroOpticalTransferFunction << std::endl; - - return hdrMetedata.str(); -} - -bool HDRVideoFrame::SetHDRMetadata(const std::string& data) -{ - // Very, very basic and brutal parsing routine for the hdr - // argument string. - // No error checking, no validation, no plan B, data must be - // exactly as expected or it won't work. - // - // Sample values : - // redPrimaryX: 0.708000004 - // redPrimaryY: 0.291999996 - // greenPrimaryX: 0.170000002 - // greenPrimaryY: 0.79699999 - // bluePrimaryX: 0.130999997 - // bluePrimaryY: 0.0460000001 - // whitePointX: 0.312700003 - // whitePointY: 0.328999996 - // minMasteringLuminance: 0.00499999989 - // maxMasteringLuminance: 10000.0 - // maxContentLightLevel: 0.0 - // maxFrameAverageLightLevel: 0.0 - // electroOpticalTransferFunction: 2 - // - // Expected format (to be entered in the "Additional Options" - // box of the Video preferences panel): - // - // --hdmi-hdr-metadata=0.708000004,0.291999996,0.170000002,0.79699999,0.130999997,0.0460000001,0.312700003,0.328999996,0.00499999989,10000.0,0.0,0.0,2 - // - // or via env var: - // - // setenv TWK_BLACKMAGIC_HDMI_HDR_METADATA - // "0.708000004,0.291999996,0.170000002,0.79699999,0.130999997,0.0460000001,0.312700003,0.328999996,0.00499999989,10000.0,0.0,0.0,2" - // - // (the types - float vs int - are important!) - - // redPrimaryX - size_t pos0 = 0; - size_t pos1 = data.find(",", pos0); - if (std::string::npos == pos1) - return false; - s_metadata.referencePrimaries.RedX = - atof(data.substr(pos0, pos1 - pos0).c_str()); - pos0 = pos1 + 1; - - // redPrimaryY - pos1 = data.find(",", pos0); - if (std::string::npos == pos1) - return false; - s_metadata.referencePrimaries.RedY = - atof(data.substr(pos0, pos1 - pos0).c_str()); - pos0 = pos1 + 1; - - // greenPrimaryX - pos1 = data.find(",", pos0); - if (std::string::npos == pos1) - return false; - s_metadata.referencePrimaries.GreenX = - atof(data.substr(pos0, pos1 - pos0).c_str()); - pos0 = pos1 + 1; - - // greenPrimaryY - pos1 = data.find(",", pos0); - if (std::string::npos == pos1) - return false; - s_metadata.referencePrimaries.GreenY = - atof(data.substr(pos0, pos1 - pos0).c_str()); - pos0 = pos1 + 1; - - // bluePrimaryX - pos1 = data.find(",", pos0); - if (std::string::npos == pos1) - return false; - s_metadata.referencePrimaries.BlueX = - atof(data.substr(pos0, pos1 - pos0).c_str()); - pos0 = pos1 + 1; - - // bluePrimaryY - pos1 = data.find(",", pos0); - if (std::string::npos == pos1) - return false; - s_metadata.referencePrimaries.BlueY = - atof(data.substr(pos0, pos1 - pos0).c_str()); - pos0 = pos1 + 1; - - // whitePointX - pos1 = data.find(",", pos0); - if (std::string::npos == pos1) - return false; - s_metadata.referencePrimaries.WhiteX = - atof(data.substr(pos0, pos1 - pos0).c_str()); - pos0 = pos1 + 1; - - // whitePointY - pos1 = data.find(",", pos0); - if (std::string::npos == pos1) - return false; - s_metadata.referencePrimaries.WhiteY = - atof(data.substr(pos0, pos1 - pos0).c_str()); - pos0 = pos1 + 1; - - // minMasteringLuminance - pos1 = data.find(",", pos0); - if (std::string::npos == pos1) - return false; - s_metadata.minDisplayMasteringLuminance = - atof(data.substr(pos0, pos1 - pos0).c_str()); - pos0 = pos1 + 1; - - // maxMasteringLuminance - pos1 = data.find(",", pos0); - if (std::string::npos == pos1) - return false; - s_metadata.maxDisplayMasteringLuminance = - atof(data.substr(pos0, pos1 - pos0).c_str()); - pos0 = pos1 + 1; - - // maxContentLightLevel - pos1 = data.find(",", pos0); - if (std::string::npos == pos1) - return false; - s_metadata.maxContentLightLevel = - atof(data.substr(pos0, pos1 - pos0).c_str()); - pos0 = pos1 + 1; - - // maxFrameAverageLightLevel - pos1 = data.find(",", pos0); - if (std::string::npos == pos1) - return false; - s_metadata.maxFrameAverageLightLevel = - atof(data.substr(pos0, pos1 - pos0).c_str()); - pos0 = pos1 + 1; - - // electroOpticalTransferFunction - pos1 = data.find(",", pos0); - if (pos1 != std::string::npos) - { - // Note : it is ok to pass npos for the length if there is no more - // commas However, if there is another comma then we'll adjust the - // length accordingly - pos1 -= pos0; - } - s_metadata.electroOpticalTransferFunction = - atoi(data.substr(pos0, pos1).c_str()); - - return true; -} diff --git a/src/plugins/output/BlackMagicDevices/StereoVideoFrame.cpp b/src/plugins/output/BlackMagicDevices/StereoVideoFrame.cpp index 6490bf7ff..95454dfec 100644 --- a/src/plugins/output/BlackMagicDevices/StereoVideoFrame.cpp +++ b/src/plugins/output/BlackMagicDevices/StereoVideoFrame.cpp @@ -1,42 +1,8 @@ -/* -LICENSE-START- -** Copyright (c) 2024 Blackmagic Design -** -** Permission is hereby granted, free of charge, to any person or organization -** obtaining a copy of the software and accompanying documentation (the -** "Software") to use, reproduce, display, distribute, sub-license, execute, -** and transmit the Software, and to prepare derivative works of the Software, -** and to permit third-parties to whom the Software is furnished to do so, in -** accordance with: -** -** (1) if the Software is obtained from Blackmagic Design, the End User License -** Agreement for the Software Development Kit ("EULA") available at -** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or -** -** (2) if the Software is obtained from any third party, such licensing terms -** as notified by that third party, -** -** and all subject to the following: -** -** (3) the copyright notices in the Software and this entire statement, -** including the above license grant, this restriction and the following -** disclaimer, must be included in all copies of the Software, in whole or in -** part, and all derivative works of the Software, unless such copies or -** derivative works are solely in the form of machine-executable object code -** generated by a source language processor. -** -** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -** DEALINGS IN THE SOFTWARE. -** -** A copy of the Software is available free of charge at -** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA. -** -** -LICENSE-END- -*/ +// +// Copyright (C) 2025 Autodesk, Inc. All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// // // StereoVideoFrame.cpp // Signal Generator @@ -44,22 +10,66 @@ #include #include -#include +#include #define CompareREFIID(iid1, iid2) (memcmp(&iid1, &iid2, sizeof(REFIID)) == 0) -StereoVideoFrame::StereoVideoFrame(IDeckLinkMutableVideoFrame* left, +StereoVideoFrame::Provider::Provider(IDeckLinkMutableVideoFrame* parent, + IDeckLinkMutableVideoFrame* right) + : m_parentFrame(parent) + , m_rightFrame(right) + , m_refCount(1) +{ + if (m_parentFrame == nullptr) + { + throw std::invalid_argument("At least a left frame should be defined"); + } + if (m_rightFrame != nullptr) + { + m_rightFrame->AddRef(); + } +} + +StereoVideoFrame::Provider::~Provider() +{ + if (m_rightFrame != nullptr) + { + m_rightFrame->Release(); + } +} + +HRESULT StereoVideoFrame::Provider::QueryInterface(REFIID iid, LPVOID* ppv) +{ + return (new StereoVideoFrame(m_parentFrame, m_rightFrame)) + ->QueryInterface(iid, ppv); +} + +ULONG StereoVideoFrame::Provider::AddRef() { return ++m_refCount; } + +ULONG +StereoVideoFrame::Provider::Release() +{ + ULONG refCount = --m_refCount; + if (refCount == 0) + { + delete this; + } + return refCount; +} + +StereoVideoFrame::StereoVideoFrame(IDeckLinkMutableVideoFrame* owner, IDeckLinkMutableVideoFrame* right) - : m_frameLeft(left) + : m_frameLeft(owner) , m_frameRight(right) , m_refCount(1) { - if (!m_frameLeft) + if (m_frameLeft == nullptr) { - throw std::invalid_argument("at minimum a left frame must be defined"); + throw std::invalid_argument("At minimum a left frame must be defined"); } + m_frameLeft->AddRef(); - if (m_frameRight) + if (m_frameRight != nullptr) { m_frameRight->AddRef(); } @@ -67,8 +77,14 @@ StereoVideoFrame::StereoVideoFrame(IDeckLinkMutableVideoFrame* left, StereoVideoFrame::~StereoVideoFrame() { - // do not release the left right frames. the frames will be freed later by - // DeckLinkVideoDevice + if (m_frameLeft != nullptr) + { + m_frameLeft->Release(); + } + if (m_frameRight != nullptr) + { + m_frameRight->Release(); + } } HRESULT StereoVideoFrame::QueryInterface(REFIID iid, LPVOID* ppv) @@ -79,92 +95,42 @@ HRESULT StereoVideoFrame::QueryInterface(REFIID iid, LPVOID* ppv) REFIID iunknown = IID_IUnknown; #endif - if (CompareREFIID(iid, iunknown)) - *ppv = static_cast(this); - else if (CompareREFIID(iid, IID_IDeckLinkVideoFrame)) - *ppv = static_cast(this); - else if (CompareREFIID(iid, IID_IDeckLinkVideoFrame3DExtensions)) - *ppv = static_cast(this); - else + if (CompareREFIID(iid, iunknown) + || CompareREFIID(iid, IID_IDeckLinkVideoFrame3DExtensions)) { - *ppv = NULL; - return E_NOINTERFACE; + *ppv = static_cast(this); + AddRef(); + return S_OK; } - AddRef(); - return S_OK; + *ppv = nullptr; + return E_NOINTERFACE; } -ULONG StereoVideoFrame::AddRef(void) -{ -#ifdef PLATFORM_WINDOWS - return _InterlockedIncrement((volatile long*)&m_refCount); -#else - ScopedLock lock(m_refMutex); - m_refCount++; - return m_refCount; -#endif -} +ULONG StereoVideoFrame::AddRef() { return ++m_refCount; } -ULONG StereoVideoFrame::Release(void) +ULONG StereoVideoFrame::Release() { -#ifdef PLATFORM_WINDOWS - ULONG newRefValue = _InterlockedDecrement((volatile long*)&m_refCount); - - if (!newRefValue) + ULONG refCount = --m_refCount; + if (refCount == 0) + { delete this; - return newRefValue; -#else - ScopedLock lock(m_refMutex); - m_refCount--; - return m_refCount; -#endif -} - -long StereoVideoFrame::GetWidth(void) { return m_frameLeft->GetWidth(); } - -long StereoVideoFrame::GetHeight(void) { return m_frameLeft->GetHeight(); } - -long StereoVideoFrame::GetRowBytes(void) { return m_frameLeft->GetRowBytes(); } - -BMDPixelFormat StereoVideoFrame::GetPixelFormat(void) -{ - return m_frameLeft->GetPixelFormat(); -} - -BMDFrameFlags StereoVideoFrame::GetFlags(void) -{ - return m_frameLeft->GetFlags(); -} - -HRESULT StereoVideoFrame::GetBytes(/* out */ void** buffer) -{ - return m_frameLeft->GetBytes(buffer); -} - -HRESULT StereoVideoFrame::GetTimecode(/* in */ BMDTimecodeFormat format, - /* out */ IDeckLinkTimecode** timecode) -{ - return m_frameLeft->GetTimecode(format, timecode); -} - -HRESULT StereoVideoFrame::GetAncillaryData( - /* out */ IDeckLinkVideoFrameAncillary** ancillary) -{ - return m_frameLeft->GetAncillaryData(ancillary); + } + return refCount; } -BMDVideo3DPackingFormat StereoVideoFrame::Get3DPackingFormat(void) +BMDVideo3DPackingFormat StereoVideoFrame::Get3DPackingFormat() { return bmdVideo3DPackingLeftOnly; } -HRESULT StereoVideoFrame::GetFrameForRightEye( - /* out */ IDeckLinkVideoFrame** rightEyeFrame) +HRESULT +StereoVideoFrame::GetFrameForRightEye(IDeckLinkVideoFrame** rightEyeFrame) { - if (m_frameRight) - return m_frameRight->QueryInterface(IID_IDeckLinkVideoFrame, - (void**)rightEyeFrame); - else - return S_FALSE; + if (m_frameRight != nullptr) + { + return m_frameRight->QueryInterface( + IID_IDeckLinkVideoFrame, reinterpret_cast(rightEyeFrame)); + } + return S_FALSE; }