Moves AdaptationSet@id management out of Period class
It is now managed in SimpleMpdNotifier. This avoids unnecessary increment in AdaptationSet id counter. Also makes sure the AdaptationSet is sorted by id in XML output. Change-Id: Ibcd0b047a71c19cd30ad7d8af9a2ed0bb05e043e
This commit is contained in:
parent
3b5b2bccca
commit
6ffa344553
|
@ -14,7 +14,7 @@
|
|||
</SegmentBase>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
<AdaptationSet id="2" contentType="audio" subsegmentAlignment="true">
|
||||
<AdaptationSet id="1" contentType="audio" subsegmentAlignment="true">
|
||||
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
|
||||
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b">
|
||||
<cenc:pssh>AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==</cenc:pssh>
|
||||
|
@ -41,7 +41,7 @@
|
|||
</SegmentBase>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
<AdaptationSet id="2" contentType="audio" subsegmentAlignment="true">
|
||||
<AdaptationSet id="1" contentType="audio" subsegmentAlignment="true">
|
||||
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
|
||||
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b">
|
||||
<cenc:pssh>AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==</cenc:pssh>
|
||||
|
|
|
@ -23,6 +23,15 @@
|
|||
</AdaptationSet>
|
||||
</Period>
|
||||
<Period id="1" duration="PT0.734067S">
|
||||
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9">
|
||||
<Representation id="0" bandwidth="869044" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
|
||||
<SegmentTemplate timescale="30000" presentationTimeOffset="62061" initialization="bear-640x360-video-init.mp4" media="bear-640x360-video-$Number$.m4s" startNumber="3">
|
||||
<SegmentTimeline>
|
||||
<S t="62062" d="22022"/>
|
||||
</SegmentTimeline>
|
||||
</SegmentTemplate>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
<AdaptationSet id="1" contentType="audio" segmentAlignment="true">
|
||||
<Representation id="1" bandwidth="108486" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
|
||||
|
@ -33,14 +42,5 @@
|
|||
</SegmentTemplate>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9">
|
||||
<Representation id="0" bandwidth="869044" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
|
||||
<SegmentTemplate timescale="30000" presentationTimeOffset="62061" initialization="bear-640x360-video-init.mp4" media="bear-640x360-video-$Number$.m4s" startNumber="3">
|
||||
<SegmentTimeline>
|
||||
<S t="62062" d="22022"/>
|
||||
</SegmentTimeline>
|
||||
</SegmentTemplate>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
</Period>
|
||||
</MPD>
|
||||
|
|
|
@ -167,12 +167,10 @@ class RepresentationStateChangeListenerImpl
|
|||
|
||||
} // namespace
|
||||
|
||||
AdaptationSet::AdaptationSet(uint32_t adaptation_set_id,
|
||||
const std::string& lang,
|
||||
AdaptationSet::AdaptationSet(const std::string& lang,
|
||||
const MpdOptions& mpd_options,
|
||||
base::AtomicSequenceNumber* counter)
|
||||
: representation_counter_(counter),
|
||||
id_(adaptation_set_id),
|
||||
lang_(lang),
|
||||
mpd_options_(mpd_options),
|
||||
segments_aligned_(kSegmentAlignmentUnknown),
|
||||
|
@ -245,7 +243,8 @@ xml::scoped_xml_ptr<xmlNode> AdaptationSet::GetXml() {
|
|||
bool suppress_representation_height = false;
|
||||
bool suppress_representation_frame_rate = false;
|
||||
|
||||
adaptation_set.SetId(id_);
|
||||
if (id_)
|
||||
adaptation_set.SetId(id_.value());
|
||||
adaptation_set.SetStringAttribute("contentType", content_type_);
|
||||
if (!lang_.empty() && lang_ != "und") {
|
||||
adaptation_set.SetStringAttribute("lang", LanguageToShortestForm(lang_));
|
||||
|
@ -296,22 +295,24 @@ xml::scoped_xml_ptr<xmlNode> AdaptationSet::GetXml() {
|
|||
return xml::scoped_xml_ptr<xmlNode>();
|
||||
}
|
||||
|
||||
if (!trick_play_reference_ids_.empty()) {
|
||||
std::string id_string;
|
||||
for (uint32_t id : trick_play_reference_ids_) {
|
||||
id_string += std::to_string(id) + ",";
|
||||
}
|
||||
DCHECK(!id_string.empty());
|
||||
id_string.resize(id_string.size() - 1);
|
||||
std::string trick_play_reference_ids;
|
||||
for (const AdaptationSet* adaptation_set : trick_play_references_) {
|
||||
if (!trick_play_reference_ids.empty())
|
||||
trick_play_reference_ids += ',';
|
||||
CHECK(adaptation_set->has_id());
|
||||
trick_play_reference_ids += std::to_string(adaptation_set->id());
|
||||
}
|
||||
if (!trick_play_reference_ids.empty()) {
|
||||
adaptation_set.AddEssentialProperty(
|
||||
"http://dashif.org/guidelines/trickmode", id_string);
|
||||
"http://dashif.org/guidelines/trickmode", trick_play_reference_ids);
|
||||
}
|
||||
|
||||
std::string switching_ids;
|
||||
for (uint32_t id : adaptation_set_switching_ids_) {
|
||||
for (const AdaptationSet* adaptation_set : switchable_adaptation_sets_) {
|
||||
if (!switching_ids.empty())
|
||||
switching_ids += ',';
|
||||
switching_ids += base::UintToString(id);
|
||||
CHECK(adaptation_set->has_id());
|
||||
switching_ids += std::to_string(adaptation_set->id());
|
||||
}
|
||||
if (!switching_ids.empty()) {
|
||||
adaptation_set.AddSupplementalProperty(
|
||||
|
@ -343,8 +344,9 @@ void AdaptationSet::ForceSetSegmentAlignment(bool segment_alignment) {
|
|||
force_set_segment_alignment_ = true;
|
||||
}
|
||||
|
||||
void AdaptationSet::AddAdaptationSetSwitching(uint32_t adaptation_set_id) {
|
||||
adaptation_set_switching_ids_.push_back(adaptation_set_id);
|
||||
void AdaptationSet::AddAdaptationSetSwitching(
|
||||
const AdaptationSet* adaptation_set) {
|
||||
switchable_adaptation_sets_.push_back(adaptation_set);
|
||||
}
|
||||
|
||||
// Check segmentAlignment for Live here. Storing all start_time and duration
|
||||
|
@ -371,8 +373,8 @@ void AdaptationSet::OnSetFrameRateForRepresentation(uint32_t representation_id,
|
|||
RecordFrameRate(frame_duration, timescale);
|
||||
}
|
||||
|
||||
void AdaptationSet::AddTrickPlayReferenceId(uint32_t id) {
|
||||
trick_play_reference_ids_.insert(id);
|
||||
void AdaptationSet::AddTrickPlayReference(const AdaptationSet* adaptation_set) {
|
||||
trick_play_references_.push_back(adaptation_set);
|
||||
}
|
||||
|
||||
const std::list<Representation*> AdaptationSet::GetRepresentations() const {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include "packager/base/atomic_sequence_num.h"
|
||||
#include "packager/base/optional.h"
|
||||
#include "packager/mpd/base/xml/scoped_xml_ptr.h"
|
||||
|
||||
namespace shaka {
|
||||
|
@ -113,12 +114,15 @@ class AdaptationSet {
|
|||
/// attribute.
|
||||
virtual void ForceSetSegmentAlignment(bool segment_alignment);
|
||||
|
||||
/// Adds the id of the adaptation set this adaptation set can switch to.
|
||||
/// @param adaptation_set_id is the id of the switchable adaptation set.
|
||||
virtual void AddAdaptationSetSwitching(uint32_t adaptation_set_id);
|
||||
/// Adds the adaptation set this adaptation set can switch to.
|
||||
/// @param adaptation_set points to the switchable adaptation set.
|
||||
virtual void AddAdaptationSetSwitching(const AdaptationSet* adaptation_set);
|
||||
|
||||
/// @return true if id is set, false otherwise.
|
||||
bool has_id() const { return static_cast<bool>(id_); }
|
||||
|
||||
// Must be unique in the Period.
|
||||
uint32_t id() const { return id_; }
|
||||
uint32_t id() const { return id_.value(); }
|
||||
|
||||
/// Set AdaptationSet@id.
|
||||
/// @param id is the new ID to be set.
|
||||
|
@ -155,10 +159,9 @@ class AdaptationSet {
|
|||
uint32_t frame_duration,
|
||||
uint32_t timescale);
|
||||
|
||||
/// Add the id of the adaptation set this trick play adaptation set belongs
|
||||
/// to.
|
||||
/// @param id the id of the reference (or main) adapation set.
|
||||
virtual void AddTrickPlayReferenceId(uint32_t id);
|
||||
/// Add the adaptation set this trick play adaptation set belongs to.
|
||||
/// @param adaptation_set points to the reference (or main) adapation set.
|
||||
virtual void AddTrickPlayReference(const AdaptationSet* adaptation_set);
|
||||
|
||||
// Return the list of Representations in this AdaptationSet.
|
||||
const std::list<Representation*> GetRepresentations() const;
|
||||
|
@ -167,15 +170,13 @@ class AdaptationSet {
|
|||
bool IsVideo() const;
|
||||
|
||||
protected:
|
||||
/// @param adaptation_set_id is an ID number for this AdaptationSet.
|
||||
/// @param lang is the language of this AdaptationSet. Mainly relevant for
|
||||
/// audio.
|
||||
/// @param mpd_options is the options for this MPD.
|
||||
/// @param mpd_type is the type of this MPD.
|
||||
/// @param representation_counter is a Counter for assigning ID numbers to
|
||||
/// Representation. It can not be NULL.
|
||||
AdaptationSet(uint32_t adaptation_set_id,
|
||||
const std::string& lang,
|
||||
AdaptationSet(const std::string& lang,
|
||||
const MpdOptions& mpd_options,
|
||||
base::AtomicSequenceNumber* representation_counter);
|
||||
|
||||
|
@ -235,12 +236,12 @@ class AdaptationSet {
|
|||
|
||||
base::AtomicSequenceNumber* const representation_counter_;
|
||||
|
||||
uint32_t id_;
|
||||
base::Optional<uint32_t> id_;
|
||||
const std::string lang_;
|
||||
const MpdOptions& mpd_options_;
|
||||
|
||||
// The ids of the adaptation sets this adaptation set can switch to.
|
||||
std::vector<uint32_t> adaptation_set_switching_ids_;
|
||||
// An array of adaptation sets this adaptation set can switch to.
|
||||
std::vector<const AdaptationSet*> switchable_adaptation_sets_;
|
||||
|
||||
// Video widths and heights of Representations. Note that this is a set; if
|
||||
// there is only 1 resolution, then @width & @height should be set, otherwise
|
||||
|
@ -286,11 +287,11 @@ class AdaptationSet {
|
|||
// reasonable and may cause an out-of-memory problem.
|
||||
RepresentationTimeline representation_segment_start_times_;
|
||||
|
||||
// Record the reference id for the original adaptation sets the trick play
|
||||
// stream belongs to. This is a set because the trick play streams may be for
|
||||
// multiple AdaptationSets (e.g. SD and HD videos in different AdaptationSets
|
||||
// can share the same trick play stream.)
|
||||
std::set<uint32_t> trick_play_reference_ids_;
|
||||
// Record the original AdaptationSets the trick play stream belongs to. There
|
||||
// can be more than one reference AdaptationSets as multiple streams e.g. SD
|
||||
// and HD videos in different AdaptationSets can share the same trick play
|
||||
// stream.
|
||||
std::vector<const AdaptationSet*> trick_play_references_;
|
||||
};
|
||||
|
||||
} // namespace shaka
|
||||
|
|
|
@ -21,16 +21,14 @@ using ::testing::Not;
|
|||
namespace shaka {
|
||||
|
||||
namespace {
|
||||
const uint32_t kAnyAdaptationSetId = 1;
|
||||
const char kNoLanguage[] = "";
|
||||
} // namespace
|
||||
|
||||
class AdaptationSetTest : public ::testing::Test {
|
||||
public:
|
||||
std::unique_ptr<AdaptationSet> CreateAdaptationSet(uint32_t adaptation_set_id,
|
||||
const std::string& lang) {
|
||||
return std::unique_ptr<AdaptationSet>(new AdaptationSet(
|
||||
adaptation_set_id, lang, mpd_options_, &representation_counter_));
|
||||
std::unique_ptr<AdaptationSet> CreateAdaptationSet(const std::string& lang) {
|
||||
return std::unique_ptr<AdaptationSet>(
|
||||
new AdaptationSet(lang, mpd_options_, &representation_counter_));
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -49,15 +47,24 @@ class LiveAdaptationSetTest : public AdaptationSetTest {
|
|||
};
|
||||
|
||||
TEST_F(AdaptationSetTest, AddAdaptationSetSwitching) {
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
adaptation_set->AddAdaptationSetSwitching(1);
|
||||
adaptation_set->AddAdaptationSetSwitching(2);
|
||||
adaptation_set->AddAdaptationSetSwitching(8);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
|
||||
auto adaptation_set_1 = CreateAdaptationSet(kNoLanguage);
|
||||
adaptation_set_1->set_id(1);
|
||||
adaptation_set->AddAdaptationSetSwitching(adaptation_set_1.get());
|
||||
|
||||
auto adaptation_set_2 = CreateAdaptationSet(kNoLanguage);
|
||||
adaptation_set_2->set_id(2);
|
||||
adaptation_set->AddAdaptationSetSwitching(adaptation_set_2.get());
|
||||
|
||||
auto adaptation_set_8 = CreateAdaptationSet(kNoLanguage);
|
||||
adaptation_set_8->set_id(8);
|
||||
adaptation_set->AddAdaptationSetSwitching(adaptation_set_8.get());
|
||||
|
||||
// The empty contentType is sort of a side effect of being able to generate an
|
||||
// MPD without adding any Representations.
|
||||
const char kExpectedOutput[] =
|
||||
"<AdaptationSet id=\"1\" contentType=\"\">"
|
||||
"<AdaptationSet contentType=\"\">"
|
||||
" <SupplementalProperty "
|
||||
" schemeIdUri=\"urn:mpeg:dash:adaptation-set-switching:2016\" "
|
||||
" value=\"1,2,8\"/>"
|
||||
|
@ -80,7 +87,7 @@ TEST_F(AdaptationSetTest, CheckAdaptationSetVideoContentType) {
|
|||
"}\n"
|
||||
"container_type: CONTAINER_MP4\n";
|
||||
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
adaptation_set->AddRepresentation(ConvertToMediaInfo(kVideoMediaInfo));
|
||||
EXPECT_THAT(adaptation_set->GetXml().get(),
|
||||
AttributeEqual("contentType", "video"));
|
||||
|
@ -98,7 +105,7 @@ TEST_F(AdaptationSetTest, CheckAdaptationSetAudioContentType) {
|
|||
"}\n"
|
||||
"container_type: CONTAINER_MP4\n";
|
||||
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
adaptation_set->AddRepresentation(ConvertToMediaInfo(kAudioMediaInfo));
|
||||
EXPECT_THAT(adaptation_set->GetXml().get(),
|
||||
AttributeEqual("contentType", "audio"));
|
||||
|
@ -114,7 +121,7 @@ TEST_F(AdaptationSetTest, CheckAdaptationSetTextContentType) {
|
|||
"}\n"
|
||||
"container_type: CONTAINER_TEXT\n";
|
||||
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, "en");
|
||||
auto adaptation_set = CreateAdaptationSet("en");
|
||||
adaptation_set->AddRepresentation(ConvertToMediaInfo(kTextMediaInfo));
|
||||
EXPECT_THAT(adaptation_set->GetXml().get(),
|
||||
AttributeEqual("contentType", "text"));
|
||||
|
@ -133,7 +140,7 @@ TEST_F(AdaptationSetTest, CopyRepresentation) {
|
|||
"}\n"
|
||||
"container_type: CONTAINER_MP4\n";
|
||||
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
Representation* representation =
|
||||
adaptation_set->AddRepresentation(ConvertToMediaInfo(kVideoMediaInfo));
|
||||
|
||||
|
@ -144,7 +151,7 @@ TEST_F(AdaptationSetTest, CopyRepresentation) {
|
|||
|
||||
// Verify that language passed to the constructor sets the @lang field is set.
|
||||
TEST_F(AdaptationSetTest, CheckLanguageAttributeSet) {
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, "en");
|
||||
auto adaptation_set = CreateAdaptationSet("en");
|
||||
EXPECT_THAT(adaptation_set->GetXml().get(), AttributeEqual("lang", "en"));
|
||||
}
|
||||
|
||||
|
@ -152,26 +159,27 @@ TEST_F(AdaptationSetTest, CheckLanguageAttributeSet) {
|
|||
TEST_F(AdaptationSetTest, CheckConvertLanguageWithSubtag) {
|
||||
// "por-BR" is the long tag for Brazillian Portuguese. The short tag
|
||||
// is "pt-BR", which is what should appear in the manifest.
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, "por-BR");
|
||||
auto adaptation_set = CreateAdaptationSet("por-BR");
|
||||
EXPECT_THAT(adaptation_set->GetXml().get(), AttributeEqual("lang", "pt-BR"));
|
||||
}
|
||||
|
||||
TEST_F(AdaptationSetTest, CheckAdaptationSetId) {
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
const uint32_t kAdaptationSetId = 42;
|
||||
auto adaptation_set = CreateAdaptationSet(kAdaptationSetId, kNoLanguage);
|
||||
adaptation_set->set_id(kAdaptationSetId);
|
||||
EXPECT_THAT(adaptation_set->GetXml().get(),
|
||||
AttributeEqual("id", std::to_string(kAdaptationSetId)));
|
||||
}
|
||||
|
||||
// Verify AdaptationSet::AddRole() works for "main" role.
|
||||
TEST_F(AdaptationSetTest, AdaptationAddRoleElementMain) {
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
adaptation_set->AddRole(AdaptationSet::kRoleMain);
|
||||
|
||||
// The empty contentType is sort of a side effect of being able to generate an
|
||||
// MPD without adding any Representations.
|
||||
const char kExpectedOutput[] =
|
||||
"<AdaptationSet id=\"1\" contentType=\"\">\n"
|
||||
"<AdaptationSet contentType=\"\">\n"
|
||||
" <Role schemeIdUri=\"urn:mpeg:dash:role:2011\" value=\"main\"/>\n"
|
||||
"</AdaptationSet>";
|
||||
EXPECT_THAT(adaptation_set->GetXml().get(), XmlNodeEqual(kExpectedOutput));
|
||||
|
@ -180,7 +188,7 @@ TEST_F(AdaptationSetTest, AdaptationAddRoleElementMain) {
|
|||
// Add Role, ContentProtection, and Representation elements. Verify that
|
||||
// ContentProtection -> Role -> Representation are in order.
|
||||
TEST_F(AdaptationSetTest, CheckContentProtectionRoleRepresentationOrder) {
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
adaptation_set->AddRole(AdaptationSet::kRoleMain);
|
||||
ContentProtectionElement any_content_protection;
|
||||
any_content_protection.scheme_id_uri = "any_scheme";
|
||||
|
@ -197,7 +205,7 @@ TEST_F(AdaptationSetTest, CheckContentProtectionRoleRepresentationOrder) {
|
|||
|
||||
xml::scoped_xml_ptr<xmlNode> adaptation_set_xml(adaptation_set->GetXml());
|
||||
const char kExpectedOutput[] =
|
||||
"<AdaptationSet id=\"1\" contentType=\"audio\">\n"
|
||||
"<AdaptationSet contentType=\"audio\">\n"
|
||||
" <ContentProtection schemeIdUri=\"any_scheme\"/>\n"
|
||||
" <Role schemeIdUri=\"urn:mpeg:dash:role:2011\" value=\"main\"/>\n"
|
||||
" <Representation id=\"0\" bandwidth=\"0\" codecs=\"mp4a.40.2\"\n"
|
||||
|
@ -232,7 +240,7 @@ TEST_F(AdaptationSetTest, AdapatationSetFrameRate) {
|
|||
" frame_duration: 3\n"
|
||||
"}\n"
|
||||
"container_type: 1\n";
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
ASSERT_TRUE(
|
||||
adaptation_set->AddRepresentation(ConvertToMediaInfo(kVideoMediaInfo1)));
|
||||
ASSERT_TRUE(
|
||||
|
@ -265,7 +273,7 @@ TEST_F(AdaptationSetTest, AdapatationSetMaxFrameRate) {
|
|||
" frame_duration: 200\n"
|
||||
"}\n"
|
||||
"container_type: 1\n";
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
ASSERT_TRUE(adaptation_set->AddRepresentation(
|
||||
ConvertToMediaInfo(kVideoMediaInfo30fps)));
|
||||
ASSERT_TRUE(adaptation_set->AddRepresentation(
|
||||
|
@ -307,7 +315,7 @@ TEST_F(AdaptationSetTest,
|
|||
"}\n"
|
||||
"container_type: 1\n";
|
||||
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
Representation* representation_480p =
|
||||
adaptation_set->AddRepresentation(ConvertToMediaInfo(k480pMediaInfo));
|
||||
Representation* representation_360p =
|
||||
|
@ -391,7 +399,7 @@ TEST_F(AdaptationSetTest, AdaptationSetParAllSame) {
|
|||
"}\n"
|
||||
"container_type: 1\n";
|
||||
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
ASSERT_TRUE(
|
||||
adaptation_set->AddRepresentation(ConvertToMediaInfo(k480pVideoInfo)));
|
||||
ASSERT_TRUE(
|
||||
|
@ -432,7 +440,7 @@ TEST_F(AdaptationSetTest, AdaptationSetParDifferent) {
|
|||
"}\n"
|
||||
"container_type: 1\n";
|
||||
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
ASSERT_TRUE(
|
||||
adaptation_set->AddRepresentation(ConvertToMediaInfo(k16by9VideoInfo)));
|
||||
ASSERT_TRUE(
|
||||
|
@ -455,7 +463,7 @@ TEST_F(AdaptationSetTest, AdaptationSetParUnknown) {
|
|||
"}\n"
|
||||
"container_type: 1\n";
|
||||
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
ASSERT_TRUE(adaptation_set->AddRepresentation(
|
||||
ConvertToMediaInfo(kUknownPixelWidthAndHeight)));
|
||||
|
||||
|
@ -487,7 +495,7 @@ TEST_F(AdaptationSetTest, AdapatationSetMaxFrameRateIntegerDivisionEdgeCase) {
|
|||
" frame_duration: 3\n"
|
||||
"}\n"
|
||||
"container_type: 1\n";
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
ASSERT_TRUE(
|
||||
adaptation_set->AddRepresentation(ConvertToMediaInfo(kVideoMediaInfo1)));
|
||||
ASSERT_TRUE(
|
||||
|
@ -552,7 +560,7 @@ TEST_F(AdaptationSetTest, BubbleUpAttributesToAdaptationSet) {
|
|||
"}\n"
|
||||
"container_type: 1\n";
|
||||
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
ASSERT_TRUE(adaptation_set->AddRepresentation(ConvertToMediaInfo(k1080p)));
|
||||
|
||||
xml::scoped_xml_ptr<xmlNode> all_attributes_on_adaptation_set(
|
||||
|
@ -610,7 +618,7 @@ TEST_F(AdaptationSetTest, GetRepresentations) {
|
|||
"}\n"
|
||||
"container_type: 1\n";
|
||||
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
|
||||
Representation* representation1 =
|
||||
adaptation_set->AddRepresentation(ConvertToMediaInfo(kMediaInfo1));
|
||||
|
@ -622,8 +630,7 @@ TEST_F(AdaptationSetTest, GetRepresentations) {
|
|||
EXPECT_THAT(adaptation_set->GetRepresentations(),
|
||||
ElementsAre(representation1, representation2));
|
||||
|
||||
auto new_adaptation_set =
|
||||
CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto new_adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
Representation* new_representation2 =
|
||||
new_adaptation_set->CopyRepresentation(*representation2);
|
||||
Representation* new_representation1 =
|
||||
|
@ -668,7 +675,7 @@ TEST_F(OnDemandAdaptationSetTest, SubsegmentAlignment) {
|
|||
const uint64_t kDuration = 10u;
|
||||
const uint64_t kAnySize = 19834u;
|
||||
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
Representation* representation_480p =
|
||||
adaptation_set->AddRepresentation(ConvertToMediaInfo(k480pMediaInfo));
|
||||
// Add a subsegment immediately before adding the 360p Representation.
|
||||
|
@ -720,7 +727,7 @@ TEST_F(OnDemandAdaptationSetTest, ForceSetsubsegmentAlignment) {
|
|||
" pixel_height: 1\n"
|
||||
"}\n"
|
||||
"container_type: 1\n";
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
Representation* representation_480p =
|
||||
adaptation_set->AddRepresentation(ConvertToMediaInfo(k480pMediaInfo));
|
||||
Representation* representation_360p =
|
||||
|
@ -768,7 +775,7 @@ TEST_F(LiveAdaptationSetTest, SegmentAlignment) {
|
|||
" pixel_height: 1\n"
|
||||
"}\n"
|
||||
"container_type: 1\n";
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
Representation* representation_480p =
|
||||
adaptation_set->AddRepresentation(ConvertToMediaInfo(k480pMediaInfo));
|
||||
Representation* representation_360p =
|
||||
|
@ -815,7 +822,7 @@ TEST_F(OnDemandAdaptationSetTest, AdapatationSetWidthAndHeight) {
|
|||
" frame_duration: 200\n"
|
||||
"}\n"
|
||||
"container_type: 1\n";
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
ASSERT_TRUE(
|
||||
adaptation_set->AddRepresentation(ConvertToMediaInfo(kVideoMediaInfo1)));
|
||||
ASSERT_TRUE(
|
||||
|
@ -849,7 +856,7 @@ TEST_F(OnDemandAdaptationSetTest, AdaptationSetMaxWidthAndMaxHeight) {
|
|||
" frame_duration: 100\n"
|
||||
"}\n"
|
||||
"container_type: 1\n";
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
ASSERT_TRUE(adaptation_set->AddRepresentation(
|
||||
ConvertToMediaInfo(kVideoMediaInfo1080p)));
|
||||
ASSERT_TRUE(adaptation_set->AddRepresentation(
|
||||
|
@ -875,7 +882,7 @@ TEST_F(AdaptationSetTest, SetSampleDuration) {
|
|||
"}\n"
|
||||
"container_type: 1\n";
|
||||
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
|
||||
const MediaInfo video_media_info = ConvertToMediaInfo(kVideoMediaInfo);
|
||||
Representation* representation =
|
||||
|
@ -911,13 +918,13 @@ TEST_F(AdaptationSetTest, AdaptationSetAddContentProtectionAndUpdate) {
|
|||
pssh.content = "any value";
|
||||
content_protection.subelements.push_back(pssh);
|
||||
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
ASSERT_TRUE(adaptation_set->AddRepresentation(
|
||||
ConvertToMediaInfo(kVideoMediaInfo1080p)));
|
||||
adaptation_set->AddContentProtectionElement(content_protection);
|
||||
|
||||
const char kExpectedOutput1[] =
|
||||
"<AdaptationSet id=\"1\" contentType=\"video\" width=\"1920\""
|
||||
"<AdaptationSet contentType=\"video\" width=\"1920\""
|
||||
" height=\"1080\" frameRate=\"3000/100\">"
|
||||
" <ContentProtection"
|
||||
" schemeIdUri=\"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed\""
|
||||
|
@ -932,7 +939,7 @@ TEST_F(AdaptationSetTest, AdaptationSetAddContentProtectionAndUpdate) {
|
|||
adaptation_set->UpdateContentProtectionPssh(
|
||||
"edef8ba9-79d6-4ace-a3c8-27dcd51d21ed", "new pssh value");
|
||||
const char kExpectedOutput2[] =
|
||||
"<AdaptationSet id=\"1\" contentType=\"video\" width=\"1920\""
|
||||
"<AdaptationSet contentType=\"video\" width=\"1920\""
|
||||
" height=\"1080\" frameRate=\"3000/100\">"
|
||||
" <ContentProtection"
|
||||
" schemeIdUri=\"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed\""
|
||||
|
@ -967,13 +974,13 @@ TEST_F(AdaptationSetTest, UpdateToRemovePsshElement) {
|
|||
"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed";
|
||||
content_protection.value = "some value";
|
||||
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
ASSERT_TRUE(adaptation_set->AddRepresentation(
|
||||
ConvertToMediaInfo(kVideoMediaInfo1080p)));
|
||||
adaptation_set->AddContentProtectionElement(content_protection);
|
||||
|
||||
const char kExpectedOutput1[] =
|
||||
"<AdaptationSet id=\"1\" contentType=\"video\" width=\"1920\""
|
||||
"<AdaptationSet contentType=\"video\" width=\"1920\""
|
||||
" height=\"1080\" frameRate=\"3000/100\">"
|
||||
" <ContentProtection"
|
||||
" schemeIdUri=\"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed\""
|
||||
|
@ -987,7 +994,7 @@ TEST_F(AdaptationSetTest, UpdateToRemovePsshElement) {
|
|||
adaptation_set->UpdateContentProtectionPssh(
|
||||
"edef8ba9-79d6-4ace-a3c8-27dcd51d21ed", "added pssh value");
|
||||
const char kExpectedOutput2[] =
|
||||
"<AdaptationSet id=\"1\" contentType=\"video\" width=\"1920\""
|
||||
"<AdaptationSet contentType=\"video\" width=\"1920\""
|
||||
" height=\"1080\" frameRate=\"3000/100\">"
|
||||
" <ContentProtection"
|
||||
" schemeIdUri=\"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed\""
|
||||
|
@ -1030,7 +1037,7 @@ TEST_F(OnDemandAdaptationSetTest,
|
|||
"container_type: CONTAINER_MP4\n";
|
||||
|
||||
const char kExpectedOutput[] =
|
||||
"<AdaptationSet id=\"1\" contentType=\"audio\">"
|
||||
"<AdaptationSet contentType=\"audio\">"
|
||||
" <Representation id=\"0\" bandwidth=\"195857\" codecs=\"mp4a.40.2\""
|
||||
" mimeType=\"audio/mp4\" audioSamplingRate=\"44100\">"
|
||||
" <AudioChannelConfiguration"
|
||||
|
@ -1054,7 +1061,7 @@ TEST_F(OnDemandAdaptationSetTest,
|
|||
pssh.content = "anything";
|
||||
content_protection.subelements.push_back(pssh);
|
||||
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, kNoLanguage);
|
||||
auto adaptation_set = CreateAdaptationSet(kNoLanguage);
|
||||
Representation* audio_representation =
|
||||
adaptation_set->AddRepresentation(ConvertToMediaInfo(kTestMediaInfo));
|
||||
ASSERT_TRUE(audio_representation);
|
||||
|
@ -1076,7 +1083,7 @@ TEST_F(OnDemandAdaptationSetTest, Text) {
|
|||
"container_type: CONTAINER_TEXT\n";
|
||||
|
||||
const char kExpectedOutput[] =
|
||||
"<AdaptationSet id=\"1\" contentType=\"text\" lang=\"en\">"
|
||||
"<AdaptationSet contentType=\"text\" lang=\"en\">"
|
||||
" <Role schemeIdUri=\"urn:mpeg:dash:role:2011\""
|
||||
" value=\"subtitle\"/>\n"
|
||||
" <Representation id=\"0\" bandwidth=\"1000\""
|
||||
|
@ -1085,7 +1092,7 @@ TEST_F(OnDemandAdaptationSetTest, Text) {
|
|||
" </Representation>"
|
||||
"</AdaptationSet>";
|
||||
|
||||
auto adaptation_set = CreateAdaptationSet(kAnyAdaptationSetId, "en");
|
||||
auto adaptation_set = CreateAdaptationSet("en");
|
||||
Representation* text_representation =
|
||||
adaptation_set->AddRepresentation(ConvertToMediaInfo(kTextMediaInfo));
|
||||
ASSERT_TRUE(text_representation);
|
||||
|
|
|
@ -17,14 +17,10 @@ MockPeriod::MockPeriod(uint32_t period_id, double start_time_in_seconds)
|
|||
: Period(period_id,
|
||||
start_time_in_seconds,
|
||||
kDefaultMpdOptions,
|
||||
&sequence_counter_,
|
||||
&sequence_counter_) {}
|
||||
|
||||
MockAdaptationSet::MockAdaptationSet(uint32_t adaptation_set_id)
|
||||
: AdaptationSet(adaptation_set_id,
|
||||
kEmptyLang,
|
||||
kDefaultMpdOptions,
|
||||
&sequence_counter_) {}
|
||||
MockAdaptationSet::MockAdaptationSet()
|
||||
: AdaptationSet(kEmptyLang, kDefaultMpdOptions, &sequence_counter_) {}
|
||||
MockAdaptationSet::~MockAdaptationSet() {}
|
||||
|
||||
MockRepresentation::MockRepresentation(uint32_t representation_id)
|
||||
|
|
|
@ -43,8 +43,7 @@ class MockPeriod : public Period {
|
|||
|
||||
class MockAdaptationSet : public AdaptationSet {
|
||||
public:
|
||||
// |adaptation_set_id| is the id for the AdaptationSet.
|
||||
explicit MockAdaptationSet(uint32_t adaptation_set_id);
|
||||
MockAdaptationSet();
|
||||
~MockAdaptationSet() override;
|
||||
|
||||
MOCK_METHOD1(AddRepresentation, Representation*(const MediaInfo& media_info));
|
||||
|
@ -56,8 +55,10 @@ class MockAdaptationSet : public AdaptationSet {
|
|||
void(const std::string& drm_uuid, const std::string& pssh));
|
||||
MOCK_METHOD1(AddRole, void(AdaptationSet::Role role));
|
||||
MOCK_METHOD1(ForceSetSegmentAlignment, void(bool segment_alignment));
|
||||
MOCK_METHOD1(AddAdaptationSetSwitching, void(uint32_t adaptation_set_id));
|
||||
MOCK_METHOD1(AddTrickPlayReferenceId, void(uint32_t id));
|
||||
MOCK_METHOD1(AddAdaptationSetSwitching,
|
||||
void(const AdaptationSet* adaptation_set));
|
||||
MOCK_METHOD1(AddTrickPlayReference,
|
||||
void(const AdaptationSet* adaptation_set));
|
||||
|
||||
private:
|
||||
// Only for constructing the super class. Not used for testing.
|
||||
|
|
|
@ -131,9 +131,9 @@ Period* MpdBuilder::GetOrCreatePeriod(double start_time_in_seconds) {
|
|||
if (match)
|
||||
return period.get();
|
||||
}
|
||||
periods_.emplace_back(
|
||||
new Period(period_counter_.GetNext(), start_time_in_seconds, mpd_options_,
|
||||
&adaptation_set_counter_, &representation_counter_));
|
||||
periods_.emplace_back(new Period(period_counter_.GetNext(),
|
||||
start_time_in_seconds, mpd_options_,
|
||||
&representation_counter_));
|
||||
return periods_.back().get();
|
||||
}
|
||||
|
||||
|
|
|
@ -120,7 +120,6 @@ class MpdBuilder {
|
|||
std::string availability_start_time_;
|
||||
|
||||
base::AtomicSequenceNumber period_counter_;
|
||||
base::AtomicSequenceNumber adaptation_set_counter_;
|
||||
base::AtomicSequenceNumber representation_counter_;
|
||||
|
||||
// By default, this returns the current time. This can be injected for
|
||||
|
|
|
@ -37,12 +37,10 @@ std::set<std::string> GetUUIDs(
|
|||
Period::Period(uint32_t period_id,
|
||||
double start_time_in_seconds,
|
||||
const MpdOptions& mpd_options,
|
||||
base::AtomicSequenceNumber* adaptation_set_counter,
|
||||
base::AtomicSequenceNumber* representation_counter)
|
||||
: id_(period_id),
|
||||
start_time_in_seconds_(start_time_in_seconds),
|
||||
mpd_options_(mpd_options),
|
||||
adaptation_set_counter_(adaptation_set_counter),
|
||||
representation_counter_(representation_counter) {}
|
||||
|
||||
AdaptationSet* Period::GetOrCreateAdaptationSet(
|
||||
|
@ -66,10 +64,9 @@ AdaptationSet* Period::GetOrCreateAdaptationSet(
|
|||
}
|
||||
// None of the adaptation sets match with the new content protection.
|
||||
// Need a new one.
|
||||
std::string language = GetLanguage(media_info);
|
||||
const std::string language = GetLanguage(media_info);
|
||||
std::unique_ptr<AdaptationSet> new_adaptation_set =
|
||||
NewAdaptationSet(adaptation_set_counter_->GetNext(), language,
|
||||
mpd_options_, representation_counter_);
|
||||
NewAdaptationSet(language, mpd_options_, representation_counter_);
|
||||
if (!SetNewAdaptationSetAttributes(language, media_info, adaptation_sets,
|
||||
new_adaptation_set.get())) {
|
||||
return nullptr;
|
||||
|
@ -83,25 +80,35 @@ AdaptationSet* Period::GetOrCreateAdaptationSet(
|
|||
for (AdaptationSet* adaptation_set : adaptation_sets) {
|
||||
if (protected_adaptation_set_map_.Switchable(*adaptation_set,
|
||||
*new_adaptation_set)) {
|
||||
adaptation_set->AddAdaptationSetSwitching(new_adaptation_set->id());
|
||||
new_adaptation_set->AddAdaptationSetSwitching(adaptation_set->id());
|
||||
adaptation_set->AddAdaptationSetSwitching(new_adaptation_set.get());
|
||||
new_adaptation_set->AddAdaptationSetSwitching(adaptation_set);
|
||||
}
|
||||
}
|
||||
}
|
||||
AdaptationSet* adaptation_set_ptr = new_adaptation_set.get();
|
||||
adaptation_sets.push_back(adaptation_set_ptr);
|
||||
adaptation_set_map_[adaptation_set_ptr->id()] = std::move(new_adaptation_set);
|
||||
adaptation_sets_.emplace_back(std::move(new_adaptation_set));
|
||||
return adaptation_set_ptr;
|
||||
}
|
||||
|
||||
xml::scoped_xml_ptr<xmlNode> Period::GetXml(bool output_period_duration) const {
|
||||
xml::scoped_xml_ptr<xmlNode> Period::GetXml(bool output_period_duration) {
|
||||
adaptation_sets_.sort(
|
||||
[](const std::unique_ptr<AdaptationSet>& adaptation_set_a,
|
||||
const std::unique_ptr<AdaptationSet>& adaptation_set_b) {
|
||||
if (!adaptation_set_a->has_id())
|
||||
return false;
|
||||
if (!adaptation_set_b->has_id())
|
||||
return true;
|
||||
return adaptation_set_a->id() < adaptation_set_b->id();
|
||||
});
|
||||
|
||||
xml::XmlNode period("Period");
|
||||
|
||||
// Required for 'dynamic' MPDs.
|
||||
period.SetId(id_);
|
||||
// Iterate thru AdaptationSets and add them to one big Period element.
|
||||
for (const auto& adaptation_set_pair : adaptation_set_map_) {
|
||||
xml::scoped_xml_ptr<xmlNode> child(adaptation_set_pair.second->GetXml());
|
||||
for (const auto& adaptation_set : adaptation_sets_) {
|
||||
xml::scoped_xml_ptr<xmlNode> child(adaptation_set->GetXml());
|
||||
if (!child || !period.AddChild(std::move(child)))
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -118,19 +125,18 @@ xml::scoped_xml_ptr<xmlNode> Period::GetXml(bool output_period_duration) const {
|
|||
|
||||
const std::list<AdaptationSet*> Period::GetAdaptationSets() const {
|
||||
std::list<AdaptationSet*> adaptation_sets;
|
||||
for (const auto& adaptation_set_pair : adaptation_set_map_) {
|
||||
adaptation_sets.push_back(adaptation_set_pair.second.get());
|
||||
for (const auto& adaptation_set : adaptation_sets_) {
|
||||
adaptation_sets.push_back(adaptation_set.get());
|
||||
}
|
||||
return adaptation_sets;
|
||||
}
|
||||
|
||||
std::unique_ptr<AdaptationSet> Period::NewAdaptationSet(
|
||||
uint32_t adaptation_set_id,
|
||||
const std::string& language,
|
||||
const MpdOptions& options,
|
||||
base::AtomicSequenceNumber* representation_counter) {
|
||||
return std::unique_ptr<AdaptationSet>(new AdaptationSet(
|
||||
adaptation_set_id, language, options, representation_counter));
|
||||
return std::unique_ptr<AdaptationSet>(
|
||||
new AdaptationSet(language, options, representation_counter));
|
||||
}
|
||||
|
||||
bool Period::SetNewAdaptationSetAttributes(
|
||||
|
@ -152,14 +158,14 @@ bool Period::SetNewAdaptationSetAttributes(
|
|||
}
|
||||
|
||||
if (media_info.video_info().has_playback_rate()) {
|
||||
uint32_t trick_play_reference_id = 0;
|
||||
if (!FindOriginalAdaptationSetForTrickPlay(media_info,
|
||||
&trick_play_reference_id)) {
|
||||
LOG(ERROR) << "Failed to find main adaptation set for trick play.";
|
||||
const AdaptationSet* trick_play_reference_adaptation_set =
|
||||
FindOriginalAdaptationSetForTrickPlay(media_info);
|
||||
if (!trick_play_reference_adaptation_set) {
|
||||
LOG(ERROR) << "Failed to find original AdaptationSet for trick play.";
|
||||
return false;
|
||||
}
|
||||
DCHECK_NE(new_adaptation_set->id(), trick_play_reference_id);
|
||||
new_adaptation_set->AddTrickPlayReferenceId(trick_play_reference_id);
|
||||
new_adaptation_set->AddTrickPlayReference(
|
||||
trick_play_reference_adaptation_set);
|
||||
}
|
||||
} else if (media_info.has_text_info()) {
|
||||
// IOP requires all AdaptationSets to have (sub)segmentAlignment set to
|
||||
|
@ -170,9 +176,8 @@ bool Period::SetNewAdaptationSetAttributes(
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Period::FindOriginalAdaptationSetForTrickPlay(
|
||||
const MediaInfo& media_info,
|
||||
uint32_t* main_adaptation_set_id) {
|
||||
const AdaptationSet* Period::FindOriginalAdaptationSetForTrickPlay(
|
||||
const MediaInfo& media_info) {
|
||||
MediaInfo media_info_no_trickplay = media_info;
|
||||
media_info_no_trickplay.mutable_video_info()->clear_playback_rate();
|
||||
|
||||
|
@ -181,25 +186,24 @@ bool Period::FindOriginalAdaptationSetForTrickPlay(
|
|||
adaptation_set_list_map_[key];
|
||||
for (AdaptationSet* adaptation_set : adaptation_sets) {
|
||||
if (protected_adaptation_set_map_.Match(*adaptation_set, media_info)) {
|
||||
*main_adaptation_set_id = adaptation_set->id();
|
||||
return true;
|
||||
return adaptation_set;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Period::ProtectedAdaptationSetMap::Register(
|
||||
const AdaptationSet& adaptation_set,
|
||||
const MediaInfo& media_info) {
|
||||
DCHECK(!ContainsKey(protected_content_map_, adaptation_set.id()));
|
||||
protected_content_map_[adaptation_set.id()] = media_info.protected_content();
|
||||
DCHECK(!ContainsKey(protected_content_map_, &adaptation_set));
|
||||
protected_content_map_[&adaptation_set] = media_info.protected_content();
|
||||
}
|
||||
|
||||
bool Period::ProtectedAdaptationSetMap::Match(
|
||||
const AdaptationSet& adaptation_set,
|
||||
const MediaInfo& media_info) {
|
||||
const auto protected_content_it =
|
||||
protected_content_map_.find(adaptation_set.id());
|
||||
protected_content_map_.find(&adaptation_set);
|
||||
// If the AdaptationSet ID is not registered in the map, then it is clear
|
||||
// content.
|
||||
if (protected_content_it == protected_content_map_.end())
|
||||
|
@ -214,9 +218,9 @@ bool Period::ProtectedAdaptationSetMap::Switchable(
|
|||
const AdaptationSet& adaptation_set_a,
|
||||
const AdaptationSet& adaptation_set_b) {
|
||||
const auto protected_content_it_a =
|
||||
protected_content_map_.find(adaptation_set_a.id());
|
||||
protected_content_map_.find(&adaptation_set_a);
|
||||
const auto protected_content_it_b =
|
||||
protected_content_map_.find(adaptation_set_b.id());
|
||||
protected_content_map_.find(&adaptation_set_b);
|
||||
|
||||
if (protected_content_it_a == protected_content_map_.end())
|
||||
return protected_content_it_b == protected_content_map_.end();
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <map>
|
||||
|
||||
#include "packager/base/atomic_sequence_num.h"
|
||||
#include "packager/base/optional.h"
|
||||
#include "packager/mpd/base/adaptation_set.h"
|
||||
#include "packager/mpd/base/media_info.pb.h"
|
||||
#include "packager/mpd/base/xml/scoped_xml_ptr.h"
|
||||
|
@ -41,6 +42,8 @@ class Period {
|
|||
/// element. This affects how MediaInfo in AdaptationSets are matched.
|
||||
/// @return the AdaptationSet matching @a media_info if found; otherwise
|
||||
/// return a new AdaptationSet.
|
||||
// TODO(kqyang): Move |content_protection_in_adaptation_set| to Period
|
||||
// constructor.
|
||||
virtual AdaptationSet* GetOrCreateAdaptationSet(
|
||||
const MediaInfo& media_info,
|
||||
bool content_protection_in_adaptation_set);
|
||||
|
@ -48,7 +51,7 @@ class Period {
|
|||
/// Generates <Period> xml element with its child AdaptationSet elements.
|
||||
/// @return On success returns a non-NULL scoped_xml_ptr. Otherwise returns a
|
||||
/// NULL scoped_xml_ptr.
|
||||
xml::scoped_xml_ptr<xmlNode> GetXml(bool output_period_duration) const;
|
||||
xml::scoped_xml_ptr<xmlNode> GetXml(bool output_period_duration);
|
||||
|
||||
/// @return The list of AdaptationSets in this Period.
|
||||
const std::list<AdaptationSet*> GetAdaptationSets() const;
|
||||
|
@ -68,14 +71,11 @@ class Period {
|
|||
/// @param period_id is an ID number for this Period.
|
||||
/// @param start_time_in_seconds is the start time for this Period.
|
||||
/// @param mpd_options is the options for this MPD.
|
||||
/// @param adaptation_set_counter is a counter for assigning ID numbers to
|
||||
/// AdaptationSet. It can not be NULL.
|
||||
/// @param representation_counter is a counter for assigning ID numbers to
|
||||
/// Representation. It can not be NULL.
|
||||
Period(uint32_t period_id,
|
||||
double start_time_in_seconds,
|
||||
const MpdOptions& mpd_options,
|
||||
base::AtomicSequenceNumber* adaptation_set_counter,
|
||||
base::AtomicSequenceNumber* representation_counter);
|
||||
|
||||
private:
|
||||
|
@ -87,7 +87,6 @@ class Period {
|
|||
|
||||
// Calls AdaptationSet constructor. For mock injection.
|
||||
virtual std::unique_ptr<AdaptationSet> NewAdaptationSet(
|
||||
uint32_t adaptation_set_id,
|
||||
const std::string& lang,
|
||||
const MpdOptions& options,
|
||||
base::AtomicSequenceNumber* representation_counter);
|
||||
|
@ -99,24 +98,19 @@ class Period {
|
|||
const std::list<AdaptationSet*>& adaptation_sets,
|
||||
AdaptationSet* new_adaptation_set);
|
||||
|
||||
// Gets the original AdaptationSet which the trick play video belongs
|
||||
// to and returns the id of the original adapatation set.
|
||||
// Gets the original AdaptationSet which the trick play video belongs to.
|
||||
// It is assumed that the corresponding AdaptationSet has been created before
|
||||
// the trick play AdaptationSet.
|
||||
// Returns true if main_adaptation_id is found, otherwise false;
|
||||
bool FindOriginalAdaptationSetForTrickPlay(
|
||||
const MediaInfo& media_info,
|
||||
uint32_t* original_adaptation_set_id);
|
||||
// Returns the original AdaptationSet if found, otherwise returns nullptr;
|
||||
const AdaptationSet* FindOriginalAdaptationSetForTrickPlay(
|
||||
const MediaInfo& media_info);
|
||||
|
||||
const uint32_t id_;
|
||||
const double start_time_in_seconds_;
|
||||
double duration_seconds_ = 0;
|
||||
const MpdOptions& mpd_options_;
|
||||
base::AtomicSequenceNumber* const adaptation_set_counter_;
|
||||
base::AtomicSequenceNumber* const representation_counter_;
|
||||
// adaptation_id => Adaptation map. It also keeps the adaptation_sets_ sorted
|
||||
// by default.
|
||||
std::map<uint32_t, std::unique_ptr<AdaptationSet>> adaptation_set_map_;
|
||||
std::list<std::unique_ptr<AdaptationSet>> adaptation_sets_;
|
||||
// AdaptationSets grouped by a specific adaptation set grouping key.
|
||||
// AdaptationSets with the same key contain identical parameters except
|
||||
// ContentProtection parameters. A single AdaptationSet would be created
|
||||
|
@ -144,8 +138,9 @@ class Period {
|
|||
ProtectedAdaptationSetMap& operator=(const ProtectedAdaptationSetMap&) =
|
||||
delete;
|
||||
|
||||
// AdaptationSet id => ProtectedContent map.
|
||||
std::map<uint32_t, MediaInfo::ProtectedContent> protected_content_map_;
|
||||
// AdaptationSet => ProtectedContent map.
|
||||
std::map<const AdaptationSet*, MediaInfo::ProtectedContent>
|
||||
protected_content_map_;
|
||||
};
|
||||
ProtectedAdaptationSetMap protected_adaptation_set_map_;
|
||||
};
|
||||
|
|
|
@ -26,8 +26,6 @@ namespace shaka {
|
|||
namespace {
|
||||
const uint32_t kDefaultPeriodId = 9u;
|
||||
const double kDefaultPeriodStartTime = 5.6;
|
||||
const uint32_t kDefaultAdaptationSetId = 0u;
|
||||
const uint32_t kTrickPlayAdaptationSetId = 1u;
|
||||
const bool kOutputPeriodDuration = true;
|
||||
|
||||
bool ElementEqual(const Element& lhs, const Element& rhs) {
|
||||
|
@ -80,12 +78,10 @@ class TestablePeriod : public Period {
|
|||
: Period(kDefaultPeriodId,
|
||||
kDefaultPeriodStartTime,
|
||||
mpd_options,
|
||||
&sequence_number_,
|
||||
&sequence_number_) {}
|
||||
|
||||
MOCK_METHOD4(NewAdaptationSet,
|
||||
MOCK_METHOD3(NewAdaptationSet,
|
||||
std::unique_ptr<AdaptationSet>(
|
||||
uint32_t adaptation_set_id,
|
||||
const std::string& lang,
|
||||
const MpdOptions& options,
|
||||
base::AtomicSequenceNumber* representation_counter));
|
||||
|
@ -101,8 +97,7 @@ class PeriodTest : public ::testing::TestWithParam<bool> {
|
|||
public:
|
||||
PeriodTest()
|
||||
: testable_period_(mpd_options_),
|
||||
default_adaptation_set_(
|
||||
new StrictMock<MockAdaptationSet>(kDefaultAdaptationSetId)),
|
||||
default_adaptation_set_(new StrictMock<MockAdaptationSet>()),
|
||||
default_adaptation_set_ptr_(default_adaptation_set_.get()) {}
|
||||
|
||||
void SetUp() override { content_protection_in_adaptation_set_ = GetParam(); }
|
||||
|
@ -130,7 +125,7 @@ TEST_P(PeriodTest, GetXml) {
|
|||
"}\n"
|
||||
"container_type: 1\n";
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _))
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(default_adaptation_set_))));
|
||||
|
||||
ASSERT_EQ(default_adaptation_set_ptr_,
|
||||
|
@ -142,7 +137,7 @@ TEST_P(PeriodTest, GetXml) {
|
|||
"<Period id=\"9\">"
|
||||
// ContentType and Representation elements are populated after
|
||||
// Representation::Init() is called.
|
||||
" <AdaptationSet id=\"0\" contentType=\"\"/>"
|
||||
" <AdaptationSet contentType=\"\"/>"
|
||||
"</Period>";
|
||||
EXPECT_THAT(testable_period_.GetXml(!kOutputPeriodDuration).get(),
|
||||
XmlNodeEqual(kExpectedXml));
|
||||
|
@ -162,7 +157,7 @@ TEST_P(PeriodTest, DynamicMpdGetXml) {
|
|||
"container_type: 1\n";
|
||||
mpd_options_.mpd_type = MpdType::kDynamic;
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _))
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(default_adaptation_set_))));
|
||||
|
||||
ASSERT_EQ(default_adaptation_set_ptr_,
|
||||
|
@ -174,7 +169,7 @@ TEST_P(PeriodTest, DynamicMpdGetXml) {
|
|||
"<Period id=\"9\" start=\"PT5.6S\">"
|
||||
// ContentType and Representation elements are populated after
|
||||
// Representation::Init() is called.
|
||||
" <AdaptationSet id=\"0\" contentType=\"\"/>"
|
||||
" <AdaptationSet contentType=\"\"/>"
|
||||
"</Period>";
|
||||
EXPECT_THAT(testable_period_.GetXml(!kOutputPeriodDuration).get(),
|
||||
XmlNodeEqual(kExpectedXml));
|
||||
|
@ -193,7 +188,7 @@ TEST_P(PeriodTest, SetDurationAndGetXml) {
|
|||
"}\n"
|
||||
"container_type: 1\n";
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _))
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(default_adaptation_set_))));
|
||||
|
||||
ASSERT_EQ(default_adaptation_set_ptr_,
|
||||
|
@ -207,7 +202,7 @@ TEST_P(PeriodTest, SetDurationAndGetXml) {
|
|||
"<Period id=\"9\" duration=\"PT100.234S\">"
|
||||
// ContentType and Representation elements are populated after
|
||||
// Representation::Init() is called.
|
||||
" <AdaptationSet id=\"0\" contentType=\"\"/>"
|
||||
" <AdaptationSet contentType=\"\"/>"
|
||||
"</Period>";
|
||||
EXPECT_THAT(testable_period_.GetXml(kOutputPeriodDuration).get(),
|
||||
XmlNodeEqual(kExpectedXml));
|
||||
|
@ -215,7 +210,7 @@ TEST_P(PeriodTest, SetDurationAndGetXml) {
|
|||
"<Period id=\"9\">"
|
||||
// ContentType and Representation elements are populated after
|
||||
// Representation::Init() is called.
|
||||
" <AdaptationSet id=\"0\" contentType=\"\"/>"
|
||||
" <AdaptationSet contentType=\"\"/>"
|
||||
"</Period>";
|
||||
EXPECT_THAT(testable_period_.GetXml(!kOutputPeriodDuration).get(),
|
||||
XmlNodeEqual(kExpectedXmlSuppressDuration));
|
||||
|
@ -230,7 +225,7 @@ TEST_P(PeriodTest, Text) {
|
|||
"}\n"
|
||||
"container_type: CONTAINER_TEXT\n";
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, Eq("en"), _, _))
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(Eq("en"), _, _))
|
||||
.WillOnce(Return(ByMove(std::move(default_adaptation_set_))));
|
||||
EXPECT_CALL(*default_adaptation_set_ptr_, ForceSetSegmentAlignment(true));
|
||||
|
||||
|
@ -240,7 +235,6 @@ TEST_P(PeriodTest, Text) {
|
|||
content_protection_in_adaptation_set_));
|
||||
}
|
||||
|
||||
// Verify AddTrickPlayReferenceId is called.
|
||||
TEST_P(PeriodTest, TrickPlayWithMatchingAdaptationSet) {
|
||||
const char kVideoMediaInfo[] =
|
||||
"video_info {\n"
|
||||
|
@ -267,15 +261,15 @@ TEST_P(PeriodTest, TrickPlayWithMatchingAdaptationSet) {
|
|||
"container_type: 1\n";
|
||||
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> trick_play_adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>(kTrickPlayAdaptationSetId));
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* trick_play_adaptation_set_ptr = trick_play_adaptation_set.get();
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _))
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(default_adaptation_set_))))
|
||||
.WillOnce(Return(ByMove(std::move(trick_play_adaptation_set))));
|
||||
|
||||
EXPECT_CALL(*trick_play_adaptation_set_ptr,
|
||||
AddTrickPlayReferenceId(Eq(kDefaultAdaptationSetId)));
|
||||
AddTrickPlayReference(Eq(default_adaptation_set_ptr_)));
|
||||
|
||||
ASSERT_EQ(default_adaptation_set_ptr_,
|
||||
testable_period_.GetOrCreateAdaptationSet(
|
||||
|
@ -314,9 +308,9 @@ TEST_P(PeriodTest, TrickPlayWithNoMatchingAdaptationSet) {
|
|||
"container_type: 1\n";
|
||||
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> trick_play_adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>(kTrickPlayAdaptationSetId));
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _))
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(default_adaptation_set_))))
|
||||
.WillOnce(Return(ByMove(std::move(trick_play_adaptation_set))));
|
||||
|
||||
|
@ -396,18 +390,16 @@ TEST_P(PeriodTest, DifferentProtectedContent) {
|
|||
|
||||
// Not using default mocks in this test so that we can keep track of
|
||||
// mocks by named mocks.
|
||||
const uint32_t kSdAdaptationSetId = 2u;
|
||||
const uint32_t kHdAdaptationSetId = 3u;
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> sd_adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>(kSdAdaptationSetId));
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* sd_adaptation_set_ptr = sd_adaptation_set.get();
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> hd_adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>(kHdAdaptationSetId));
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* hd_adaptation_set_ptr = hd_adaptation_set.get();
|
||||
|
||||
InSequence in_sequence;
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _))
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(sd_adaptation_set))));
|
||||
|
||||
if (content_protection_in_adaptation_set_) {
|
||||
|
@ -418,7 +410,7 @@ TEST_P(PeriodTest, DifferentProtectedContent) {
|
|||
*sd_adaptation_set_ptr,
|
||||
AddContentProtectionElement(ContentProtectionElementEq(sd_my_drm)));
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _))
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(hd_adaptation_set))));
|
||||
|
||||
// Add main Role here for both.
|
||||
|
@ -502,7 +494,7 @@ TEST_P(PeriodTest, SameProtectedContent) {
|
|||
InSequence in_sequence;
|
||||
|
||||
// Only called once.
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _))
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(default_adaptation_set_))));
|
||||
|
||||
if (content_protection_in_adaptation_set_) {
|
||||
|
@ -579,22 +571,24 @@ TEST_P(PeriodTest, SetAdaptationSetSwitching) {
|
|||
const uint32_t kSdAdaptationSetId = 6u;
|
||||
const uint32_t kHdAdaptationSetId = 7u;
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> sd_adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>(kSdAdaptationSetId));
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* sd_adaptation_set_ptr = sd_adaptation_set.get();
|
||||
sd_adaptation_set_ptr->set_id(kSdAdaptationSetId);
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> hd_adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>(kHdAdaptationSetId));
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* hd_adaptation_set_ptr = hd_adaptation_set.get();
|
||||
hd_adaptation_set_ptr->set_id(kHdAdaptationSetId);
|
||||
|
||||
InSequence in_sequence;
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _))
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(sd_adaptation_set))));
|
||||
|
||||
if (content_protection_in_adaptation_set_) {
|
||||
EXPECT_CALL(*sd_adaptation_set_ptr, AddContentProtectionElement(_))
|
||||
.Times(2);
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _))
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(hd_adaptation_set))));
|
||||
|
||||
// Add main Role here for both.
|
||||
|
@ -605,9 +599,9 @@ TEST_P(PeriodTest, SetAdaptationSetSwitching) {
|
|||
.Times(2);
|
||||
|
||||
EXPECT_CALL(*sd_adaptation_set_ptr,
|
||||
AddAdaptationSetSwitching(kHdAdaptationSetId));
|
||||
AddAdaptationSetSwitching(hd_adaptation_set_ptr));
|
||||
EXPECT_CALL(*hd_adaptation_set_ptr,
|
||||
AddAdaptationSetSwitching(kSdAdaptationSetId));
|
||||
AddAdaptationSetSwitching(sd_adaptation_set_ptr));
|
||||
}
|
||||
|
||||
ASSERT_EQ(sd_adaptation_set_ptr, testable_period_.GetOrCreateAdaptationSet(
|
||||
|
@ -643,11 +637,12 @@ TEST_P(PeriodTest, SetAdaptationSetSwitching) {
|
|||
|
||||
const uint32_t k4kAdaptationSetId = 4000u;
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> fourk_adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>(k4kAdaptationSetId));
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* fourk_adaptation_set_ptr = fourk_adaptation_set.get();
|
||||
fourk_adaptation_set_ptr->set_id(k4kAdaptationSetId);
|
||||
|
||||
if (content_protection_in_adaptation_set_) {
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _))
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(fourk_adaptation_set))));
|
||||
|
||||
EXPECT_CALL(*fourk_adaptation_set_ptr, AddRole(AdaptationSet::kRoleMain));
|
||||
|
@ -655,13 +650,13 @@ TEST_P(PeriodTest, SetAdaptationSetSwitching) {
|
|||
.Times(2);
|
||||
|
||||
EXPECT_CALL(*sd_adaptation_set_ptr,
|
||||
AddAdaptationSetSwitching(k4kAdaptationSetId));
|
||||
AddAdaptationSetSwitching(fourk_adaptation_set_ptr));
|
||||
EXPECT_CALL(*fourk_adaptation_set_ptr,
|
||||
AddAdaptationSetSwitching(kSdAdaptationSetId));
|
||||
AddAdaptationSetSwitching(sd_adaptation_set_ptr));
|
||||
EXPECT_CALL(*hd_adaptation_set_ptr,
|
||||
AddAdaptationSetSwitching(k4kAdaptationSetId));
|
||||
AddAdaptationSetSwitching(fourk_adaptation_set_ptr));
|
||||
EXPECT_CALL(*fourk_adaptation_set_ptr,
|
||||
AddAdaptationSetSwitching(kHdAdaptationSetId));
|
||||
AddAdaptationSetSwitching(hd_adaptation_set_ptr));
|
||||
}
|
||||
|
||||
ASSERT_EQ(content_protection_in_adaptation_set_ ? fourk_adaptation_set_ptr
|
||||
|
@ -716,22 +711,24 @@ TEST_P(PeriodTest, DoNotSetAdaptationSetSwitchingIfContentTypesDifferent) {
|
|||
const uint32_t kVideoAdaptationSetId = 6u;
|
||||
const uint32_t kAudioAdaptationSetId = 7u;
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> video_adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>(kVideoAdaptationSetId));
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* video_adaptation_set_ptr = video_adaptation_set.get();
|
||||
video_adaptation_set_ptr->set_id(kVideoAdaptationSetId);
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> audio_adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>(kAudioAdaptationSetId));
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* audio_adaptation_set_ptr = audio_adaptation_set.get();
|
||||
audio_adaptation_set_ptr->set_id(kAudioAdaptationSetId);
|
||||
|
||||
InSequence in_sequence;
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _))
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(video_adaptation_set))));
|
||||
if (content_protection_in_adaptation_set_) {
|
||||
EXPECT_CALL(*video_adaptation_set_ptr, AddContentProtectionElement(_))
|
||||
.Times(2);
|
||||
}
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _))
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(audio_adaptation_set))));
|
||||
if (content_protection_in_adaptation_set_) {
|
||||
EXPECT_CALL(*audio_adaptation_set_ptr, AddContentProtectionElement(_))
|
||||
|
@ -796,17 +793,17 @@ TEST_P(PeriodTest, SplitAdaptationSetsByLanguageAndCodec) {
|
|||
"media_duration_seconds: 10.5\n";
|
||||
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> aac_eng_adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>(1));
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* aac_eng_adaptation_set_ptr = aac_eng_adaptation_set.get();
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> aac_ger_adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>(2));
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* aac_ger_adaptation_set_ptr = aac_ger_adaptation_set.get();
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> vorbis_german_adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>(3));
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* vorbis_german_adaptation_set_ptr = vorbis_german_adaptation_set.get();
|
||||
|
||||
// We expect three AdaptationSets.
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _))
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(aac_eng_adaptation_set))))
|
||||
.WillOnce(Return(ByMove(std::move(aac_ger_adaptation_set))))
|
||||
.WillOnce(Return(ByMove(std::move(vorbis_german_adaptation_set))));
|
||||
|
@ -855,13 +852,13 @@ TEST_P(PeriodTest, GetAdaptationSets) {
|
|||
"media_duration_seconds: 10.5\n";
|
||||
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set_1(
|
||||
new StrictMock<MockAdaptationSet>(1));
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* adaptation_set_1_ptr = adaptation_set_1.get();
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set_2(
|
||||
new StrictMock<MockAdaptationSet>(2));
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* adaptation_set_2_ptr = adaptation_set_2.get();
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _))
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(adaptation_set_1))))
|
||||
.WillOnce(Return(ByMove(std::move(adaptation_set_2))));
|
||||
|
||||
|
@ -878,7 +875,7 @@ TEST_P(PeriodTest, GetAdaptationSets) {
|
|||
ElementsAre(adaptation_set_1_ptr, adaptation_set_2_ptr));
|
||||
}
|
||||
|
||||
TEST_P(PeriodTest, GetAdaptationSetsOrderedByAdaptationSetId) {
|
||||
TEST_P(PeriodTest, OrderedByAdaptationSetId) {
|
||||
const char kContent1[] =
|
||||
"audio_info {\n"
|
||||
" codec: 'mp4a.40.2'\n"
|
||||
|
@ -903,25 +900,34 @@ TEST_P(PeriodTest, GetAdaptationSetsOrderedByAdaptationSetId) {
|
|||
"media_duration_seconds: 10.5\n";
|
||||
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set_1(
|
||||
new StrictMock<MockAdaptationSet>(1));
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* adaptation_set_1_ptr = adaptation_set_1.get();
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set_2(
|
||||
new StrictMock<MockAdaptationSet>(2));
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* adaptation_set_2_ptr = adaptation_set_2.get();
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(adaptation_set_2))))
|
||||
.WillOnce(Return(ByMove(std::move(adaptation_set_1))));
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(adaptation_set_1))))
|
||||
.WillOnce(Return(ByMove(std::move(adaptation_set_2))));
|
||||
|
||||
ASSERT_EQ(adaptation_set_2_ptr, testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kContent2),
|
||||
content_protection_in_adaptation_set_));
|
||||
ASSERT_EQ(adaptation_set_1_ptr, testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kContent1),
|
||||
content_protection_in_adaptation_set_));
|
||||
EXPECT_THAT(testable_period_.GetAdaptationSets(),
|
||||
// Elements are ordered by id().
|
||||
ElementsAre(adaptation_set_1_ptr, adaptation_set_2_ptr));
|
||||
ASSERT_EQ(adaptation_set_2_ptr, testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kContent2),
|
||||
content_protection_in_adaptation_set_));
|
||||
|
||||
adaptation_set_1_ptr->set_id(2);
|
||||
adaptation_set_2_ptr->set_id(1);
|
||||
const char kExpectedXml[] =
|
||||
R"(<Period id="9">)"
|
||||
// ContentType and Representation elements are populated after
|
||||
// Representation::Init() is called.
|
||||
R"( <AdaptationSet id="1" contentType=""/>)"
|
||||
R"( <AdaptationSet id="2" contentType=""/>)"
|
||||
R"(</Period>)";
|
||||
EXPECT_THAT(testable_period_.GetXml(!kOutputPeriodDuration).get(),
|
||||
XmlNodeEqual(kExpectedXml));
|
||||
}
|
||||
INSTANTIATE_TEST_CASE_P(ContentProtectionInAdaptationSet,
|
||||
PeriodTest,
|
||||
|
|
|
@ -43,15 +43,31 @@ bool SimpleMpdNotifier::NotifyNewContainer(const MediaInfo& media_info,
|
|||
|
||||
MediaInfo adjusted_media_info(media_info);
|
||||
MpdBuilder::MakePathsRelativeToMpd(output_path_, &adjusted_media_info);
|
||||
const Representation* kNoOriginalRepresentation = nullptr;
|
||||
const double kPeriodStartTimeSeconds = 0.0;
|
||||
|
||||
base::AutoLock auto_lock(lock_);
|
||||
const Representation* representation = AddRepresentationToPeriod(
|
||||
adjusted_media_info, kNoOriginalRepresentation, kPeriodStartTimeSeconds);
|
||||
const double kPeriodStartTimeSeconds = 0.0;
|
||||
Period* period = mpd_builder_->GetOrCreatePeriod(kPeriodStartTimeSeconds);
|
||||
DCHECK(period);
|
||||
AdaptationSet* adaptation_set = period->GetOrCreateAdaptationSet(
|
||||
media_info, content_protection_in_adaptation_set_);
|
||||
DCHECK(adaptation_set);
|
||||
if (!adaptation_set->has_id())
|
||||
adaptation_set->set_id(next_adaptation_set_id_++);
|
||||
Representation* representation =
|
||||
adaptation_set->AddRepresentation(adjusted_media_info);
|
||||
if (!representation)
|
||||
return false;
|
||||
|
||||
*container_id = representation->id();
|
||||
if (content_protection_in_adaptation_set_) {
|
||||
// ContentProtection elements are already added to AdaptationSet above.
|
||||
// Use RepresentationId to AdaptationSet map to update ContentProtection
|
||||
// in AdaptationSet in NotifyEncryptionUpdate.
|
||||
representation_id_to_adaptation_set_[representation->id()] = adaptation_set;
|
||||
} else {
|
||||
AddContentProtectionElements(media_info, representation);
|
||||
}
|
||||
representation_map_[representation->id()] = representation;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -96,16 +112,32 @@ bool SimpleMpdNotifier::NotifyCueEvent(uint32_t container_id,
|
|||
const MediaInfo& media_info = original_representation->GetMediaInfo();
|
||||
const double period_start_time_seconds =
|
||||
static_cast<double>(timestamp) / media_info.reference_time_scale();
|
||||
const Representation* new_representation = AddRepresentationToPeriod(
|
||||
media_info, original_representation, period_start_time_seconds);
|
||||
if (!new_representation)
|
||||
|
||||
Period* period = mpd_builder_->GetOrCreatePeriod(period_start_time_seconds);
|
||||
DCHECK(period);
|
||||
AdaptationSet* adaptation_set = period->GetOrCreateAdaptationSet(
|
||||
media_info, content_protection_in_adaptation_set_);
|
||||
DCHECK(adaptation_set);
|
||||
if (!adaptation_set->has_id()) {
|
||||
adaptation_set->set_id(original_adaptation_set->id());
|
||||
} else {
|
||||
DCHECK_EQ(adaptation_set->id(), original_adaptation_set->id());
|
||||
}
|
||||
|
||||
Representation* representation =
|
||||
adaptation_set->CopyRepresentation(*original_representation);
|
||||
if (!representation)
|
||||
return false;
|
||||
|
||||
// TODO(kqyang): Pass the ID to GetOrCreateAdaptationSet instead?
|
||||
AdaptationSet* new_adaptation_set =
|
||||
representation_id_to_adaptation_set_[container_id];
|
||||
DCHECK(new_adaptation_set);
|
||||
new_adaptation_set->set_id(original_adaptation_set->id());
|
||||
if (content_protection_in_adaptation_set_) {
|
||||
// ContentProtection elements are already added to AdaptationSet above.
|
||||
// Use RepresentationId to AdaptationSet map to update ContentProtection
|
||||
// in AdaptationSet in NotifyEncryptionUpdate.
|
||||
representation_id_to_adaptation_set_[representation->id()] = adaptation_set;
|
||||
} else {
|
||||
AddContentProtectionElements(media_info, representation);
|
||||
}
|
||||
representation_map_[representation->id()] = representation;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -138,37 +170,4 @@ bool SimpleMpdNotifier::Flush() {
|
|||
return WriteMpdToFile(output_path_, mpd_builder_.get());
|
||||
}
|
||||
|
||||
Representation* SimpleMpdNotifier::AddRepresentationToPeriod(
|
||||
const MediaInfo& media_info,
|
||||
const Representation* original_representation,
|
||||
double period_start_time_seconds) {
|
||||
Period* period = mpd_builder_->GetOrCreatePeriod(period_start_time_seconds);
|
||||
DCHECK(period);
|
||||
|
||||
AdaptationSet* adaptation_set = period->GetOrCreateAdaptationSet(
|
||||
media_info, content_protection_in_adaptation_set_);
|
||||
DCHECK(adaptation_set);
|
||||
|
||||
Representation* representation = nullptr;
|
||||
if (original_representation) {
|
||||
representation =
|
||||
adaptation_set->CopyRepresentation(*original_representation);
|
||||
} else {
|
||||
representation = adaptation_set->AddRepresentation(media_info);
|
||||
}
|
||||
if (!representation)
|
||||
return nullptr;
|
||||
|
||||
if (content_protection_in_adaptation_set_) {
|
||||
// ContentProtection elements are already added to AdaptationSet above.
|
||||
// Use RepresentationId to AdaptationSet map to update ContentProtection
|
||||
// in AdaptationSet in NotifyEncryptionUpdate.
|
||||
representation_id_to_adaptation_set_[representation->id()] = adaptation_set;
|
||||
} else {
|
||||
AddContentProtectionElements(media_info, representation);
|
||||
}
|
||||
representation_map_[representation->id()] = representation;
|
||||
return representation;
|
||||
}
|
||||
|
||||
} // namespace shaka
|
||||
|
|
|
@ -56,17 +56,6 @@ class SimpleMpdNotifier : public MpdNotifier {
|
|||
|
||||
friend class SimpleMpdNotifierTest;
|
||||
|
||||
// Add a new representation. If |original_representation| is not nullptr, the
|
||||
// new Representation will clone from it; otherwise the new Representation is
|
||||
// created from |media_info|.
|
||||
// The new Representation will be added to Period with the specified start
|
||||
// time.
|
||||
// Returns the new Representation on success; otherwise a nullptr is returned.
|
||||
Representation* AddRepresentationToPeriod(
|
||||
const MediaInfo& media_info,
|
||||
const Representation* original_representation,
|
||||
double period_start_time_seconds);
|
||||
|
||||
// Testing only method. Returns a pointer to MpdBuilder.
|
||||
MpdBuilder* MpdBuilderForTesting() const { return mpd_builder_.get(); }
|
||||
|
||||
|
@ -81,6 +70,7 @@ class SimpleMpdNotifier : public MpdNotifier {
|
|||
bool content_protection_in_adaptation_set_ = true;
|
||||
base::Lock lock_;
|
||||
|
||||
uint32_t next_adaptation_set_id_ = 0;
|
||||
// Maps Representation ID to Representation.
|
||||
std::map<uint32_t, Representation*> representation_map_;
|
||||
// Maps Representation ID to AdaptationSet. This is for updating the PSSH.
|
||||
|
|
|
@ -28,7 +28,6 @@ using ::testing::StrEq;
|
|||
namespace {
|
||||
const uint32_t kDefaultPeriodId = 0u;
|
||||
const double kDefaultPeriodStartTime = 0.0;
|
||||
const uint32_t kDefaultAdaptationSetId = 0u;
|
||||
const uint32_t kDefaultTimeScale = 10;
|
||||
const bool kContentProtectionInAdaptationSet = true;
|
||||
|
||||
|
@ -43,8 +42,7 @@ class SimpleMpdNotifierTest : public ::testing::Test {
|
|||
SimpleMpdNotifierTest()
|
||||
: default_mock_period_(
|
||||
new MockPeriod(kDefaultPeriodId, kDefaultPeriodStartTime)),
|
||||
default_mock_adaptation_set_(
|
||||
new MockAdaptationSet(kDefaultAdaptationSetId)) {}
|
||||
default_mock_adaptation_set_(new MockAdaptationSet()) {}
|
||||
|
||||
void SetUp() override {
|
||||
ASSERT_TRUE(base::CreateTemporaryFile(&temp_file_path_));
|
||||
|
@ -207,7 +205,7 @@ TEST_F(SimpleMpdNotifierTest, NotifyCueEvent) {
|
|||
std::unique_ptr<MockPeriod> mock_period(
|
||||
new MockPeriod(kDefaultPeriodId, kDefaultPeriodStartTime));
|
||||
std::unique_ptr<MockAdaptationSet> mock_adaptation_set(
|
||||
new MockAdaptationSet(kDefaultAdaptationSetId));
|
||||
new MockAdaptationSet());
|
||||
std::unique_ptr<MockRepresentation> mock_representation(
|
||||
new MockRepresentation(kRepresentationId));
|
||||
|
||||
|
@ -229,7 +227,7 @@ TEST_F(SimpleMpdNotifierTest, NotifyCueEvent) {
|
|||
std::unique_ptr<MockPeriod> mock_period2(
|
||||
new MockPeriod(kAnotherPeriodId, kArbitraryPeriodStartTime));
|
||||
std::unique_ptr<MockAdaptationSet> mock_adaptation_set2(
|
||||
new MockAdaptationSet(kDefaultAdaptationSetId));
|
||||
new MockAdaptationSet());
|
||||
std::unique_ptr<MockRepresentation> mock_representation2(
|
||||
new MockRepresentation(kRepresentationId));
|
||||
|
||||
|
@ -339,8 +337,8 @@ TEST_F(SimpleMpdNotifierTest, MultipleMediaInfo) {
|
|||
SimpleMpdNotifier notifier(empty_mpd_option_);
|
||||
std::unique_ptr<MockMpdBuilder> mock_mpd_builder(new MockMpdBuilder());
|
||||
|
||||
std::unique_ptr<MockAdaptationSet> adaptation_set1(new MockAdaptationSet(1));
|
||||
std::unique_ptr<MockAdaptationSet> adaptation_set2(new MockAdaptationSet(2));
|
||||
std::unique_ptr<MockAdaptationSet> adaptation_set1(new MockAdaptationSet());
|
||||
std::unique_ptr<MockAdaptationSet> adaptation_set2(new MockAdaptationSet());
|
||||
|
||||
std::unique_ptr<MockRepresentation> representation1(
|
||||
new MockRepresentation(1));
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT0S">
|
||||
<Period id="0">
|
||||
<AdaptationSet id="0" width="720" height="480" frameRate="10/1" contentType="video" par="3:2">
|
||||
<AdaptationSet contentType="video" width="720" height="480" frameRate="10/1" par="3:2">
|
||||
<Representation id="0" bandwidth="7620" codecs="avc1.010101" mimeType="video/mp4" sar="1:1">
|
||||
<BaseURL>test_output_file_name1.mp4</BaseURL>
|
||||
<SegmentBase indexRange="121-221" timescale="1000">
|
||||
|
@ -9,7 +9,7 @@
|
|||
</SegmentBase>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
<AdaptationSet id="1" contentType="audio">
|
||||
<AdaptationSet contentType="audio">
|
||||
<Representation id="1" bandwidth="400" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
|
||||
<BaseURL>test_output_file_name_audio1.mp4</BaseURL>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT0S">
|
||||
<Period id="0">
|
||||
<AdaptationSet id="0" width="720" height="480" frameRate="10/1" contentType="video" par="3:2">
|
||||
<AdaptationSet width="720" height="480" frameRate="10/1" contentType="video" par="3:2">
|
||||
<Representation id="0" bandwidth="7620" codecs="avc1.010101" mimeType="video/mp4" sar="1:1">
|
||||
<BaseURL>test_output_file_name1.mp4</BaseURL>
|
||||
<SegmentBase indexRange="121-221" timescale="1000">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT0S">
|
||||
<Period id="0">
|
||||
<AdaptationSet id="0" maxWidth="720" maxHeight="480" maxFrameRate="10/1" contentType="video">
|
||||
<AdaptationSet contentType="video" maxWidth="720" maxHeight="480" maxFrameRate="10/1">
|
||||
<Representation id="0" bandwidth="7620" codecs="avc1.010101" mimeType="video/mp4" width="720" height="480" frameRate="10/1" sar="1:1">
|
||||
<BaseURL>test_output_file_name1.mp4</BaseURL>
|
||||
<SegmentBase indexRange="121-221" timescale="1000">
|
||||
|
|
Loading…
Reference in New Issue