Skip to content

Commit aa6d500

Browse files
committed
[PropertiesUtils] Add streams_config property
1 parent 767d20b commit aa6d500

File tree

9 files changed

+225
-11
lines changed

9 files changed

+225
-11
lines changed

inputstream.adaptive/addon.xml.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
name="adaptive"
1111
extension=""
1212
tags="true"
13-
listitemprops="license_type|license_key|license_data|license_flags|manifest_type|server_certificate|manifest_update_parameter|manifest_params|manifest_headers|stream_params|stream_headers|original_audio_language|play_timeshift_buffer|pre_init_data|stream_selection_type|chooser_bandwidth_max|chooser_resolution_max|chooser_resolution_secure_max|live_delay"
13+
listitemprops="license_type|license_key|license_data|license_flags|manifest_type|server_certificate|manifest_update_parameter|manifest_params|manifest_headers|stream_params|stream_headers|original_audio_language|play_timeshift_buffer|pre_init_data|stream_selection_type|chooser_bandwidth_max|chooser_resolution_max|chooser_resolution_secure_max|live_delay|streams_config"
1414
library_@PLATFORM@="@LIBRARY_FILENAME@"/>
1515
<extension point="xbmc.addon.metadata">
1616
<platform>@PLATFORM@</platform>

src/Session.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -798,13 +798,10 @@ void CSession::AddStream(PLAYLIST::CAdaptationSet* adp,
798798
stream.m_info.SetStreamType(INPUTSTREAM_TYPE_AUDIO);
799799
if (adp->IsImpaired())
800800
flags |= INPUTSTREAM_FLAG_VISUAL_IMPAIRED;
801+
if (adp->IsOriginal())
802+
flags |= INPUTSTREAM_FLAG_ORIGINAL;
801803
if (adp->IsDefault())
802804
flags |= INPUTSTREAM_FLAG_DEFAULT;
803-
if (adp->IsOriginal() || (!m_kodiProps.m_audioLanguageOrig.empty() &&
804-
adp->GetLanguage() == m_kodiProps.m_audioLanguageOrig))
805-
{
806-
flags |= INPUTSTREAM_FLAG_ORIGINAL;
807-
}
808805
break;
809806
}
810807
case StreamType::SUBTITLE:

src/common/AdaptationSet.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,24 @@ bool PLAYLIST::CAdaptationSet::Compare(const std::unique_ptr<CAdaptationSet>& le
157157

158158
return false;
159159
}
160+
161+
std::vector<std::unique_ptr<CAdaptationSet>>::const_iterator PLAYLIST::CAdaptationSet::
162+
FindAudioAdpSet(const std::vector<std::unique_ptr<CAdaptationSet>>& adpSets,
163+
const std::string langCode,
164+
bool isPreferStereo,
165+
bool filterImpaired)
166+
{
167+
for (auto& itAdpSet = adpSets.cbegin(); itAdpSet != adpSets.cend(); itAdpSet++)
168+
{
169+
auto adpSet = itAdpSet->get();
170+
if (adpSet->GetStreamType() == StreamType::AUDIO &&
171+
STRING::CompareNoCase(adpSet->GetLanguage(), langCode) &&
172+
(isPreferStereo ? adpSet->GetRepresentations()[0]->GetAudioChannels() <= 2
173+
: adpSet->GetRepresentations()[0]->GetAudioChannels() > 2) &&
174+
adpSet->IsImpaired() == filterImpaired)
175+
{
176+
return itAdpSet;
177+
}
178+
}
179+
return adpSets.end();
180+
}

src/common/AdaptationSet.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,12 @@ class ATTR_DLL_LOCAL CAdaptationSet : public CCommonSegAttribs, public CCommonAt
109109
static bool Compare(const std::unique_ptr<CAdaptationSet>& left,
110110
const std::unique_ptr<CAdaptationSet>& right);
111111

112+
static std::vector<std::unique_ptr<CAdaptationSet>>::const_iterator FindAudioAdpSet(
113+
const std::vector<std::unique_ptr<CAdaptationSet>>& adpSets,
114+
const std::string langCode,
115+
bool isPreferStereo,
116+
bool filterImpaired = false);
117+
112118
protected:
113119
std::vector<std::unique_ptr<CRepresentation>> m_representations;
114120

src/common/AdaptiveTree.cpp

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ namespace adaptive
6868

