Skip to content

Commit

Permalink
[Android] Add Dolby Vision compatibility mode setting
Browse files Browse the repository at this point in the history
For Dolby Vision profile 7 video files, the metadata is converted automatically
to improve compatibility on certain Android devices
  • Loading branch information
quietvoid committed Sep 2, 2023
1 parent 1c52605 commit cfcb8a2
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 1 deletion.
12 changes: 12 additions & 0 deletions addons/resource.language.en_gb/resources/strings.po
Expand Up @@ -23481,3 +23481,15 @@ msgstr ""
msgctxt "#39195"
msgid "Enable this option for the best picture quality. Disabling reduces the load on resource-limited systems."
msgstr ""

#. Title of Dolby Vision RPU conversion setting
#: system/settings/settings.xml
msgctxt "#39196"
msgid "Dolby Vision compatibility mode"
msgstr ""

#. Help text for setting "Convert Dolby Vision for compatibility" of label #39196
#: system/settings/settings.xml
msgctxt "#39197"
msgid "If enabled, Dolby Vision profile 7 will be converted to profile 8.1, which is more commonly supported by devices. Enable if your device supports Dolby Vision, but has issues with some videos."
msgstr ""
11 changes: 11 additions & 0 deletions system/settings/settings.xml
Expand Up @@ -179,6 +179,17 @@
<default>true</default>
<control type="toggle" />
</setting>
<setting id="videoplayer.convertdovi" type="boolean" label="39196" help="39197">
<requirement>HAS_MEDIACODEC</requirement>
<dependencies>
<dependency type="visible">
<condition on="property" name="supportsdolbyvision" />
</dependency>
</dependencies>
<level>2</level>
<default>false</default>
<control type="toggle" />
</setting>
</group>
<group id="4" label="14232">
<setting id="videoplayer.stereoscopicplaybackmode" type="integer" label="36520" help="36537">
Expand Down
Expand Up @@ -560,7 +560,22 @@ bool CDVDVideoCodecAndroidMediaCodec::Open(CDVDStreamInfo &hints, CDVDCodecOptio
{
m_bitstream.reset();
}

// Only set for profile 7, container hint allows to skip parsing unnecessarily
if (m_bitstream && m_hints.dovi.dv_profile == 7)
{
bool convertDovi = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
CSettings::SETTING_VIDEOPLAYER_CONVERTDOVI);

CLog::Log(LOGDEBUG,
"CDVDVideoCodecAndroidMediaCodec::Open Dolby Vision compatibility mode "
"enabled: {}",
convertDovi);

m_bitstream->SetConvertDovi(convertDovi);
}
}

break;
}
case AV_CODEC_ID_WMV3:
Expand Down
9 changes: 9 additions & 0 deletions xbmc/settings/SettingConditions.cpp
Expand Up @@ -118,6 +118,14 @@ bool SupportsVideoSuperResolution(const std::string& condition,
return CServiceBroker::GetWinSystem()->SupportsVideoSuperResolution();
}

bool SupportsDolbyVision(const std::string& condition,
const std::string& value,
const SettingConstPtr& setting,
void* data)
{
return CServiceBroker::GetWinSystem()->GetDisplayHDRCapabilities().SupportsDolbyVision();
}

