aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorquietvoid <39477805+quietvoid@users.noreply.github.com>2024-01-27 07:16:33 -0500
committerquietvoid <39477805+quietvoid@users.noreply.github.com>2024-01-28 06:27:29 -0500
commit449abdce505f0e0952aac264daf0e9838c243786 (patch)
tree2569ccb0c0a104eb63282bdb1b580ba9449f2a6a
parenta8f286cb6e6fb7c5d74983a13c095f8faac489d1 (diff)
[Android] Allow removing HDR10+ dynamic HDR metadata
-rw-r--r--addons/resource.language.en_gb/resources/strings.po6
-rwxr-xr-xsystem/settings/settings.xml3
-rw-r--r--xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp4
-rw-r--r--xbmc/settings/Settings.h1
-rw-r--r--xbmc/utils/BitstreamConverter.cpp29
-rw-r--r--xbmc/utils/BitstreamConverter.h2
-rw-r--r--xbmc/utils/HevcSei.cpp64
-rw-r--r--xbmc/utils/HevcSei.h12
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);