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 + 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; }