[DASH] Support custom Accessibility and Role elements

Add dash_accessibilities stream descriptor, which is a semi-colon
separated list of accessibility_scheme_id_uri=value. It is optional.

Add dash_roles stream descriptor, which is a semi-colon separated
list of strings. It is optional.

Closes #565.

Change-Id: Idb1c20bb410fdd016db07e11fe507c102a3dd8ea
This commit is contained in:
KongQun Yang 2019-06-12 23:01:16 -07:00
parent 0a2b43939c
commit 796974d2a1
22 changed files with 251 additions and 8 deletions

View File

@ -0,0 +1,16 @@
DASH specific stream descriptor fields
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
:dash_accessibilities (accessibilities):
Optional semicolon separated list of values for DASH Accessibility element.
The value should be in the format: scheme_id_uri=value, which propagates
to the Accessibility element in the result DASH manifest. See DASH
(ISO/IEC 23009-1) specification for details.
:dash_roles (roles):
Optional semicolon separated list of values for DASH Role element. The
value should be one of: **caption**, **subtitle**, **main**, **alternate**,
**supplementary**, **commentary** and **dub**. See DASH (ISO/IEC 23009-1)
specification for details.

View File

@ -82,5 +82,6 @@ which describes the streams.
Configuration options Configuration options
--------------------- ---------------------
.. include:: /options/dash_stream_descriptors.rst
.. include:: /options/dash_options.rst .. include:: /options/dash_options.rst
.. include:: /options/segment_template_formatting.rst .. include:: /options/segment_template_formatting.rst

View File

@ -104,7 +104,14 @@ const char kUsage[] =
" unspecified, no I-Frames only playlist is created.\n" " unspecified, no I-Frames only playlist is created.\n"
" - hls_characteristics (charcs): Optional colon/semicolon separated\n" " - hls_characteristics (charcs): Optional colon/semicolon separated\n"
" list of values for the CHARACTERISTICS attribute for EXT-X-MEDIA.\n" " list of values for the CHARACTERISTICS attribute for EXT-X-MEDIA.\n"
" See CHARACTERISTICS attribute in http://bit.ly/2OOUkdB for details.\n"; " See CHARACTERISTICS attribute in http://bit.ly/2OOUkdB for details.\n"
" - dash_accessibilities (accessibilities): Optional semicolon separated\n"
" list of values for DASH Accessibility elements. The value should be\n"
" in the format: scheme_id_uri=value.\n"
" - dash_roles (roles): Optional semicolon separated list of values for\n"
" DASH Role elements. The value should be one of: caption, subtitle,\n"
" main, alternate, supplementary, commentary and dub. See DASH\n"
" (ISO/IEC 23009-1) specification for details.\n";
// Labels for parameters in RawKey key info. // Labels for parameters in RawKey key info.
const char kDrmLabelLabel[] = "label"; const char kDrmLabelLabel[] = "label";

View File