6969
void AdaptiveTree::PostOpen(const UTILS::PROPERTIES::KodiProperties& kodiProps)
7070
{
71+
FixStreamsFlags(kodiProps);
72+
7173
// A manifest can provide live delay value, if not so we use our default
7274
// value of 16 secs, this is needed to ensure an appropriate playback,
7375
// an add-on can override the delay to try fix edge use cases
@@ -374,4 +376,148 @@ namespace adaptive
374376
m_cvWait.notify_all();
375377
}
376378

379+
void AdaptiveTree::FixStreamsFlags(const UTILS::PROPERTIES::KodiProperties& kodiProps)
380+
{
381+
// Add-ons can override subtitles "default" flag to streams
382+
if (!kodiProps.m_subtitleLangDefault.empty())
383+
{
384+
for (auto& period : m_periods)
385+
{
386+
for (auto& adpSet : period->GetAdaptationSets())
387+
{
388+
if (adpSet->GetStreamType() == StreamType::SUBTITLE)
389+
{
390+
adpSet->SetIsDefault(
391+
STRING::CompareNoCase(adpSet->GetLanguage(), kodiProps.m_subtitleLangDefault));
392+
}
393+
}
394+
}
395+
}
396+
397+
// Add-ons can override audio "original" flag to streams
398+
if (!kodiProps.m_audioLangOriginal.empty())
399+
{
400+
for (auto& period : m_periods)
401+
{
402+
for (auto& adpSet : period->GetAdaptationSets())
403+
{
404+
if (adpSet->GetStreamType() == StreamType::AUDIO)
405+
{
406+
adpSet->SetIsOriginal(
407+
STRING::CompareNoCase(adpSet->GetLanguage(), kodiProps.m_audioLangOriginal));
408+
}
409+
}
410+
}
411+
}
412+
413+
// Audio streams "default" flag customization / workaround
414+
415+
// Manifest of video services dont always set appropriately the default stream flag and also
416+
// the manifest "default" stream flag dont have always the same meaning of Kodi track "default" flag,
417+
// this can lead to wrong audio track selected when playback start.
418+
// A good example is when "Media default" Kodi audio setting is set, where kodi expects just
419+
// a single track with the default flag.
420+
// Another problem is when video services provide multiple audio streams with same language code
421+
// but differents channels, most of the times we can have 1 stereo and 1 multichannels
422+
// stream with same language code, rarely there are multi-codecs with same channels,
423+
// but we simplify by ignoring codec types.
424+
425+
// To allow Kodi VP to do a better track auto-selection we need:
426+
// - Set default flag to a single track only
427+
// - Set default flag to stereo or multichannels track, not both
428+
// to do this its needed that an addon specify what to do because C++ interface dont provide
429+
// access to kodi language settings where python can do it, then we cant automatize it.
430+
const std::string langCodeDef = kodiProps.m_audioLangDefault;
431+
const std::string langCodeOrig = kodiProps.m_audioLangOriginal;
432+
433+
if (!langCodeDef.empty() || !langCodeOrig.empty())
434+
{
435+
bool isDefaultStereo = kodiProps.m_audioPrefStereo; // add-on based setting
436+
437+
for (auto& period : m_periods)
438+
{
439+
auto& adpSets = period->GetAdaptationSets();
440+
auto itAudioStream = adpSets.cend();
441+
442+
// Try give priority to "impaired" streams
443+
if (kodiProps.m_audioPrefType == "impaired")
444+
{
445+
if (isDefaultStereo)
446+
{
447+
itAudioStream = CAdaptationSet::FindAudioAdpSet(adpSets, langCodeDef, true, true);
448+
if (itAudioStream == adpSets.cend()) // No stereo stream, find multichannels
449+
itAudioStream = CAdaptationSet::FindAudioAdpSet(adpSets, langCodeDef, false, true);
450+
}
451+
else
452+
{
453+
itAudioStream = CAdaptationSet::FindAudioAdpSet(adpSets, langCodeDef, false, true);
454+
if (itAudioStream == adpSets.cend()) // No multichannels stream, find stereo
455+
itAudioStream = CAdaptationSet::FindAudioAdpSet(adpSets, langCodeDef, true, true);
456+
}
457+
458+
// No stream found, try find a "impaired" stream with the "original" language code
459+
if (itAudioStream == adpSets.cend() && !langCodeOrig.empty())
460+
{
461+
if (isDefaultStereo)
462+
{
463+
itAudioStream = CAdaptationSet::FindAudioAdpSet(adpSets, langCodeOrig, true, true);
464+
if (itAudioStream == adpSets.cend()) // No stereo stream, find multichannels
465+
itAudioStream = CAdaptationSet::FindAudioAdpSet(adpSets, langCodeOrig, false, true);
466+
}
467+
else
468+
{
469+
itAudioStream = CAdaptationSet::FindAudioAdpSet(adpSets, langCodeOrig, false, true);
470+
if (itAudioStream == adpSets.cend()) // No multichannels stream, find stereo
471+
itAudioStream = CAdaptationSet::FindAudioAdpSet(adpSets, langCodeOrig, true, true);
472+
}
473+
}
474+
}
475+
476+
// Try find a stream with specified lang code
477+
if (kodiProps.m_audioPrefType != "original" && itAudioStream == adpSets.cend() &&
478+
!langCodeDef.empty())
479+
{
480+
if (isDefaultStereo)
481+
{
482+
itAudioStream = CAdaptationSet::FindAudioAdpSet(adpSets, langCodeDef, true);
483+
if (itAudioStream == adpSets.cend()) // No stereo stream, find multichannels
484+
itAudioStream = CAdaptationSet::FindAudioAdpSet(adpSets, langCodeDef, false);
485+
}
486+
else
487+
{
488+
itAudioStream = CAdaptationSet::FindAudioAdpSet(adpSets, langCodeDef, false);
489+
if (itAudioStream == adpSets.cend()) // No multichannels stream, find stereo
490+
itAudioStream = CAdaptationSet::FindAudioAdpSet(adpSets, langCodeDef, true);
491+
}
492+
}
493+
494+
// No stream found, try find a stream with the "original" language code
495+
if (itAudioStream == adpSets.cend() && !langCodeOrig.empty())
496+
{
497+
if (isDefaultStereo)
498+
{
499+
itAudioStream = CAdaptationSet::FindAudioAdpSet(adpSets, langCodeOrig, true);
500+
if (itAudioStream == adpSets.cend()) // No stereo stream, find multichannels
501+
itAudioStream = CAdaptationSet::FindAudioAdpSet(adpSets, langCodeOrig, false);
502+
}
503+
else
504+
{
505+
itAudioStream = CAdaptationSet::FindAudioAdpSet(adpSets, langCodeOrig, false);
506+
if (itAudioStream == adpSets.cend()) // No multichannels stream, find stereo
507+
itAudioStream = CAdaptationSet::FindAudioAdpSet(adpSets, langCodeOrig, true);
508+
}
509+
}
510+
511+
// Update "default" flags
512+
if (itAudioStream != adpSets.cend())
513+
{
514+
for (auto& adpSet : adpSets)
515+
{
516+
adpSet->SetIsDefault(adpSet.get() == itAudioStream->get());
517+
}
518+
}
519+
}
520+
}
521+
}
522+
377523
} // namespace adaptive