bool SupportsScreenMove(const std::string& condition,
const std::string& value,
const SettingConstPtr& setting,
Expand Down Expand Up @@ -470,6 +478,7 @@ void CSettingConditions::Initialize()
m_complexConditions.emplace("hassystemsdrpeakluminance", HasSystemSdrPeakLuminance);
m_complexConditions.emplace("supportsscreenmove", SupportsScreenMove);
m_complexConditions.emplace("supportsvideosuperresolution", SupportsVideoSuperResolution);
m_complexConditions.emplace("supportsdolbyvision", SupportsDolbyVision);
m_complexConditions.emplace("ishdrdisplay", IsHDRDisplay);
m_complexConditions.emplace("ismasteruser", IsMasterUser);
m_complexConditions.emplace("hassubtitlesfontextensions", HasSubtitlesFontExtensions);
Expand Down
1 change: 1 addition & 0 deletions xbmc/settings/Settings.h
Expand Up @@ -129,6 +129,7 @@ class CSettings : public CSettingsBase, public CSettingCreator, public CSettingC
static constexpr auto SETTING_VIDEOPLAYER_USESTAGEFRIGHT = "videoplayer.usestagefright";
static constexpr auto SETTING_VIDEOPLAYER_LIMITGUIUPDATE = "videoplayer.limitguiupdate";
static constexpr auto SETTING_VIDEOPLAYER_SUPPORTMVC = "videoplayer.supportmvc";
static constexpr auto SETTING_VIDEOPLAYER_CONVERTDOVI = "videoplayer.convertdovi";
static constexpr auto SETTING_MYVIDEOS_SELECTACTION = "myvideos.selectaction";
static constexpr auto SETTING_MYVIDEOS_USETAGS = "myvideos.usetags";
static constexpr auto SETTING_MYVIDEOS_EXTRACTFLAGS = "myvideos.extractflags";
Expand Down
76 changes: 75 additions & 1 deletion xbmc/utils/BitstreamConverter.cpp
Expand Up @@ -20,6 +20,13 @@

#include <algorithm>

extern "C"
{
#ifdef HAVE_LIBDOVI
#include <libdovi/rpu_parser.h>
#endif
}

enum {
AVC_NAL_SLICE=1,
AVC_NAL_DPA,
Expand Down Expand Up @@ -257,6 +264,32 @@ static bool has_sei_recovery_point(const uint8_t *p, const uint8_t *end)
return false;
}

#ifdef HAVE_LIBDOVI
// The returned data must be freed with `dovi_data_free`
// May be NULL if no conversion was done
static const DoviData* convert_dovi_rpu_nal(uint8_t* buf, uint32_t nal_size)
{
DoviRpuOpaque* rpu = dovi_parse_unspec62_nalu(buf, nal_size);
const DoviRpuDataHeader* header = dovi_rpu_get_header(rpu);
const DoviData* rpu_data = NULL;

if (header && header->guessed_profile == 7)
{
int ret = dovi_convert_rpu_with_mode(rpu, 2);
if (ret < 0)
goto done;

rpu_data = dovi_write_unspec62_nalu(rpu);
}

done:
dovi_rpu_free_header(header);
dovi_rpu_free(rpu);

return rpu_data;
}
#endif

////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
CBitstreamParser::CBitstreamParser() = default;
Expand Down Expand Up @@ -322,6 +355,7 @@ CBitstreamConverter::CBitstreamConverter()
m_convert_bytestream = false;
m_sps_pps_context.sps_pps_data = NULL;
m_start_decode = true;
m_convert_dovi = false;
}

CBitstreamConverter::~CBitstreamConverter()
Expand Down Expand Up @@ -875,6 +909,10 @@ bool CBitstreamConverter::BitstreamConvert(uint8_t* pData, int iSize, uint8_t **
uint32_t cumul_size = 0;
const uint8_t *buf_end = buf + buf_size;

#ifdef HAVE_LIBDOVI
const DoviData* rpu_data = NULL;
#endif

switch (m_codec)
{
case AV_CODEC_ID_H264:
Expand Down Expand Up @@ -928,12 +966,48 @@ bool CBitstreamConverter::BitstreamConvert(uint8_t* pData, int iSize, uint8_t **
}
else
{
BitstreamAllocAndCopy(poutbuf, poutbuf_size, NULL, 0, buf, nal_size, unit_type);
bool write_buf = true;
const uint8_t* buf_to_write = buf;
int32_t final_nal_size = nal_size;

if (!m_sps_pps_context.first_idr && IsSlice(unit_type))
{
m_sps_pps_context.first_idr = 1;
m_sps_pps_context.idr_sps_pps_seen = 0;
}

if (m_convert_dovi)
{
if (unit_type == HEVC_NAL_UNSPEC62)
{
#ifdef HAVE_LIBDOVI
// Convert the RPU itself
rpu_data = convert_dovi_rpu_nal(buf, nal_size);
if (rpu_data)
{
buf_to_write = rpu_data->data;
final_nal_size = rpu_data->len;
}
#endif
}
else if (unit_type == HEVC_NAL_UNSPEC63)
{
// Ignore the enhancement layer, may or may not help
write_buf = false;
}
}

if (write_buf)
BitstreamAllocAndCopy(poutbuf, poutbuf_size, NULL, 0, buf_to_write, final_nal_size,
unit_type);

#ifdef HAVE_LIBDOVI
if (rpu_data)
{
dovi_data_free(rpu_data);
rpu_data = NULL;
}
#endif
}

buf += nal_size;
Expand Down
2 changes: 2 additions & 0 deletions xbmc/utils/BitstreamConverter.h
Expand Up @@ -99,6 +99,7 @@ class CBitstreamConverter
int GetExtraSize() const;
void ResetStartDecode(void);
bool CanStartDecode() const;
void SetConvertDovi(bool value) { m_convert_dovi = value; }

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

Expand Down Expand Up @@ -143,4 +144,5 @@ class CBitstreamConverter
bool m_convert_bytestream;
AVCodecID m_codec;
bool m_start_decode;
bool m_convert_dovi;
};

0 comments on commit cfcb8a2

Please sign in to comment.