@ -31,6 +31,8 @@ enum FieldType {
kSkipEncryptionField, kSkipEncryptionField,
kDrmStreamLabelField, kDrmStreamLabelField,
kHlsCharacteristicsField, kHlsCharacteristicsField,
kDashAccessiblitiesField,
kDashRolesField,
}; };
struct FieldNameToTypeMapping { struct FieldNameToTypeMapping {
@ -67,6 +69,14 @@ const FieldNameToTypeMapping kFieldNameTypeMappings[] = {
{"hls_characteristics", kHlsCharacteristicsField}, {"hls_characteristics", kHlsCharacteristicsField},
{"characteristics", kHlsCharacteristicsField}, {"characteristics", kHlsCharacteristicsField},
{"charcs", kHlsCharacteristicsField}, {"charcs", kHlsCharacteristicsField},
{"dash_accessibilities", kDashAccessiblitiesField},
{"dash_accessibility", kDashAccessiblitiesField},
{"accessibilities", kDashAccessiblitiesField},
{"accessibility", kDashAccessiblitiesField},
{"dash_roles", kDashRolesField},
{"dash_role", kDashRolesField},
{"roles", kDashRolesField},
{"role", kDashRolesField},
}; };
FieldType GetFieldType(const std::string& field_name) { FieldType GetFieldType(const std::string& field_name) {
@ -176,6 +186,26 @@ base::Optional<StreamDescriptor> ParseStreamDescriptor(
base::SplitString(iter->second, ";:", base::TRIM_WHITESPACE, base::SplitString(iter->second, ";:", base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY); base::SPLIT_WANT_NONEMPTY);
break; break;
case kDashAccessiblitiesField:
descriptor.dash_accessiblities =
base::SplitString(iter->second, ";", base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
for (const std::string& accessibility :
descriptor.dash_accessiblities) {
size_t pos = accessibility.find('=');
if (pos == std::string::npos) {
LOG(ERROR)
<< "Accessibility should be in scheme=value format, but seeing "
<< accessibility;
return base::nullopt;
}
}
break;
case kDashRolesField:
descriptor.dash_roles =
base::SplitString(iter->second, ";", base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
break;
default: default:
LOG(ERROR) << "Unknown field in stream descriptor (\"" << iter->first LOG(ERROR) << "Unknown field in stream descriptor (\"" << iter->first
<< "\")."; << "\").";

View File

@ -275,6 +275,8 @@ class PackagerAppTest(unittest.TestCase):
using_time_specifier=False, using_time_specifier=False,
hls=False, hls=False,
hls_characteristics=None, hls_characteristics=None,
dash_accessibilities=None,
dash_roles=None,
trick_play_factor=None, trick_play_factor=None,
drm_label=None, drm_label=None,
skip_encryption=None, skip_encryption=None,
@ -301,6 +303,8 @@ class PackagerAppTest(unittest.TestCase):
$Number$. This flag is only relevant if segmented is True. $Number$. This flag is only relevant if segmented is True.
hls: Should the output be for an HLS manifest. hls: Should the output be for an HLS manifest.
hls_characteristics: CHARACTERISTICS attribute for the HLS stream. hls_characteristics: CHARACTERISTICS attribute for the HLS stream.
dash_accessibilities: Accessibility element for the DASH stream.
dash_roles: Role element for the DASH stream.
trick_play_factor: Signals the stream is to be used for a trick play trick_play_factor: Signals the stream is to be used for a trick play
stream and which key frames to use. A trick play factor of 0 is the stream and which key frames to use. A trick play factor of 0 is the
same as not specifying a trick play factor. same as not specifying a trick play factor.
@ -356,6 +360,11 @@ class PackagerAppTest(unittest.TestCase):
if hls_characteristics: if hls_characteristics:
stream.Append('hls_characteristics', hls_characteristics) stream.Append('hls_characteristics', hls_characteristics)
if dash_accessibilities:
stream.Append('dash_accessibilities', dash_accessibilities)
if dash_roles:
stream.Append('dash_roles', dash_roles)
requires_init_segment = segmented and base_ext not in [ requires_init_segment = segmented and base_ext not in [
'aac', 'ac3', 'ec3', 'ts', 'vtt' 'aac', 'ac3', 'ec3', 'ts', 'vtt'
] ]
@ -638,6 +647,18 @@ class PackagerFunctionalTest(PackagerAppTest):
self._GetStreams(['audio', 'video']), self._GetFlags(output_dash=True)) self._GetStreams(['audio', 'video']), self._GetFlags(output_dash=True))
self._CheckTestResults('audio-video') self._CheckTestResults('audio-video')
def testAudioVideoWithAccessibilitiesAndRoles(self):
streams = [
self._GetStream(
'audio',
dash_accessibilities='urn:tva:metadata:cs:AudioPurposeCS:2007=1',
dash_roles='alternate'),
self._GetStream('video'),
]
self.assertPackageSuccess(streams, self._GetFlags(output_dash=True))
self._CheckTestResults('audio-video-with-accessibilities-and-roles')
def testAudioVideoWithTrickPlay(self): def testAudioVideoWithTrickPlay(self):
streams = [ streams = [
self._GetStream('audio'), self._GetStream('audio'),

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT2.7360665798187256S">
<Period id="0">
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9">
<Representation id="0" bandwidth="973483" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
<BaseURL>bear-640x360-video.mp4</BaseURL>
<SegmentBase indexRange="859-926" timescale="30000">
<Initialization range="0-858"/>
</SegmentBase>
</Representation>
</AdaptationSet>
<AdaptationSet id="1" contentType="audio" subsegmentAlignment="true">
<Accessibility schemeIdUri="urn:tva:metadata:cs:AudioPurposeCS:2007" value="1"/>
<Role schemeIdUri="urn:mpeg:dash:role:2011" value="alternate"/>
<Representation id="1" bandwidth="133334" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
<BaseURL>bear-640x360-audio.mp4</BaseURL>
<SegmentBase indexRange="793-860" timescale="44100">
<Initialization range="0-792"/>
</SegmentBase>
</Representation>
</AdaptationSet>
</Period>
</MPD>

View File

@ -71,6 +71,10 @@ void MpdNotifyMuxerListener::OnMediaStart(
LOG(ERROR) << "Failed to generate MediaInfo from input."; LOG(ERROR) << "Failed to generate MediaInfo from input.";
return; return;
} }
for (const std::string& accessibility : accessibilities_)
media_info->add_dash_accessibilities(accessibility);
for (const std::string& role : roles_)
media_info->add_dash_roles(role);
if (is_encrypted_) { if (is_encrypted_) {
internal::SetContentProtectionFields(protection_scheme_, default_key_id_, internal::SetContentProtectionFields(protection_scheme_, default_key_id_,

View File

@ -55,6 +55,12 @@ class MpdNotifyMuxerListener : public MuxerListener {
void OnCueEvent(int64_t timestamp, const std::string& cue_data) override; void OnCueEvent(int64_t timestamp, const std::string& cue_data) override;
/// @} /// @}
void set_accessibilities(const std::vector<std::string>& accessiblities) {
accessibilities_ = accessiblities;
}
void set_roles(const std::vector<std::string>& roles) { roles_ = roles; }
private: private:
MpdNotifyMuxerListener(const MpdNotifyMuxerListener&) = delete; MpdNotifyMuxerListener(const MpdNotifyMuxerListener&) = delete;
MpdNotifyMuxerListener& operator=(const MpdNotifyMuxerListener&) = delete; MpdNotifyMuxerListener& operator=(const MpdNotifyMuxerListener&) = delete;
@ -65,6 +71,9 @@ class MpdNotifyMuxerListener : public MuxerListener {
base::Optional<uint32_t> notification_id_; base::Optional<uint32_t> notification_id_;
std::unique_ptr<MediaInfo> media_info_; std::unique_ptr<MediaInfo> media_info_;
std::vector<std::string> accessibilities_;
std::vector<std::string> roles_;
bool is_encrypted_ = false; bool is_encrypted_ = false;
// Storage for values passed to OnEncryptionInfoReady(). // Storage for values passed to OnEncryptionInfoReady().
FourCC protection_scheme_ = FOURCC_NULL; FourCC protection_scheme_ = FOURCC_NULL;

View File

@ -6,6 +6,7 @@
#include "packager/media/event/muxer_listener_factory.h" #include "packager/media/event/muxer_listener_factory.h"
#include "packager/base/memory/ptr_util.h"
#include "packager/base/strings/stringprintf.h" #include "packager/base/strings/stringprintf.h"
#include "packager/hls/base/hls_notifier.h" #include "packager/hls/base/hls_notifier.h"
#include "packager/media/event/combined_muxer_listener.h" #include "packager/media/event/combined_muxer_listener.h"
@ -30,10 +31,13 @@ std::unique_ptr<MuxerListener> CreateMediaInfoDumpListenerInternal(
} }
std::unique_ptr<MuxerListener> CreateMpdListenerInternal( std::unique_ptr<MuxerListener> CreateMpdListenerInternal(
const MuxerListenerFactory::StreamData& stream,
MpdNotifier* notifier) { MpdNotifier* notifier) {
DCHECK(notifier); DCHECK(notifier);
std::unique_ptr<MuxerListener> listener(new MpdNotifyMuxerListener(notifier)); auto listener = base::MakeUnique<MpdNotifyMuxerListener>(notifier);
listener->set_accessibilities(stream.dash_accessiblities);
listener->set_roles(stream.dash_roles);
return listener; return listener;
} }
@ -91,7 +95,8 @@ std::unique_ptr<MuxerListener> MuxerListenerFactory::CreateListener(
CreateMediaInfoDumpListenerInternal(stream.media_info_output)); CreateMediaInfoDumpListenerInternal(stream.media_info_output));
} }
if (mpd_notifier_) { if (mpd_notifier_) {
combined_listener->AddListener(CreateMpdListenerInternal(mpd_notifier_)); combined_listener->AddListener(
CreateMpdListenerInternal(stream, mpd_notifier_));
} }
if (hls_notifier_) { if (hls_notifier_) {
for (auto& listener : for (auto& listener :

View File

@ -46,6 +46,11 @@ class MuxerListenerFactory {
std::string hls_playlist_name; std::string hls_playlist_name;
std::string hls_iframe_playlist_name; std::string hls_iframe_playlist_name;
std::vector<std::string> hls_characteristics; std::vector<std::string> hls_characteristics;
// DASH specific values needed to write DASH mpd. Will only be used if an
// MpdNotifier is given to the factory.
std::vector<std::string> dash_accessiblities;
std::vector<std::string> dash_roles;
}; };
/// Create a new muxer listener. /// Create a new muxer listener.

View File

@ -55,11 +55,8 @@ std::string RoleToText(AdaptationSet::Role role) {
case AdaptationSet::kRoleDub: case AdaptationSet::kRoleDub:
return "dub"; return "dub";
default: default:
break; return "unknown";
} }
NOTREACHED();
return "";
} }
// Returns the picture aspect ratio string e.g. "16:9", "4:3". // Returns the picture aspect ratio string e.g. "16:9", "4:3".
@ -225,6 +222,11 @@ void AdaptationSet::UpdateContentProtectionPssh(const std::string& drm_uuid,
&content_protection_elements_); &content_protection_elements_);
} }
void AdaptationSet::AddAccessibility(const std::string& scheme,
const std::string& value) {
accessibilities_.push_back(Accessibility{scheme, value});
}
void AdaptationSet::AddRole(Role role) { void AdaptationSet::AddRole(Role role) {
roles_.insert(role); roles_.insert(role);
} }
@ -318,6 +320,11 @@ xml::scoped_xml_ptr<xmlNode> AdaptationSet::GetXml() {
"urn:mpeg:dash:adaptation-set-switching:2016", switching_ids); "urn:mpeg:dash:adaptation-set-switching:2016", switching_ids);
} }
for (const AdaptationSet::Accessibility& accessibility : accessibilities_) {
adaptation_set.AddAccessibilityElement(accessibility.scheme,
accessibility.value);
}
for (AdaptationSet::Role role : roles_) for (AdaptationSet::Role role : roles_)
adaptation_set.AddRoleElement("urn:mpeg:dash:role:2011", RoleToText(role)); adaptation_set.AddRoleElement("urn:mpeg:dash:role:2011", RoleToText(role));

View File

@ -40,6 +40,7 @@ class AdaptationSet {
// element to the AdaptationSet with schemeIdUri=urn:mpeg:dash:role:2011. // element to the AdaptationSet with schemeIdUri=urn:mpeg:dash:role:2011.
// See ISO/IEC 23009-1:2012 section 5.8.5.5. // See ISO/IEC 23009-1:2012 section 5.8.5.5.
enum Role { enum Role {
kRoleUnknown,
kRoleCaption, kRoleCaption,
kRoleSubtitle, kRoleSubtitle,
kRoleMain, kRoleMain,
@ -94,6 +95,13 @@ class AdaptationSet {
virtual void UpdateContentProtectionPssh(const std::string& drm_uuid, virtual void UpdateContentProtectionPssh(const std::string& drm_uuid,
const std::string& pssh); const std::string& pssh);
/// Set the Accessibility element for this AdaptationSet.
/// See ISO/IEC 23009-1:2012 section 5.8.4.3.
/// @param scheme is the schemeIdUri of the accessibility element.
/// @param value is the value of the accessibility element.
virtual void AddAccessibility(const std::string& scheme,
const std::string& value);
/// Set the Role element for this AdaptationSet. /// Set the Role element for this AdaptationSet.
/// The Role element's is schemeIdUri='urn:mpeg:dash:role:2011'. /// The Role element's is schemeIdUri='urn:mpeg:dash:role:2011'.
/// See ISO/IEC 23009-1:2012 section 5.8.5.5. /// See ISO/IEC 23009-1:2012 section 5.8.5.5.
@ -271,6 +279,13 @@ class AdaptationSet {
// in this set. // in this set.
std::set<std::string> picture_aspect_ratio_; std::set<std::string> picture_aspect_ratio_;
// accessibilities of this AdaptationSet.
struct Accessibility {
std::string scheme;
std::string value;
};
std::vector<Accessibility> accessibilities_;
// The roles of this AdaptationSet. // The roles of this AdaptationSet.
std::set<Role> roles_; std::set<Role> roles_;

View File

@ -163,6 +163,22 @@ TEST_F(AdaptationSetTest, CheckAdaptationSetId) {
AttributeEqual("id", std::to_string(kAdaptationSetId))); AttributeEqual("id", std::to_string(kAdaptationSetId)));
} }
// Verify AdaptationSet::AddAccessibilityElement() works.
TEST_F(AdaptationSetTest, AddAccessibilityElement) {
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
adaptation_set->AddAccessibility("urn:tva:metadata:cs:AudioPurposeCS:2007",
"2");
// The empty contentType is sort of a side effect of being able to generate an
// MPD without adding any Representations.
const char kExpectedOutput[] =
"<AdaptationSet contentType=\"\">\n"
" <Accessibility schemeIdUri=\"urn:tva:metadata:cs:AudioPurposeCS:2007\""
" value=\"2\"/>\n"
"</AdaptationSet>";
EXPECT_THAT(adaptation_set->GetXml().get(), XmlNodeEqual(kExpectedOutput));
}
// Verify AdaptationSet::AddRole() works for "main" role. // Verify AdaptationSet::AddRole() works for "main" role.
TEST_F(AdaptationSetTest, AdaptationAddRoleElementMain) { TEST_F(AdaptationSetTest, AdaptationAddRoleElementMain) {
auto adaptation_set = CreateAdaptationSet(kNoLanguage); auto adaptation_set = CreateAdaptationSet(kNoLanguage);

View File

@ -164,4 +164,12 @@ message MediaInfo {
// HLS only. Defines CHARACTERISTICS attribute of the stream. // HLS only. Defines CHARACTERISTICS attribute of the stream.
repeated string hls_characteristics = 20; repeated string hls_characteristics = 20;
// DASH only. Defines Accessibility elements of the stream. It should be in
// the format: scheme_id_uri=value.
repeated string dash_accessibilities = 21;
// DASH only. Defines Role elements of the stream. The value can be a valid
// Role value defined in "urn:mpeg:dash:role:2011" scheme or in the format:
// scheme_id_uri=value (to be implemented).
repeated string dash_roles = 22;
} }

View File

@ -168,6 +168,18 @@ std::string GetAdaptationSetKey(const MediaInfo& media_info) {
key.append(":trick_play"); key.append(":trick_play");
} }
if (!media_info.dash_accessibilities().empty()) {
key.append(":accessibility_");
for (const std::string& accessibility : media_info.dash_accessibilities())
key.append(accessibility);
}
if (!media_info.dash_roles().empty()) {
key.append(":roles_");
for (const std::string& role : media_info.dash_roles())
key.append(role);
}
return key; return key;
} }

View File

@ -42,6 +42,24 @@ const std::string& GetDefaultTextLanguage(const MpdOptions& mpd_options) {
: mpd_options.mpd_params.default_text_language; : mpd_options.mpd_params.default_text_language;
} }
AdaptationSet::Role RoleFromString(const std::string& role_str) {
if (role_str == "caption")
return AdaptationSet::Role::kRoleCaption;
if (role_str == "subtitle")
return AdaptationSet::Role::kRoleSubtitle;
if (role_str == "main")
return AdaptationSet::Role::kRoleMain;
if (role_str == "alternate")
return AdaptationSet::Role::kRoleAlternate;
if (role_str == "supplementary")
return AdaptationSet::Role::kRoleSupplementary;
if (role_str == "commentary")
return AdaptationSet::Role::kRoleCommentary;
if (role_str == "dub")
return AdaptationSet::Role::kRoleDub;
return AdaptationSet::Role::kRoleUnknown;
}
} // namespace } // namespace
Period::Period(uint32_t period_id, Period::Period(uint32_t period_id,
@ -159,7 +177,16 @@ bool Period::SetNewAdaptationSetAttributes(
const MediaInfo& media_info, const MediaInfo& media_info,
const std::list<AdaptationSet*>& adaptation_sets, const std::list<AdaptationSet*>& adaptation_sets,
AdaptationSet* new_adaptation_set) { AdaptationSet* new_adaptation_set) {
if (!language.empty()) { if (!media_info.dash_roles().empty()) {
for (const std::string& role_str : media_info.dash_roles()) {
AdaptationSet::Role role = RoleFromString(role_str);
if (role == AdaptationSet::kRoleUnknown) {
LOG(ERROR) << "Unrecognized role '" << role_str << "'.";
return false;
}
new_adaptation_set->AddRole(role);
}
} else if (!language.empty()) {
const bool is_main_role = const bool is_main_role =
language == (media_info.has_audio_info() language == (media_info.has_audio_info()
? GetDefaultAudioLanguage(mpd_options_) ? GetDefaultAudioLanguage(mpd_options_)
@ -167,6 +194,17 @@ bool Period::SetNewAdaptationSetAttributes(
if (is_main_role) if (is_main_role)
new_adaptation_set->AddRole(AdaptationSet::kRoleMain); new_adaptation_set->AddRole(AdaptationSet::kRoleMain);
} }
for (const std::string& accessibility : media_info.dash_accessibilities()) {
size_t pos = accessibility.find('=');
if (pos == std::string::npos) {
LOG(ERROR)
<< "Accessibility should be in scheme=value format, but seeing "
<< accessibility;
return false;
}
new_adaptation_set->AddAccessibility(accessibility.substr(0, pos),
accessibility.substr(pos + 1));
}
if (media_info.has_video_info()) { if (media_info.has_video_info()) {
// Because 'language' is ignored for videos, |adaptation_sets| must have // Because 'language' is ignored for videos, |adaptation_sets| must have

View File

@ -286,6 +286,16 @@ AdaptationSetXmlNode::AdaptationSetXmlNode()
: RepresentationBaseXmlNode("AdaptationSet") {} : RepresentationBaseXmlNode("AdaptationSet") {}
AdaptationSetXmlNode::~AdaptationSetXmlNode() {} AdaptationSetXmlNode::~AdaptationSetXmlNode() {}
void AdaptationSetXmlNode::AddAccessibilityElement(
const std::string& scheme_id_uri,
const std::string& value) {
XmlNode accessibility("Accessibility");
accessibility.SetStringAttribute("schemeIdUri", scheme_id_uri);
if (!value.empty())
accessibility.SetStringAttribute("value", value);
AddChild(accessibility.PassScopedPtr());
}
void AdaptationSetXmlNode::AddRoleElement(const std::string& scheme_id_uri, void AdaptationSetXmlNode::AddRoleElement(const std::string& scheme_id_uri,
const std::string& value) { const std::string& value) {
XmlNode role("Role"); XmlNode role("Role");

View File

@ -130,6 +130,11 @@ class AdaptationSetXmlNode : public RepresentationBaseXmlNode {
AdaptationSetXmlNode(); AdaptationSetXmlNode();
~AdaptationSetXmlNode() override; ~AdaptationSetXmlNode() override;
/// @param scheme_id_uri is content of the schemeIdUri attribute.
/// @param value is the content of value attribute.
void AddAccessibilityElement(const std::string& scheme_id_uri,
const std::string& value);
/// @param scheme_id_uri is content of the schemeIdUri attribute. /// @param scheme_id_uri is content of the schemeIdUri attribute.
/// @param value is the content of value attribute. /// @param value is the content of value attribute.
void AddRoleElement(const std::string& scheme_id_uri, void AddRoleElement(const std::string& scheme_id_uri,

View File

@ -87,11 +87,15 @@ MuxerListenerFactory::StreamData ToMuxerListenerData(
const StreamDescriptor& stream) { const StreamDescriptor& stream) {
MuxerListenerFactory::StreamData data; MuxerListenerFactory::StreamData data;
data.media_info_output = stream.output; data.media_info_output = stream.output;
data.hls_group_id = stream.hls_group_id; data.hls_group_id = stream.hls_group_id;
data.hls_name = stream.hls_name; data.hls_name = stream.hls_name;
data.hls_playlist_name = stream.hls_playlist_name; data.hls_playlist_name = stream.hls_playlist_name;
data.hls_iframe_playlist_name = stream.hls_iframe_playlist_name; data.hls_iframe_playlist_name = stream.hls_iframe_playlist_name;
data.hls_characteristics = stream.hls_characteristics; data.hls_characteristics = stream.hls_characteristics;
data.dash_accessiblities = stream.dash_accessiblities;
data.dash_roles = stream.dash_roles;
return data; return data;
}; };

View File

@ -123,6 +123,11 @@ struct StreamDescriptor {
/// Optional for HLS output. It defines the CHARACTERISTICS attribute of the /// Optional for HLS output. It defines the CHARACTERISTICS attribute of the
/// stream. /// stream.
std::vector<std::string> hls_characteristics; std::vector<std::string> hls_characteristics;
/// Optional for DASH output. It defines Accessibility elements of the stream.
std::vector<std::string> dash_accessiblities;
/// Optional for DASH output. It defines Role elements of the stream.
std::vector<std::string> dash_roles;
}; };
class SHAKA_EXPORT Packager { class SHAKA_EXPORT Packager {