src/common/AdaptiveTree.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,12 @@ class ATTR_DLL_LOCAL AdaptiveTree
254254
const std::string& data,
255255
std::string_view info);
256256

257+
/*!
258+
* \brief Apply fixes and overrides to audio/subtitles stream flags.
259+
* \param kodiProps The Kodi properties
260+
*/
261+
void FixStreamsFlags(const UTILS::PROPERTIES::KodiProperties& kodiProps);
262+
257263
void SortTree();
258264

259265
// Live segment update section

src/parser/DASHTree.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,8 @@ void adaptive::CDashTree::ParseTagAdaptationSet(pugi::xml_node nodeAdp, PLAYLIST
376376
adpSet->SetIsForced(true);
377377
else if (value == "main")
378378
adpSet->SetIsDefault(true);
379+
else if (value == "caption" || value == "alternate" || value == "commentary")
380+
adpSet->SetIsImpaired(true);
379381
}
380382
}
381383

@@ -388,7 +390,7 @@ void adaptive::CDashTree::ParseTagAdaptationSet(pugi::xml_node nodeAdp, PLAYLIST
388390

389391
if (schemeIdUri == "urn:mpeg:dash:role:2011")
390392
{
391-
if (value == "caption")
393+
if (STRING::StartsWith(value, "caption")) // caption or captions
392394
adpSet->SetIsImpaired(true);
393395
}
394396
}

