diff options
author | quietvoid <39477805+quietvoid@users.noreply.github.com> | 2024-01-27 07:16:33 -0500 |
---|---|---|
committer | quietvoid <39477805+quietvoid@users.noreply.github.com> | 2024-01-28 06:27:29 -0500 |
commit | 449abdce505f0e0952aac264daf0e9838c243786 (patch) | |
tree | 2569ccb0c0a104eb63282bdb1b580ba9449f2a6a | |
parent | a8f286cb6e6fb7c5d74983a13c095f8faac489d1 (diff) |
[Android] Allow removing HDR10+ dynamic HDR metadata
-rw-r--r-- | addons/resource.language.en_gb/resources/strings.po | 6 | ||||
-rwxr-xr-x | system/settings/settings.xml | 3 | ||||
-rw-r--r-- | xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp | 4 | ||||
-rw-r--r-- | xbmc/settings/Settings.h | 1 | ||||
-rw-r--r-- | xbmc/utils/BitstreamConverter.cpp | 29 | ||||
-rw-r--r-- | xbmc/utils/BitstreamConverter.h | 2 | ||||
-rw-r--r-- | xbmc/utils/HevcSei.cpp | 64 | ||||
-rw-r--r-- | xbmc/utils/HevcSei.h | 12 |
8 files changed, 120 insertions, 1 deletions
diff --git a/addons/resource.language.en_gb/resources/strings.po b/addons/resource.language.en_gb/resources/strings.po index 43d66219b1..0c14482663 100644 --- a/addons/resource.language.en_gb/resources/strings.po +++ b/addons/resource.language.en_gb/resources/strings.po @@ -23783,6 +23783,12 @@ msgctxt "#39200" msgid "Dolby Vision" msgstr "" +#. Label of HDR10+ option for setting "Allowed HDR dynamic metadata formats" of label #39198 +#: system/settings/settings.xml +msgctxt "#39201" +msgid "HDR10+" +msgstr "" + # 40000 to 40800 are reserved for Video Versions feature #. Generic video versions label (plural) diff --git a/system/settings/settings.xml b/system/settings/settings.xml index d38de29414..45ee68e299 100755 --- a/system/settings/settings.xml +++ b/system/settings/settings.xml @@ -193,10 +193,11 @@ <setting id="videoplayer.allowedhdrformats" type="list[integer]" label="39198" help="39199"> <requirement>HAS_MEDIACODEC</requirement> <level>2</level> - <default>0</default> <!-- Allow all HDR formats --> + <default>0,1</default> <!-- Allow all HDR formats --> <constraints> <options> <option label="39200">0</option> <!-- Dolby Vision --> + <option label="39201">1</option> <!-- HDR10+ --> </options> <delimiter>,</delimiter> </constraints> diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp index d8c21558f7..0bc6305eaf 100644 --- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp +++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp @@ -505,6 +505,7 @@ bool CDVDVideoCodecAndroidMediaCodec::Open(CDVDStreamInfo &hints, CDVDCodecOptio const auto settings = CServiceBroker::GetSettingsComponent()->GetSettings(); bool convertDovi{false}; bool removeDovi{false}; + bool removeHdr10Plus{false}; if (settings) { @@ -515,6 +516,8 @@ bool CDVDVideoCodecAndroidMediaCodec::Open(CDVDStreamInfo &hints, CDVDCodecOptio settings->GetSetting(CSettings::SETTING_VIDEOPLAYER_ALLOWEDHDRFORMATS))); removeDovi = !CSettingUtils::FindIntInList( allowedHdrFormatsSetting, CSettings::VIDEOPLAYER_ALLOWED_HDR_TYPE_DOLBY_VISION); + removeHdr10Plus = !CSettingUtils::FindIntInList( + allowedHdrFormatsSetting, CSettings::VIDEOPLAYER_ALLOWED_HDR_TYPE_HDR10PLUS); } bool isDvhe = (m_hints.codec_tag == MKTAG('d', 'v', 'h', 'e')); @@ -605,6 +608,7 @@ bool CDVDVideoCodecAndroidMediaCodec::Open(CDVDStreamInfo &hints, CDVDCodecOptio if (m_bitstream) { m_bitstream->SetRemoveDovi(removeDovi); + m_bitstream->SetRemoveHdr10Plus(removeHdr10Plus); // Only set for profile 7, container hint allows to skip parsing unnecessarily if (m_hints.dovi.dv_profile == 7) diff --git a/xbmc/settings/Settings.h b/xbmc/settings/Settings.h index 346c062a88..bfc5e6072c 100644 --- a/xbmc/settings/Settings.h +++ b/xbmc/settings/Settings.h @@ -501,6 +501,7 @@ public: // values for SETTING_VIDEOPLAYER_ALLOWEDHDRFORMATS static const int VIDEOPLAYER_ALLOWED_HDR_TYPE_DOLBY_VISION = 0; + static const int VIDEOPLAYER_ALLOWED_HDR_TYPE_HDR10PLUS = 1; /*! \brief Creates a new settings wrapper around a new settings manager. diff --git a/xbmc/utils/BitstreamConverter.cpp b/xbmc/utils/BitstreamConverter.cpp index 270e00cccc..fe08a00a00 100644 --- a/xbmc/utils/BitstreamConverter.cpp +++ b/xbmc/utils/BitstreamConverter.cpp @@ -17,6 +17,7 @@ #include "BitstreamConverter.h" #include "BitstreamReader.h" #include "BitstreamWriter.h" +#include "HevcSei.h" #include <algorithm> @@ -357,6 +358,7 @@ CBitstreamConverter::CBitstreamConverter() m_start_decode = true; m_convert_dovi = false; m_removeDovi = false; + m_removeHdr10Plus = false; } CBitstreamConverter::~CBitstreamConverter() @@ -914,6 +916,8 @@ bool CBitstreamConverter::BitstreamConvert(uint8_t* pData, int iSize, uint8_t ** const DoviData* rpu_data = NULL; #endif + std::vector<uint8_t> finalPrefixSeiNalu; + switch (m_codec) { case AV_CODEC_ID_H264: @@ -971,6 +975,8 @@ bool CBitstreamConverter::BitstreamConvert(uint8_t* pData, int iSize, uint8_t ** const uint8_t* buf_to_write = buf; int32_t final_nal_size = nal_size; + bool containsHdr10Plus{false}; + if (!m_sps_pps_context.first_idr && IsSlice(unit_type)) { m_sps_pps_context.first_idr = 1; @@ -980,6 +986,26 @@ bool CBitstreamConverter::BitstreamConvert(uint8_t* pData, int iSize, uint8_t ** if (m_removeDovi && (unit_type == HEVC_NAL_UNSPEC62 || unit_type == HEVC_NAL_UNSPEC63)) write_buf = false; + // Try removing HDR10+ only if the NAL is big enough, optimization + if (m_removeHdr10Plus && unit_type == HEVC_NAL_SEI_PREFIX && nal_size >= 7) + { + std::tie(containsHdr10Plus, finalPrefixSeiNalu) = + CHevcSei::RemoveHdr10PlusFromSeiNalu(buf, nal_size); + + if (containsHdr10Plus) + { + if (!finalPrefixSeiNalu.empty()) + { + buf_to_write = finalPrefixSeiNalu.data(); + final_nal_size = finalPrefixSeiNalu.size(); + } + else + { + write_buf = false; + } + } + } + if (write_buf && m_convert_dovi) { if (unit_type == HEVC_NAL_UNSPEC62) @@ -1012,6 +1038,9 @@ bool CBitstreamConverter::BitstreamConvert(uint8_t* pData, int iSize, uint8_t ** rpu_data = NULL; } #endif + + if (containsHdr10Plus && !finalPrefixSeiNalu.empty()) + finalPrefixSeiNalu.clear(); } buf += nal_size; diff --git a/xbmc/utils/BitstreamConverter.h b/xbmc/utils/BitstreamConverter.h index 25e5d650ba..6548eae8c3 100644 --- a/xbmc/utils/BitstreamConverter.h +++ b/xbmc/utils/BitstreamConverter.h @@ -101,6 +101,7 @@ public: bool CanStartDecode() const; void SetConvertDovi(bool value) { m_convert_dovi = value; } void SetRemoveDovi(bool value) { m_removeDovi = value; } + void SetRemoveHdr10Plus(bool value) { m_removeHdr10Plus = value; } static bool mpeg2_sequence_header(const uint8_t *data, const uint32_t size, mpeg2_sequence *sequence); @@ -147,4 +148,5 @@ protected: bool m_start_decode; bool m_convert_dovi; bool m_removeDovi; + bool m_removeHdr10Plus; }; diff --git a/xbmc/utils/HevcSei.cpp b/xbmc/utils/HevcSei.cpp index 03cfc14916..996f9195ef 100644 --- a/xbmc/utils/HevcSei.cpp +++ b/xbmc/utils/HevcSei.cpp @@ -120,3 +120,67 @@ std::vector<CHevcSei> CHevcSei::ParseSeiRbspUnclearedEmulation(const uint8_t* in HevcClearStartCodeEmulationPrevention3Byte(inData, inDataLen, buf); return ParseSeiRbsp(buf.data(), buf.size()); } + +std::optional<const CHevcSei*> CHevcSei::FindHdr10PlusSeiMessage( + const std::vector<uint8_t>& buf, const std::vector<CHevcSei>& messages) +{ + for (const CHevcSei& sei : messages) + { + // User Data Registered ITU-T T.35 + if (sei.m_payloadType == 4 && sei.m_payloadSize >= 7) + { + CBitstreamReader br(buf.data() + sei.m_payloadOffset, sei.m_payloadSize); + const auto itu_t_t35_country_code = br.ReadBits(8); + const auto itu_t_t35_terminal_provider_code = br.ReadBits(16); + const auto itu_t_t35_terminal_provider_oriented_code = br.ReadBits(16); + + // United States, Samsung Electronics America, ST 2094-40 + if (itu_t_t35_country_code == 0xB5 && itu_t_t35_terminal_provider_code == 0x003C && + itu_t_t35_terminal_provider_oriented_code == 0x0001) + { + const auto application_identifier = br.ReadBits(8); + const auto application_version = br.ReadBits(8); + + if (application_identifier == 4 && application_version <= 1) + return &sei; + } + } + } + + return {}; +} + +std::pair<bool, const std::vector<uint8_t>> CHevcSei::RemoveHdr10PlusFromSeiNalu( + const uint8_t* inData, const size_t inDataLen) +{ + bool containsHdr10Plus{false}; + + std::vector<uint8_t> buf; + std::vector<CHevcSei> messages = CHevcSei::ParseSeiRbspUnclearedEmulation(inData, inDataLen, buf); + + if (auto res = CHevcSei::FindHdr10PlusSeiMessage(buf, messages)) + { + auto msg = *res; + + containsHdr10Plus = true; + if (messages.size() > 1) + { + // Multiple SEI messages in NALU, remove only the HDR10+ one + buf.erase(std::next(buf.begin(), msg->m_msgOffset), + std::next(buf.begin(), msg->m_payloadOffset + msg->m_payloadSize)); + HevcAddStartCodeEmulationPrevention3Byte(buf); + } + else + { + // Single SEI message in NALU + buf.clear(); + } + } + else + { + // No HDR10+ + buf.clear(); + } + + return std::make_pair(containsHdr10Plus, buf); +} diff --git a/xbmc/utils/HevcSei.h b/xbmc/utils/HevcSei.h index 08943afd54..7379c6924d 100644 --- a/xbmc/utils/HevcSei.h +++ b/xbmc/utils/HevcSei.h @@ -45,6 +45,18 @@ public: const size_t inDataLen, std::vector<uint8_t>& buf); + // Returns a HDR10+ SEI message if present in the list + static std::optional<const CHevcSei*> FindHdr10PlusSeiMessage( + const std::vector<uint8_t>& buf, const std::vector<CHevcSei>& messages); + + // Returns a pair with: + // 1) a bool for whether or not the NALU SEI payload contains a HDR10+ SEI message. + // 2) a vector of bytes: + // When not empty: the new NALU containing all but the HDR10+ SEI message. + // Otherwise: the NALU contained only one HDR10+ SEI and can be discarded. + static std::pair<bool, const std::vector<uint8_t>> RemoveHdr10PlusFromSeiNalu( + const uint8_t* inData, const size_t inDataLen); + private: // Parses single SEI message from the reader and pushes it to the list static int ParseSeiMessage(CBitstreamReader& br, std::vector<CHevcSei>& messages); |