Skip to content

Commit

Permalink
[Android] Allow removing HDR10+ dynamic HDR metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
quietvoid committed Jan 27, 2024
1 parent 55dfb8a commit 3c034f1
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 1 deletion.
6 changes: 6 additions & 0 deletions addons/resource.language.en_gb/resources/strings.po
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion system/settings/settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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'));
Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions xbmc/settings/Settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,7 @@ class CSettings : public CSettingsBase, public CSettingCreator, public CSettingC

// 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.
Expand Down
29 changes: 29 additions & 0 deletions xbmc/utils/BitstreamConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "BitstreamConverter.h"
#include "BitstreamReader.h"
#include "BitstreamWriter.h"
#include "HevcSei.h"

#include <algorithm>

Expand Down Expand Up @@ -357,6 +358,7 @@ CBitstreamConverter::CBitstreamConverter()
m_start_decode = true;
m_convert_dovi = false;
m_remove_dovi = false;
m_remove_hdr10plus = false;
}

CBitstreamConverter::~CBitstreamConverter()
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand All @@ -980,6 +986,26 @@ bool CBitstreamConverter::BitstreamConvert(uint8_t* pData, int iSize, uint8_t **
if (m_remove_dovi && (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_remove_hdr10plus && 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)
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions xbmc/utils/BitstreamConverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ class CBitstreamConverter
bool CanStartDecode() const;
void SetConvertDovi(bool value) { m_convert_dovi = value; }
void SetRemoveDovi(bool value) { m_remove_dovi = value; }
void SetRemoveHdr10Plus(bool value) { m_remove_hdr10plus = value; }

static bool mpeg2_sequence_header(const uint8_t *data, const uint32_t size, mpeg2_sequence *sequence);

Expand Down Expand Up @@ -147,4 +148,5 @@ class CBitstreamConverter
bool m_start_decode;
bool m_convert_dovi;
bool m_remove_dovi;
bool m_remove_hdr10plus;
};
63 changes: 63 additions & 0 deletions xbmc/utils/HevcSei.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,66 @@ const std::vector<CHevcSei> CHevcSei::ParseSeiRbspUnclearedEmulation(const uint8
hevc_clear_start_code_emulation_prevention_3_byte(in_data, in_data_len, buf);
return ParseSeiRbsp(buf.data(), buf.size());
}

const std::optional<const CHevcSei*> CHevcSei::FindHdr10PlusSeiMessage(
const std::vector<uint8_t>& buf, const std::vector<CHevcSei>& messages)
{
for (const CHevcSei& sei : messages)
{
if (sei.payload_type == 4 && sei.payload_size >= 7)
{
CBitstreamReader br(buf.data() + sei.payload_offset, sei.payload_size);
auto itu_t_t35_country_code = br.ReadBits(8);
auto itu_t_t35_terminal_provider_code = br.ReadBits(16);
auto itu_t_t35_terminal_provider_oriented_code = br.ReadBits(16);

if (itu_t_t35_country_code == 0xB5 && itu_t_t35_terminal_provider_code == 0x003C &&
itu_t_t35_terminal_provider_oriented_code == 0x0001)
{
auto application_identifier = br.ReadBits(8);
auto application_version = br.ReadBits(8);

if (application_identifier == 4 && application_version <= 1)
return &sei;
}
}
}

return {};
}

const std::pair<bool, const std::vector<uint8_t>> CHevcSei::RemoveHdr10PlusFromSeiNalu(
const uint8_t* in_data, const size_t in_data_len)
{
bool containsHdr10Plus = false;

std::vector<uint8_t> buf;
std::vector<CHevcSei> messages =
CHevcSei::ParseSeiRbspUnclearedEmulation(in_data, in_data_len, 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->msg_offset),
std::next(buf.begin(), msg->payload_offset + msg->payload_size));
hevc_add_start_code_emulation_prevention_3_byte(buf);
}
else
{
// Single SEI message in NALU
buf.clear();
}
}
else
{
// No HDR10+
buf.clear();
}

return std::make_pair(containsHdr10Plus, buf);
}
13 changes: 13 additions & 0 deletions xbmc/utils/HevcSei.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,19 @@ class CHevcSei
static const std::vector<CHevcSei> ParseSeiRbspUnclearedEmulation(const uint8_t* in_data,
const size_t in_data_len,
std::vector<uint8_t>& buf);

// Returns a HDR10+ SEI message if present in the list
static const 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 const std::pair<bool, const std::vector<uint8_t>> RemoveHdr10PlusFromSeiNalu(
const uint8_t* in_data, const size_t in_data_len);

private:
// Used when parsing
uint8_t last_payload_type_byte;
Expand Down

0 comments on commit 3c034f1

Please sign in to comment.