src/utils/PropertiesUtils.cpp

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ constexpr std::string_view PROP_MANIFEST_HEADERS = "inputstream.adaptive.manifes
3434
constexpr std::string_view PROP_STREAM_PARAMS = "inputstream.adaptive.stream_params";
3535
constexpr std::string_view PROP_STREAM_HEADERS = "inputstream.adaptive.stream_headers";
3636

37-
constexpr std::string_view PROP_AUDIO_LANG_ORIG = "inputstream.adaptive.original_audio_language";
37+
constexpr std::string_view PROP_AUDIO_LANG_ORIG = "inputstream.adaptive.original_audio_language"; //! @todo: deprecated, to be removed on next Kodi release
38+
constexpr std::string_view PROP_STREAMS_CONFIG = "inputstream.adaptive.streams_config";
39+
3840
constexpr std::string_view PROP_PLAY_TIMESHIFT_BUFFER = "inputstream.adaptive.play_timeshift_buffer";
3941
constexpr std::string_view PROP_LIVE_DELAY = "inputstream.adaptive.live_delay";
4042
constexpr std::string_view PROP_PRE_INIT_DATA = "inputstream.adaptive.pre_init_data";
@@ -121,9 +123,29 @@ KodiProperties UTILS::PROPERTIES::ParseKodiProperties(
121123
{
122124
ParseHeaderString(props.m_streamHeaders, prop.second);
123125
}
124-
else if (prop.first == PROP_AUDIO_LANG_ORIG)
126+
else if (prop.first == PROP_AUDIO_LANG_ORIG) //! @todo: deprecated, to be removed on next Kodi release
127+
{
128+
LOG::Log(LOGWARNING,
129+
"Warning \"inputstream.adaptive.original_audio_language\" property is deprecated "
130+
"has been replaced by \"inputstream.adaptive.stream_audio_cfg\". "
131+
"Please read Wiki \"Integration\" page to learn more about the new properties.");
132+
props.m_audioLangOriginal = prop.second;
133+
}
134+
else if (prop.first == PROP_STREAMS_CONFIG)
125135
{
126-
props.m_audioLanguageOrig = prop.second;
136+
auto values = STRING::ToMap(prop.second, '=', ';');
137+
138+
if (STRING::KeyExists(values, "audio_langcode_default"))
139+
props.m_audioLangDefault = STRING::Trim(values["audio_langcode_default"]);
140+
if (STRING::KeyExists(values, "audio_langcode_original"))
141+
props.m_audioLangOriginal = STRING::Trim(values["audio_langcode_original"]);
142+
if (STRING::KeyExists(values, "audio_prefer_stereo"))
143+
props.m_audioPrefStereo = values["audio_prefer_stereo"] == "true";
144+
if (STRING::KeyExists(values, "audio_prefer_type"))
145+
props.m_audioPrefType = STRING::Trim(values["audio_prefer_type"]);
146+
147+
if (STRING::KeyExists(values, "subtitles_langcode_default"))
148+
props.m_subtitleLangDefault = STRING::Trim(values["subtitles_langcode_default"]);
127149
}
128150
else if (prop.first == PROP_PLAY_TIMESHIFT_BUFFER)
129151
{

src/utils/PropertiesUtils.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,21 @@ struct KodiProperties
5353
std::string m_streamParams;
5454
// HTTP headers used to download streams
5555
std::map<std::string, std::string> m_streamHeaders;
56-
std::string m_audioLanguageOrig;
56+
57+
// Defines what type of audio tracks should be preferred for the "default" flag,
58+
// accepted values are: original, impaired, or empty string.
59+
// When empty: it try to set the flag to a regular language track or fallback to original language
60+
std::string m_audioPrefType;
61+
// Defines if stereo audio tracks are preferred over multichannels one,
62+
// it depends from m_audioLangDefault
63+
bool m_audioPrefStereo{false};
64+
// Force audio streams with the specified language code to have the "default" flag
65+
std::string m_audioLangDefault;
66+
// Force audio streams with the specified language code to have the "original" flag
67+
std::string m_audioLangOriginal;
68+
// Force subtitle streams with the specified language code to have the "default" flag
69+
std::string m_subtitleLangDefault;
70+
5771
bool m_playTimeshiftBuffer{false};
5872
// Set a custom delay from live edge in seconds
5973
uint64_t m_liveDelay{0};

0 commit comments

Comments
 (0)