diff --git a/packager/mpd/base/mpd_builder.cc b/packager/mpd/base/mpd_builder.cc index 7ccdaf7e2d..8e5247fc87 100644 --- a/packager/mpd/base/mpd_builder.cc +++ b/packager/mpd/base/mpd_builder.cc @@ -273,6 +273,33 @@ void AddPictureAspectRatio( picture_aspect_ratio->insert(par); } +std::string RoleToText(AdaptationSet::Role role) { + // Using switch so that the compiler can detect whether there is a case that's + // not being handled. + switch (role) { + case AdaptationSet::kRoleCaption: + return "caption"; + case AdaptationSet::kRoleSubtitle: + return "subtitle"; + case AdaptationSet::kRoleMain: + return "main"; + case AdaptationSet::kRoleAlternate: + return "alternate"; + case AdaptationSet::kRoleSupplementary: + return "supplementary"; + case AdaptationSet::kRoleCommentary: + return "commentary"; + case AdaptationSet::kRoleDub: + return "dub"; + default: + NOTREACHED(); + return ""; + } + + NOTREACHED(); + return ""; +} + // Spooky static initialization/cleanup of libxml. class LibXmlInitializer { public: @@ -619,6 +646,10 @@ void AdaptationSet::AddContentProtectionElement( RemoveDuplicateAttributes(&content_protection_elements_.back()); } +void AdaptationSet::AddRole(Role role) { + roles_.insert(role); +} + // Creates a copy of xml element, iterate thru all the // (child) elements and add them to the copy. xml::ScopedXmlPtr::type AdaptationSet::GetXml() { @@ -670,6 +701,12 @@ xml::ScopedXmlPtr::type AdaptationSet::GetXml() { if (group_ >= 0) adaptation_set.SetIntegerAttribute("group", group_); + + for (std::set::const_iterator role_it = roles_.begin(); + role_it != roles_.end(); ++role_it) { + adaptation_set.AddRoleElement("urn:mpeg:dash:role:2011", + RoleToText(*role_it)); + } return adaptation_set.PassScopedPtr(); } diff --git a/packager/mpd/base/mpd_builder.h b/packager/mpd/base/mpd_builder.h index d4794e1a37..28bb3250aa 100644 --- a/packager/mpd/base/mpd_builder.h +++ b/packager/mpd/base/mpd_builder.h @@ -71,6 +71,8 @@ class MpdBuilder { void AddBaseUrl(const std::string& base_url); /// Adds to the MPD. + /// @param lang is the language of the AdaptationSet. This can be empty for + /// videos, for example. /// @return The new adaptation set, which is owned by this instance. AdaptationSet* AddAdaptationSet(const std::string& lang); @@ -153,6 +155,19 @@ class MpdBuilder { /// elements to the AdaptationSet element. class AdaptationSet { public: + // The role for this AdaptationSet. These values are used to add a Role + // element to the AdaptationSet with schemeIdUri=urn:mpeg:dash:role:2011. + // See ISO/IEC 23009-1:2012 section 5.8.5.5. + enum Role { + kRoleCaption, + kRoleSubtitle, + kRoleMain, + kRoleAlternate, + kRoleSupplementary, + kRoleCommentary, + kRoleDub + }; + ~AdaptationSet(); /// Create a Representation instance using @a media_info. @@ -169,6 +184,12 @@ class AdaptationSet { /// then the former is used. void AddContentProtectionElement(const ContentProtectionElement& element); + /// Set the Role element for this AdaptationSet. + /// The Role element's is schemeIdUri='urn:mpeg:dash:role:2011'. + /// See ISO/IEC 23009-1:2012 section 5.8.5.5. + /// @param role of this AdaptationSet. + void AddRole(Role role); + /// Makes a copy of AdaptationSet xml element with its child Representation /// and ContentProtection elements. /// @return On success returns a non-NULL ScopedXmlPtr. Otherwise returns a @@ -255,6 +276,9 @@ class AdaptationSet { // in this set. std::set picture_aspect_ratio_; + // The roles of this AdaptationSet. + std::set roles_; + DISALLOW_COPY_AND_ASSIGN(AdaptationSet); }; diff --git a/packager/mpd/base/mpd_builder_unittest.cc b/packager/mpd/base/mpd_builder_unittest.cc index b7c45de722..885bc0aa1a 100644 --- a/packager/mpd/base/mpd_builder_unittest.cc +++ b/packager/mpd/base/mpd_builder_unittest.cc @@ -487,6 +487,36 @@ TEST_F(CommonMpdBuilderTest, CheckAdaptationSetId) { ASSERT_NO_FATAL_FAILURE(CheckIdEqual(kAdaptationSetId, &adaptation_set)); } +// Verify AdaptationSet::AddRole() works for "main" role. +TEST_F(CommonMpdBuilderTest, AdaptationAddRoleElementMain) { + MpdBuilder mpd_builder(MpdBuilder::kStatic, MpdOptions()); + AdaptationSet* adaptation_set = mpd_builder.AddAdaptationSet(""); + + adaptation_set->AddRole(AdaptationSet::kRoleMain); + xml::ScopedXmlPtr::type adaptation_set_xml(adaptation_set->GetXml()); + // The empty contentType is sort of a side effect of being able to generate an + // MPD without adding any Representations. + const char kExpectedOutput[] = + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; + std::string mpd_output; + EXPECT_TRUE(mpd_builder.ToString(&mpd_output)); + EXPECT_TRUE(XmlEqual(kExpectedOutput, mpd_output)) + << "Expected " << kExpectedOutput << std::endl << "Actual: " << mpd_output; +} + // Verify that if all video Representations in an AdaptationSet have the same // frame rate, AdaptationSet also has a frameRate attribute. TEST_F(CommonMpdBuilderTest, AdapatationSetFrameRate) { diff --git a/packager/mpd/base/xml/xml_node.cc b/packager/mpd/base/xml/xml_node.cc index 12bbda4a9b..b97cc8629a 100644 --- a/packager/mpd/base/xml/xml_node.cc +++ b/packager/mpd/base/xml/xml_node.cc @@ -320,6 +320,14 @@ AdaptationSetXmlNode::AdaptationSetXmlNode() : RepresentationBaseXmlNode("AdaptationSet") {} AdaptationSetXmlNode::~AdaptationSetXmlNode() {} +void AdaptationSetXmlNode::AddRoleElement(const std::string& scheme_id_uri, + const std::string& value) { + XmlNode role("Role"); + role.SetStringAttribute("schemeIdUri", scheme_id_uri); + role.SetStringAttribute("value", value); + AddChild(role.PassScopedPtr()); +} + RepresentationXmlNode::RepresentationXmlNode() : RepresentationBaseXmlNode("Representation") {} RepresentationXmlNode::~RepresentationXmlNode() {} diff --git a/packager/mpd/base/xml/xml_node.h b/packager/mpd/base/xml/xml_node.h index 56f025c7e2..c94b03bb9c 100644 --- a/packager/mpd/base/xml/xml_node.h +++ b/packager/mpd/base/xml/xml_node.h @@ -117,6 +117,11 @@ class AdaptationSetXmlNode : public RepresentationBaseXmlNode { AdaptationSetXmlNode(); virtual ~AdaptationSetXmlNode(); + /// @param scheme_id_uri is content of the schemeIdUri attribute. + /// @param value is the content of value attribute. + void AddRoleElement(const std::string& scheme_id_uri, + const std::string& value); + private: DISALLOW_COPY_AND_ASSIGN(AdaptationSetXmlNode); };