1167 lines
43 KiB
C++
1167 lines
43 KiB
C++
// Copyright 2017 Google LLC. All rights reserved.
|
|
//
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file or at
|
|
// https://developers.google.com/open-source/licenses/bsd
|
|
|
|
#include "packager/mpd/base/period.h"
|
|
|
|
#include <gmock/gmock.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
#include "packager/mpd/base/mock_mpd_builder.h"
|
|
#include "packager/mpd/base/mpd_options.h"
|
|
#include "packager/mpd/test/mpd_builder_test_helper.h"
|
|
#include "packager/mpd/test/xml_compare.h"
|
|
|
|
using ::testing::_;
|
|
using ::testing::ByMove;
|
|
using ::testing::ElementsAre;
|
|
using ::testing::Eq;
|
|
using ::testing::InSequence;
|
|
using ::testing::Return;
|
|
using ::testing::StrictMock;
|
|
|
|
namespace shaka {
|
|
namespace {
|
|
const uint32_t kDefaultPeriodId = 9u;
|
|
const double kDefaultPeriodStartTime = 5.6;
|
|
const bool kOutputPeriodDuration = true;
|
|
|
|
bool ElementEqual(const Element& lhs, const Element& rhs) {
|
|
const bool all_equal_except_sublement_check =
|
|
lhs.name == rhs.name && lhs.attributes.size() == rhs.attributes.size() &&
|
|
std::equal(lhs.attributes.begin(), lhs.attributes.end(),
|
|
rhs.attributes.begin()) &&
|
|
lhs.content == rhs.content &&
|
|
lhs.subelements.size() == rhs.subelements.size();
|
|
|
|
if (!all_equal_except_sublement_check) {
|
|
return false;
|
|
}
|
|
|
|
for (size_t i = 0; i < lhs.subelements.size(); ++i) {
|
|
if (!ElementEqual(lhs.subelements[i], rhs.subelements[i]))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ContentProtectionElementEqual(const ContentProtectionElement& lhs,
|
|
const ContentProtectionElement& rhs) {
|
|
const bool all_equal_except_sublement_check =
|
|
lhs.value == rhs.value && lhs.scheme_id_uri == rhs.scheme_id_uri &&
|
|
lhs.additional_attributes.size() == rhs.additional_attributes.size() &&
|
|
std::equal(lhs.additional_attributes.begin(),
|
|
lhs.additional_attributes.end(),
|
|
rhs.additional_attributes.begin()) &&
|
|
lhs.subelements.size() == rhs.subelements.size();
|
|
|
|
if (!all_equal_except_sublement_check)
|
|
return false;
|
|
|
|
for (size_t i = 0; i < lhs.subelements.size(); ++i) {
|
|
if (!ElementEqual(lhs.subelements[i], rhs.subelements[i]))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
MATCHER_P(ContentProtectionElementEq, expected, "") {
|
|
return ContentProtectionElementEqual(arg, expected);
|
|
}
|
|
|
|
/// A Period class that is capable of injecting mocked AdaptationSet.
|
|
class TestablePeriod : public Period {
|
|
public:
|
|
TestablePeriod(const MpdOptions& mpd_options)
|
|
: Period(kDefaultPeriodId,
|
|
kDefaultPeriodStartTime,
|
|
mpd_options,
|
|
&sequence_number_) {}
|
|
|
|
MOCK_METHOD3(
|
|
NewAdaptationSet,
|
|
std::unique_ptr<AdaptationSet>(const std::string& lang,
|
|
const MpdOptions& options,
|
|
uint32_t* representation_counter));
|
|
|
|
private:
|
|
// Only for constructing the super class. Not used for testing.
|
|
uint32_t sequence_number_ = 0;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
class PeriodTest : public ::testing::Test {
|
|
public:
|
|
PeriodTest()
|
|
: testable_period_(mpd_options_),
|
|
default_adaptation_set_(new StrictMock<MockAdaptationSet>()),
|
|
default_adaptation_set_ptr_(default_adaptation_set_.get()) {}
|
|
|
|
protected:
|
|
MpdOptions mpd_options_;
|
|
TestablePeriod testable_period_;
|
|
bool content_protection_in_adaptation_set_ = true;
|
|
|
|
// Default mock that can be used for the tests.
|
|
std::unique_ptr<StrictMock<MockAdaptationSet>> default_adaptation_set_;
|
|
StrictMock<MockAdaptationSet>* default_adaptation_set_ptr_;
|
|
};
|
|
|
|
TEST_F(PeriodTest, GetXml) {
|
|
const char kVideoMediaInfo[] =
|
|
"video_info {\n"
|
|
" codec: 'avc1'\n"
|
|
" width: 1280\n"
|
|
" height: 720\n"
|
|
" time_scale: 10\n"
|
|
" frame_duration: 10\n"
|
|
" pixel_width: 1\n"
|
|
" pixel_height: 1\n"
|
|
"}\n"
|
|
"container_type: 1\n";
|
|
|
|
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
|
.WillOnce(Return(ByMove(std::move(default_adaptation_set_))));
|
|
|
|
ASSERT_EQ(default_adaptation_set_ptr_,
|
|
testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kVideoMediaInfo),
|
|
content_protection_in_adaptation_set_));
|
|
|
|
const char kExpectedXml[] =
|
|
"<Period id=\"9\">"
|
|
// ContentType and Representation elements are populated after
|
|
// Representation::Init() is called.
|
|
" <AdaptationSet contentType=\"\"/>"
|
|
"</Period>";
|
|
EXPECT_THAT(testable_period_.GetXml(!kOutputPeriodDuration),
|
|
XmlNodeEqual(kExpectedXml));
|
|
}
|
|
|
|
TEST_F(PeriodTest, DynamicMpdGetXml) {
|
|
const char kVideoMediaInfo[] =
|
|
"video_info {\n"
|
|
" codec: 'avc1'\n"
|
|
" width: 1280\n"
|
|
" height: 720\n"
|
|
" time_scale: 10\n"
|
|
" frame_duration: 10\n"
|
|
" pixel_width: 1\n"
|
|
" pixel_height: 1\n"
|
|
"}\n"
|
|
"container_type: 1\n";
|
|
mpd_options_.mpd_type = MpdType::kDynamic;
|
|
|
|
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
|
.WillOnce(Return(ByMove(std::move(default_adaptation_set_))));
|
|
|
|
ASSERT_EQ(default_adaptation_set_ptr_,
|
|
testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kVideoMediaInfo),
|
|
content_protection_in_adaptation_set_));
|
|
|
|
const char kExpectedXml[] =
|
|
"<Period id=\"9\" start=\"PT5.6S\">"
|
|
// ContentType and Representation elements are populated after
|
|
// Representation::Init() is called.
|
|
" <AdaptationSet contentType=\"\"/>"
|
|
"</Period>";
|
|
EXPECT_THAT(testable_period_.GetXml(!kOutputPeriodDuration),
|
|
XmlNodeEqual(kExpectedXml));
|
|
}
|
|
|
|
TEST_F(PeriodTest, LowLatencyDashMpdGetXml) {
|
|
const char kVideoMediaInfo[] =
|
|
"video_info {\n"
|
|
" codec: 'avc1'\n"
|
|
" width: 1280\n"
|
|
" height: 720\n"
|
|
" time_scale: 10\n"
|
|
" frame_duration: 10\n"
|
|
" pixel_width: 1\n"
|
|
" pixel_height: 1\n"
|
|
"}\n"
|
|
"container_type: 1\n";
|
|
mpd_options_.mpd_type = MpdType::kDynamic;
|
|
mpd_options_.mpd_params.low_latency_dash_mode = true;
|
|
mpd_options_.mpd_params.target_latency_seconds = 1;
|
|
|
|
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
|
.WillOnce(Return(ByMove(std::move(default_adaptation_set_))));
|
|
|
|
ASSERT_EQ(default_adaptation_set_ptr_,
|
|
testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kVideoMediaInfo),
|
|
content_protection_in_adaptation_set_));
|
|
|
|
const char kExpectedXml[] =
|
|
"<Period id=\"9\" start=\"PT5.6S\">"
|
|
// LL-DASH standards require ServiceDescription and Latency elements
|
|
" <ServiceDescription id=\"9\" >"
|
|
// In LL-DASH MPD, the target latency is in ms, so the expected value is
|
|
// 1000.
|
|
" <Latency target=\"1000\"/>"
|
|
" </ServiceDescription>"
|
|
// ContentType and Representation elements are populated after
|
|
// Representation::Init() is called.
|
|
" <AdaptationSet contentType=\"\"/>"
|
|
"</Period>";
|
|
EXPECT_THAT(testable_period_.GetXml(!kOutputPeriodDuration),
|
|
XmlNodeEqual(kExpectedXml));
|
|
}
|
|
|
|
TEST_F(PeriodTest, SetDurationAndGetXml) {
|
|
const char kVideoMediaInfo[] =
|
|
"video_info {\n"
|
|
" codec: 'avc1'\n"
|
|
" width: 1280\n"
|
|
" height: 720\n"
|
|
" time_scale: 10\n"
|
|
" frame_duration: 10\n"
|
|
" pixel_width: 1\n"
|
|
" pixel_height: 1\n"
|
|
"}\n"
|
|
"container_type: 1\n";
|
|
|
|
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
|
.WillOnce(Return(ByMove(std::move(default_adaptation_set_))));
|
|
|
|
ASSERT_EQ(default_adaptation_set_ptr_,
|
|
testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kVideoMediaInfo),
|
|
content_protection_in_adaptation_set_));
|
|
|
|
testable_period_.set_duration_seconds(100.234);
|
|
|
|
const char kExpectedXml[] =
|
|
"<Period id=\"9\" duration=\"PT100.234S\">"
|
|
// ContentType and Representation elements are populated after
|
|
// Representation::Init() is called.
|
|
" <AdaptationSet contentType=\"\"/>"
|
|
"</Period>";
|
|
EXPECT_THAT(testable_period_.GetXml(kOutputPeriodDuration),
|
|
XmlNodeEqual(kExpectedXml));
|
|
const char kExpectedXmlSuppressDuration[] =
|
|
"<Period id=\"9\">"
|
|
// ContentType and Representation elements are populated after
|
|
// Representation::Init() is called.
|
|
" <AdaptationSet contentType=\"\"/>"
|
|
"</Period>";
|
|
EXPECT_THAT(testable_period_.GetXml(!kOutputPeriodDuration),
|
|
XmlNodeEqual(kExpectedXmlSuppressDuration));
|
|
}
|
|
|
|
// Verify ForceSetSegmentAlignment is called.
|
|
TEST_F(PeriodTest, Text) {
|
|
const char kTextMediaInfo[] =
|
|
"text_info {\n"
|
|
" codec: 'ttml'\n"
|
|
" language: 'en'\n"
|
|
"}\n"
|
|
"container_type: CONTAINER_TEXT\n";
|
|
|
|
EXPECT_CALL(testable_period_, NewAdaptationSet(Eq("en"), _, _))
|
|
.WillOnce(Return(ByMove(std::move(default_adaptation_set_))));
|
|
EXPECT_CALL(*default_adaptation_set_ptr_, ForceSetSegmentAlignment(true));
|
|
|
|
ASSERT_EQ(default_adaptation_set_ptr_,
|
|
testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kTextMediaInfo),
|
|
content_protection_in_adaptation_set_));
|
|
}
|
|
|
|
TEST_F(PeriodTest, TrickPlayWithMatchingAdaptationSet) {
|
|
const char kVideoMediaInfo[] =
|
|
"video_info {\n"
|
|
" codec: 'avc1'\n"
|
|
" width: 1280\n"
|
|
" height: 720\n"
|
|
" time_scale: 10\n"
|
|
" frame_duration: 10\n"
|
|
" pixel_width: 1\n"
|
|
" pixel_height: 1\n"
|
|
"}\n"
|
|
"container_type: 1\n";
|
|
const char kTrickPlayMediaInfo[] =
|
|
"video_info {\n"
|
|
" codec: 'avc1'\n"
|
|
" width: 1280\n"
|
|
" height: 720\n"
|
|
" time_scale: 10\n"
|
|
" frame_duration: 100\n"
|
|
" pixel_width: 1\n"
|
|
" pixel_height: 1\n"
|
|
" playback_rate: 10\n"
|
|
"}\n"
|
|
"container_type: 1\n";
|
|
|
|
std::unique_ptr<StrictMock<MockAdaptationSet>> trick_play_adaptation_set(
|
|
new StrictMock<MockAdaptationSet>());
|
|
auto* trick_play_adaptation_set_ptr = trick_play_adaptation_set.get();
|
|
|
|
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,
|
|
AddTrickPlayReference(Eq(default_adaptation_set_ptr_)));
|
|
|
|
ASSERT_EQ(default_adaptation_set_ptr_,
|
|
testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kVideoMediaInfo),
|
|
content_protection_in_adaptation_set_));
|
|
ASSERT_EQ(trick_play_adaptation_set_ptr,
|
|
testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kTrickPlayMediaInfo),
|
|
content_protection_in_adaptation_set_));
|
|
}
|
|
|
|
TEST_F(PeriodTest, TrickPlayCacheWithMatchingAdaptationSet) {
|
|
const char kVideoMediaInfo[] =
|
|
"video_info {\n"
|
|
" codec: 'avc1'\n"
|
|
" width: 1280\n"
|
|
" height: 720\n"
|
|
" time_scale: 10\n"
|
|
" frame_duration: 10\n"
|
|
" pixel_width: 1\n"
|
|
" pixel_height: 1\n"
|
|
"}\n"
|
|
"container_type: 1\n";
|
|
const char kTrickPlayMediaInfo[] =
|
|
"video_info {\n"
|
|
" codec: 'avc1'\n"
|
|
" width: 1280\n"
|
|
" height: 720\n"
|
|
" time_scale: 10\n"
|
|
" frame_duration: 100\n"
|
|
" pixel_width: 1\n"
|
|
" pixel_height: 1\n"
|
|
" playback_rate: 10\n"
|
|
"}\n"
|
|
"container_type: 1\n";
|
|
|
|
std::unique_ptr<StrictMock<MockAdaptationSet>> trick_play_adaptation_set(
|
|
new StrictMock<MockAdaptationSet>());
|
|
auto* trick_play_adaptation_set_ptr = trick_play_adaptation_set.get();
|
|
|
|
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
|
.WillOnce(Return(ByMove(std::move(trick_play_adaptation_set))))
|
|
.WillOnce(Return(ByMove(std::move(default_adaptation_set_))));
|
|
|
|
EXPECT_CALL(*trick_play_adaptation_set_ptr,
|
|
AddTrickPlayReference(Eq(default_adaptation_set_ptr_)));
|
|
|
|
ASSERT_EQ(trick_play_adaptation_set_ptr,
|
|
testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kTrickPlayMediaInfo),
|
|
content_protection_in_adaptation_set_));
|
|
ASSERT_EQ(default_adaptation_set_ptr_,
|
|
testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kVideoMediaInfo),
|
|
content_protection_in_adaptation_set_));
|
|
}
|
|
|
|
// Verify no AdaptationSet is returned on trickplay media info.
|
|
TEST_F(PeriodTest, TrickPlayWithNoMatchingAdaptationSet) {
|
|
const char kVideoMediaInfo[] =
|
|
"video_info {\n"
|
|
" codec: 'avc1'\n"
|
|
" width: 1280\n"
|
|
" height: 720\n"
|
|
" time_scale: 10\n"
|
|
" frame_duration: 10\n"
|
|
" pixel_width: 1\n"
|
|
" pixel_height: 1\n"
|
|
"}\n"
|
|
"container_type: 1\n";
|
|
const char kVp9TrickPlayMediaInfo[] =
|
|
"video_info {\n"
|
|
" codec: 'vp9'\n"
|
|
" width: 1280\n"
|
|
" height: 720\n"
|
|
" time_scale: 10\n"
|
|
" frame_duration: 100\n"
|
|
" pixel_width: 1\n"
|
|
" pixel_height: 1\n"
|
|
" playback_rate: 10\n"
|
|
"}\n"
|
|
"container_type: 1\n";
|
|
|
|
std::unique_ptr<StrictMock<MockAdaptationSet>> trick_play_adaptation_set(
|
|
new StrictMock<MockAdaptationSet>());
|
|
|
|
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
|
.WillOnce(Return(ByMove(std::move(default_adaptation_set_))))
|
|
.WillOnce(Return(ByMove(std::move(trick_play_adaptation_set))));
|
|
|
|
ASSERT_EQ(default_adaptation_set_ptr_,
|
|
testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kVideoMediaInfo),
|
|
content_protection_in_adaptation_set_));
|
|
|
|
ASSERT_TRUE(testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kVp9TrickPlayMediaInfo),
|
|
content_protection_in_adaptation_set_));
|
|
|
|
ASSERT_TRUE(!testable_period_.trickplay_cache().empty());
|
|
}
|
|
|
|
// Don't put different audio languages or codecs in the same AdaptationSet.
|
|
TEST_F(PeriodTest, SplitAdaptationSetsByLanguageAndCodec) {
|
|
const char kAacEnglishAudioContent[] =
|
|
"audio_info {\n"
|
|
" codec: 'mp4a.40.2'\n"
|
|
" sampling_frequency: 44100\n"
|
|
" time_scale: 1200\n"
|
|
" num_channels: 2\n"
|
|
" language: 'eng'\n"
|
|
"}\n"
|
|
"reference_time_scale: 50\n"
|
|
"container_type: CONTAINER_MP4\n"
|
|
"media_duration_seconds: 10.5\n";
|
|
const char kAacGermanAudioContent[] =
|
|
"audio_info {\n"
|
|
" codec: 'mp4a.40.2'\n"
|
|
" sampling_frequency: 44100\n"
|
|
" time_scale: 1200\n"
|
|
" num_channels: 2\n"
|
|
" language: 'ger'\n"
|
|
"}\n"
|
|
"reference_time_scale: 50\n"
|
|
"container_type: CONTAINER_MP4\n"
|
|
"media_duration_seconds: 10.5\n";
|
|
const char kVorbisGermanAudioContent1[] =
|
|
"audio_info {\n"
|
|
" codec: 'vorbis'\n"
|
|
" sampling_frequency: 44100\n"
|
|
" time_scale: 1200\n"
|
|
" num_channels: 2\n"
|
|
" language: 'ger'\n"
|
|
"}\n"
|
|
"reference_time_scale: 50\n"
|
|
"container_type: CONTAINER_WEBM\n"
|
|
"media_duration_seconds: 10.5\n";
|
|
const char kVorbisGermanAudioContent2[] =
|
|
"audio_info {\n"
|
|
" codec: 'vorbis'\n"
|
|
" sampling_frequency: 44100\n"
|
|
" time_scale: 1200\n"
|
|
" num_channels: 2\n"
|
|
" language: 'ger'\n"
|
|
"}\n"
|
|
"reference_time_scale: 50\n"
|
|
"container_type: CONTAINER_WEBM\n"
|
|
"media_duration_seconds: 10.5\n";
|
|
|
|
std::unique_ptr<StrictMock<MockAdaptationSet>> aac_eng_adaptation_set(
|
|
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>());
|
|
auto* aac_ger_adaptation_set_ptr = aac_ger_adaptation_set.get();
|
|
std::unique_ptr<StrictMock<MockAdaptationSet>> vorbis_german_adaptation_set(
|
|
new StrictMock<MockAdaptationSet>());
|
|
auto* vorbis_german_adaptation_set_ptr = vorbis_german_adaptation_set.get();
|
|
|
|
// We expect three AdaptationSets.
|
|
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))));
|
|
|
|
ASSERT_EQ(aac_eng_adaptation_set_ptr,
|
|
testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kAacEnglishAudioContent),
|
|
content_protection_in_adaptation_set_));
|
|
ASSERT_EQ(aac_ger_adaptation_set_ptr,
|
|
testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kAacGermanAudioContent),
|
|
content_protection_in_adaptation_set_));
|
|
ASSERT_EQ(vorbis_german_adaptation_set_ptr,
|
|
testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kVorbisGermanAudioContent1),
|
|
content_protection_in_adaptation_set_));
|
|
// The same AdaptationSet is returned.
|
|
ASSERT_EQ(vorbis_german_adaptation_set_ptr,
|
|
testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kVorbisGermanAudioContent2),
|
|
content_protection_in_adaptation_set_));
|
|
}
|
|
|
|
TEST_F(PeriodTest, GetAdaptationSets) {
|
|
const char kContent1[] =
|
|
"audio_info {\n"
|
|
" codec: 'mp4a.40.2'\n"
|
|
" sampling_frequency: 44100\n"
|
|
" time_scale: 1200\n"
|
|
" num_channels: 2\n"
|
|
" language: 'eng'\n"
|
|
"}\n"
|
|
"reference_time_scale: 50\n"
|
|
"container_type: CONTAINER_MP4\n"
|
|
"media_duration_seconds: 10.5\n";
|
|
const char kContent2[] =
|
|
"audio_info {\n"
|
|
" codec: 'mp4a.40.2'\n"
|
|
" sampling_frequency: 44100\n"
|
|
" time_scale: 1200\n"
|
|
" num_channels: 2\n"
|
|
" language: 'ger'\n"
|
|
"}\n"
|
|
"reference_time_scale: 50\n"
|
|
"container_type: CONTAINER_MP4\n"
|
|
"media_duration_seconds: 10.5\n";
|
|
|
|
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set_1(
|
|
new StrictMock<MockAdaptationSet>());
|
|
auto* adaptation_set_1_ptr = adaptation_set_1.get();
|
|
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set_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_1))))
|
|
.WillOnce(Return(ByMove(std::move(adaptation_set_2))));
|
|
|
|
ASSERT_EQ(adaptation_set_1_ptr, testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kContent1),
|
|
content_protection_in_adaptation_set_));
|
|
EXPECT_THAT(testable_period_.GetAdaptationSets(),
|
|
ElementsAre(adaptation_set_1_ptr));
|
|
|
|
ASSERT_EQ(adaptation_set_2_ptr, testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kContent2),
|
|
content_protection_in_adaptation_set_));
|
|
EXPECT_THAT(testable_period_.GetAdaptationSets(),
|
|
ElementsAre(adaptation_set_1_ptr, adaptation_set_2_ptr));
|
|
}
|
|
|
|
TEST_F(PeriodTest, OrderedByAdaptationSetId) {
|
|
const char kContent1[] =
|
|
"audio_info {\n"
|
|
" codec: 'mp4a.40.2'\n"
|
|
" sampling_frequency: 44100\n"
|
|
" time_scale: 1200\n"
|
|
" num_channels: 2\n"
|
|
" language: 'eng'\n"
|
|
"}\n"
|
|
"reference_time_scale: 50\n"
|
|
"container_type: CONTAINER_MP4\n"
|
|
"media_duration_seconds: 10.5\n";
|
|
const char kContent2[] =
|
|
"audio_info {\n"
|
|
" codec: 'mp4a.40.2'\n"
|
|
" sampling_frequency: 44100\n"
|
|
" time_scale: 1200\n"
|
|
" num_channels: 2\n"
|
|
" language: 'ger'\n"
|
|
"}\n"
|
|
"reference_time_scale: 50\n"
|
|
"container_type: CONTAINER_MP4\n"
|
|
"media_duration_seconds: 10.5\n";
|
|
|
|
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set_1(
|
|
new StrictMock<MockAdaptationSet>());
|
|
auto* adaptation_set_1_ptr = adaptation_set_1.get();
|
|
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set_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_1))))
|
|
.WillOnce(Return(ByMove(std::move(adaptation_set_2))));
|
|
|
|
ASSERT_EQ(adaptation_set_1_ptr, testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kContent1),
|
|
content_protection_in_adaptation_set_));
|
|
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),
|
|
XmlNodeEqual(kExpectedXml));
|
|
}
|
|
|
|
TEST_F(PeriodTest, AudioAdaptationSetDefaultLanguage) {
|
|
mpd_options_.mpd_params.default_language = "en";
|
|
const char kEnglishAudioContent[] =
|
|
"audio_info {\n"
|
|
" codec: 'mp4a.40.2'\n"
|
|
" sampling_frequency: 44100\n"
|
|
" time_scale: 1200\n"
|
|
" num_channels: 2\n"
|
|
" language: 'en'\n"
|
|
"}\n"
|
|
"reference_time_scale: 50\n"
|
|
"container_type: CONTAINER_MP4\n"
|
|
"media_duration_seconds: 10.5\n";
|
|
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set(
|
|
new StrictMock<MockAdaptationSet>());
|
|
auto* adaptation_set_ptr = adaptation_set.get();
|
|
|
|
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
|
.WillOnce(Return(ByMove(std::move(adaptation_set))));
|
|
EXPECT_CALL(*adaptation_set_ptr, AddRole(AdaptationSet::kRoleMain));
|
|
ASSERT_EQ(adaptation_set_ptr, testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kEnglishAudioContent),
|
|
content_protection_in_adaptation_set_));
|
|
}
|
|
|
|
TEST_F(PeriodTest, AudioAdaptationSetNonDefaultLanguage) {
|
|
mpd_options_.mpd_params.default_language = "fr";
|
|
const char kEnglishAudioContent[] =
|
|
"audio_info {\n"
|
|
" codec: 'mp4a.40.2'\n"
|
|
" sampling_frequency: 44100\n"
|
|
" time_scale: 1200\n"
|
|
" num_channels: 2\n"
|
|
" language: 'en'\n"
|
|
"}\n"
|
|
"reference_time_scale: 50\n"
|
|
"container_type: CONTAINER_MP4\n"
|
|
"media_duration_seconds: 10.5\n";
|
|
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set(
|
|
new StrictMock<MockAdaptationSet>());
|
|
auto* adaptation_set_ptr = adaptation_set.get();
|
|
|
|
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
|
.WillOnce(Return(ByMove(std::move(adaptation_set))));
|
|
EXPECT_CALL(*adaptation_set_ptr, AddRole(AdaptationSet::kRoleMain)).Times(0);
|
|
ASSERT_EQ(adaptation_set_ptr, testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kEnglishAudioContent),
|
|
content_protection_in_adaptation_set_));
|
|
}
|
|
|
|
TEST_F(PeriodTest, TextAdaptationSetDefaultLanguage) {
|
|
mpd_options_.mpd_params.default_language = "en";
|
|
const char kEnglishTextContent[] =
|
|
"text_info {\n"
|
|
" codec: 'webvtt'\n"
|
|
" language: 'en'\n"
|
|
" type: SUBTITLE\n"
|
|
"}";
|
|
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set(
|
|
new StrictMock<MockAdaptationSet>());
|
|
auto* adaptation_set_ptr = adaptation_set.get();
|
|
|
|
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
|
.WillOnce(Return(ByMove(std::move(adaptation_set))));
|
|
EXPECT_CALL(*adaptation_set_ptr, AddRole(AdaptationSet::kRoleMain));
|
|
EXPECT_CALL(*adaptation_set_ptr, ForceSetSegmentAlignment(true));
|
|
ASSERT_EQ(adaptation_set_ptr, testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kEnglishTextContent),
|
|
content_protection_in_adaptation_set_));
|
|
}
|
|
|
|
TEST_F(PeriodTest, TextAdaptationSetNonDefaultLanguage) {
|
|
mpd_options_.mpd_params.default_language = "fr";
|
|
const char kEnglishTextContent[] =
|
|
"text_info {\n"
|
|
" codec: 'webvtt'\n"
|
|
" language: 'en'\n"
|
|
" type: SUBTITLE\n"
|
|
"}";
|
|
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set(
|
|
new StrictMock<MockAdaptationSet>());
|
|
auto* adaptation_set_ptr = adaptation_set.get();
|
|
|
|
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
|
.WillOnce(Return(ByMove(std::move(adaptation_set))));
|
|
EXPECT_CALL(*adaptation_set_ptr, AddRole(AdaptationSet::kRoleMain)).Times(0);
|
|
EXPECT_CALL(*adaptation_set_ptr, ForceSetSegmentAlignment(true));
|
|
ASSERT_EQ(adaptation_set_ptr, testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kEnglishTextContent),
|
|
content_protection_in_adaptation_set_));
|
|
}
|
|
|
|
TEST_F(PeriodTest, TextAdaptationSetNonDefaultLanguageButDefaultTextLanguage) {
|
|
mpd_options_.mpd_params.default_language = "fr";
|
|
mpd_options_.mpd_params.default_text_language = "en";
|
|
const char kEnglishTextContent[] =
|
|
"text_info {\n"
|
|
" codec: 'webvtt'\n"
|
|
" language: 'en'\n"
|
|
" type: SUBTITLE\n"
|
|
"}";
|
|
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set(
|
|
new StrictMock<MockAdaptationSet>());
|
|
auto* adaptation_set_ptr = adaptation_set.get();
|
|
|
|
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
|
.WillOnce(Return(ByMove(std::move(adaptation_set))));
|
|
EXPECT_CALL(*adaptation_set_ptr, AddRole(AdaptationSet::kRoleMain));
|
|
EXPECT_CALL(*adaptation_set_ptr, ForceSetSegmentAlignment(true));
|
|
ASSERT_EQ(adaptation_set_ptr, testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kEnglishTextContent),
|
|
content_protection_in_adaptation_set_));
|
|
}
|
|
|
|
TEST_F(PeriodTest, TextAdaptationSetDefaultLanguageButNonDefaultTextLanguage) {
|
|
mpd_options_.mpd_params.default_language = "en";
|
|
mpd_options_.mpd_params.default_text_language = "fr";
|
|
const char kEnglishTextContent[] =
|
|
"text_info {\n"
|
|
" codec: 'webvtt'\n"
|
|
" language: 'en'\n"
|
|
" type: SUBTITLE\n"
|
|
"}";
|
|
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set(
|
|
new StrictMock<MockAdaptationSet>());
|
|
auto* adaptation_set_ptr = adaptation_set.get();
|
|
|
|
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
|
.WillOnce(Return(ByMove(std::move(adaptation_set))));
|
|
EXPECT_CALL(*adaptation_set_ptr, AddRole(AdaptationSet::kRoleMain)).Times(0);
|
|
EXPECT_CALL(*adaptation_set_ptr, ForceSetSegmentAlignment(true));
|
|
ASSERT_EQ(adaptation_set_ptr, testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kEnglishTextContent),
|
|
content_protection_in_adaptation_set_));
|
|
}
|
|
|
|
class PeriodTestWithContentProtection
|
|
: public PeriodTest,
|
|
public ::testing::WithParamInterface<bool> {
|
|
void SetUp() override { content_protection_in_adaptation_set_ = GetParam(); }
|
|
};
|
|
|
|
// With content_protection_adaptation_set_ == true, verify with different
|
|
// MediaInfo::ProtectedContent, two AdaptationSets should be created.
|
|
// AdaptationSets with different DRM won't be switchable.
|
|
// Otherwise, only one AdaptationSet is created.
|
|
TEST_P(PeriodTestWithContentProtection, DifferentProtectedContent) {
|
|
// Note they both have different (bogus) pssh, like real use case.
|
|
// default Key ID = _default_key_id_
|
|
const char kSdProtectedContent[] =
|
|
"video_info {\n"
|
|
" codec: 'avc1'\n"
|
|
" width: 640\n"
|
|
" height: 360\n"
|
|
" time_scale: 10\n"
|
|
" frame_duration: 10\n"
|
|
" pixel_width: 1\n"
|
|
" pixel_height: 1\n"
|
|
"}\n"
|
|
"protected_content {\n"
|
|
" content_protection_entry {\n"
|
|
" uuid: 'myuuid'\n"
|
|
" name_version: 'MyContentProtection version 1'\n"
|
|
" pssh: 'pssh1'\n"
|
|
" }\n"
|
|
" default_key_id: '_default_key_id_'\n"
|
|
"}\n"
|
|
"container_type: 1\n";
|
|
// default Key ID = .default.key.id.
|
|
const char kHdProtectedContent[] =
|
|
"video_info {\n"
|
|
" codec: 'avc1'\n"
|
|
" width: 1280\n"
|
|
" height: 720\n"
|
|
" time_scale: 10\n"
|
|
" frame_duration: 10\n"
|
|
" pixel_width: 1\n"
|
|
" pixel_height: 1\n"
|
|
"}\n"
|
|
"protected_content {\n"
|
|
" content_protection_entry {\n"
|
|
" uuid: 'anotheruuid'\n"
|
|
" name_version: 'SomeOtherProtection version 3'\n"
|
|
" pssh: 'pssh2'\n"
|
|
" }\n"
|
|
" default_key_id: '.default.key.id.'\n"
|
|
"}\n"
|
|
"container_type: 1\n";
|
|
|
|
// Check that the right ContentProtectionElements for SD is created.
|
|
// HD is the same case, so not checking.
|
|
ContentProtectionElement mp4_protection;
|
|
mp4_protection.scheme_id_uri = "urn:mpeg:dash:mp4protection:2011";
|
|
mp4_protection.value = "cenc";
|
|
// This should match the "_default_key_id_" above, but taking it as hex data
|
|
// and converted to UUID format.
|
|
mp4_protection.additional_attributes["cenc:default_KID"] =
|
|
"5f646566-6175-6c74-5f6b-65795f69645f";
|
|
ContentProtectionElement sd_my_drm;
|
|
sd_my_drm.scheme_id_uri = "urn:uuid:myuuid";
|
|
sd_my_drm.value = "MyContentProtection version 1";
|
|
Element cenc_pssh;
|
|
cenc_pssh.name = "cenc:pssh";
|
|
cenc_pssh.content = "cHNzaDE="; // Base64 encoding of 'pssh1'.
|
|
sd_my_drm.subelements.push_back(cenc_pssh);
|
|
|
|
// Not using default mocks in this test so that we can keep track of
|
|
// mocks by named mocks.
|
|
std::unique_ptr<StrictMock<MockAdaptationSet>> sd_adaptation_set(
|
|
new StrictMock<MockAdaptationSet>());
|
|
auto* sd_adaptation_set_ptr = sd_adaptation_set.get();
|
|
std::unique_ptr<StrictMock<MockAdaptationSet>> hd_adaptation_set(
|
|
new StrictMock<MockAdaptationSet>());
|
|
auto* hd_adaptation_set_ptr = hd_adaptation_set.get();
|
|
|
|
InSequence in_sequence;
|
|
|
|
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(
|
|
ContentProtectionElementEq(mp4_protection)));
|
|
EXPECT_CALL(
|
|
*sd_adaptation_set_ptr,
|
|
AddContentProtectionElement(ContentProtectionElementEq(sd_my_drm)));
|
|
|
|
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
|
.WillOnce(Return(ByMove(std::move(hd_adaptation_set))));
|
|
|
|
// Add main Role here for both.
|
|
EXPECT_CALL(*sd_adaptation_set_ptr, AddRole(AdaptationSet::kRoleMain));
|
|
EXPECT_CALL(*hd_adaptation_set_ptr, AddRole(AdaptationSet::kRoleMain));
|
|
|
|
// Called twice for the same reason as above.
|
|
EXPECT_CALL(*hd_adaptation_set_ptr, AddContentProtectionElement(_))
|
|
.Times(2);
|
|
}
|
|
|
|
ASSERT_EQ(sd_adaptation_set_ptr, testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kSdProtectedContent),
|
|
content_protection_in_adaptation_set_));
|
|
ASSERT_EQ(content_protection_in_adaptation_set_ ? hd_adaptation_set_ptr
|
|
: sd_adaptation_set_ptr,
|
|
testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kHdProtectedContent),
|
|
content_protection_in_adaptation_set_));
|
|
}
|
|
|
|
// Verify with the same MediaInfo::ProtectedContent, only one AdaptationSets
|
|
// should be created regardless of the value of
|
|
// content_protection_in_adaptation_set_.
|
|
TEST_P(PeriodTestWithContentProtection, SameProtectedContent) {
|
|
// These have the same default key ID and PSSH.
|
|
const char kSdProtectedContent[] =
|
|
"video_info {\n"
|
|
" codec: 'avc1'\n"
|
|
" width: 640\n"
|
|
" height: 360\n"
|
|
" time_scale: 10\n"
|
|
" frame_duration: 10\n"
|
|
" pixel_width: 1\n"
|
|
" pixel_height: 1\n"
|
|
"}\n"
|
|
"protected_content {\n"
|
|
" content_protection_entry {\n"
|
|
" uuid: 'myuuid'\n"
|
|
" name_version: 'MyContentProtection version 1'\n"
|
|
" pssh: 'psshbox'\n"
|
|
" }\n"
|
|
" default_key_id: '.DEFAULT.KEY.ID.'\n"
|
|
"}\n"
|
|
"container_type: 1\n";
|
|
const char kHdProtectedContent[] =
|
|
"video_info {\n"
|
|
" codec: 'avc1'\n"
|
|
" width: 1280\n"
|
|
" height: 720\n"
|
|
" time_scale: 10\n"
|
|
" frame_duration: 10\n"
|
|
" pixel_width: 1\n"
|
|
" pixel_height: 1\n"
|
|
"}\n"
|
|
"protected_content {\n"
|
|
" content_protection_entry {\n"
|
|
" uuid: 'myuuid'\n"
|
|
" name_version: 'MyContentProtection version 1'\n"
|
|
" pssh: 'psshbox'\n"
|
|
" }\n"
|
|
" default_key_id: '.DEFAULT.KEY.ID.'\n"
|
|
"}\n"
|
|
"container_type: 1\n";
|
|
|
|
ContentProtectionElement mp4_protection;
|
|
mp4_protection.scheme_id_uri = "urn:mpeg:dash:mp4protection:2011";
|
|
mp4_protection.value = "cenc";
|
|
// This should match the ".DEFAULT.KEY.ID." above, but taking it as hex data
|
|
// and converted to UUID format.
|
|
mp4_protection.additional_attributes["cenc:default_KID"] =
|
|
"2e444546-4155-4c54-2e4b-45592e49442e";
|
|
ContentProtectionElement my_drm;
|
|
my_drm.scheme_id_uri = "urn:uuid:myuuid";
|
|
my_drm.value = "MyContentProtection version 1";
|
|
Element cenc_pssh;
|
|
cenc_pssh.name = "cenc:pssh";
|
|
cenc_pssh.content = "cHNzaGJveA=="; // Base64 encoding of 'psshbox'.
|
|
my_drm.subelements.push_back(cenc_pssh);
|
|
|
|
InSequence in_sequence;
|
|
|
|
// Only called once.
|
|
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
|
.WillOnce(Return(ByMove(std::move(default_adaptation_set_))));
|
|
|
|
if (content_protection_in_adaptation_set_) {
|
|
EXPECT_CALL(*default_adaptation_set_ptr_,
|
|
AddContentProtectionElement(
|
|
ContentProtectionElementEq(mp4_protection)));
|
|
EXPECT_CALL(
|
|
*default_adaptation_set_ptr_,
|
|
AddContentProtectionElement(ContentProtectionElementEq(my_drm)));
|
|
}
|
|
|
|
ASSERT_EQ(default_adaptation_set_ptr_,
|
|
testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kSdProtectedContent),
|
|
content_protection_in_adaptation_set_));
|
|
ASSERT_EQ(default_adaptation_set_ptr_,
|
|
testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kHdProtectedContent),
|
|
content_protection_in_adaptation_set_));
|
|
}
|
|
|
|
// With content_protection_in_adaptation_set_ == true,
|
|
// Default Key IDs are different but if the content protection UUIDs match,
|
|
// then the AdaptationSet they belong to should be switchable. This is a long
|
|
// test. Basically this
|
|
// 1. Add an SD protected content. This should make an AdaptationSet.
|
|
// 2. Add an HD protected content. This should make another AdaptationSet that
|
|
// is different from the SD version. SD AdaptationSet and HD AdaptationSet
|
|
// should be switchable.
|
|
// 3. Add a 4k protected content. This should also make a new AdaptationSet.
|
|
// It should be switchable with SD/HD AdaptationSet.
|
|
// Otherwise only one AdaptationSet is created.
|
|
TEST_P(PeriodTestWithContentProtection, SetAdaptationSetSwitching) {
|
|
// These have the same default key ID and PSSH.
|
|
const char kSdProtectedContent[] =
|
|
"video_info {\n"
|
|
" codec: 'avc1'\n"
|
|
" width: 640\n"
|
|
" height: 360\n"
|
|
" time_scale: 10\n"
|
|
" frame_duration: 10\n"
|
|
" pixel_width: 1\n"
|
|
" pixel_height: 1\n"
|
|
"}\n"
|
|
"protected_content {\n"
|
|
" content_protection_entry {\n"
|
|
" uuid: 'myuuid'\n"
|
|
" name_version: 'MyContentProtection version 1'\n"
|
|
" pssh: 'pssh_sd'\n"
|
|
" }\n"
|
|
" default_key_id: '_default_key_id_'\n"
|
|
"}\n"
|
|
"container_type: 1\n";
|
|
const char kHdProtectedContent[] =
|
|
"video_info {\n"
|
|
" codec: 'avc1'\n"
|
|
" width: 1280\n"
|
|
" height: 720\n"
|
|
" time_scale: 10\n"
|
|
" frame_duration: 10\n"
|
|
" pixel_width: 1\n"
|
|
" pixel_height: 1\n"
|
|
"}\n"
|
|
"protected_content {\n"
|
|
" content_protection_entry {\n"
|
|
" uuid: 'myuuid'\n"
|
|
" name_version: 'MyContentProtection version 1'\n"
|
|
" pssh: 'pssh_hd'\n"
|
|
" }\n"
|
|
" default_key_id: '.DEFAULT.KEY.ID.'\n"
|
|
"}\n"
|
|
"container_type: 1\n";
|
|
|
|
const uint32_t kSdAdaptationSetId = 6u;
|
|
const uint32_t kHdAdaptationSetId = 7u;
|
|
std::unique_ptr<StrictMock<MockAdaptationSet>> sd_adaptation_set(
|
|
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>());
|
|
auto* hd_adaptation_set_ptr = hd_adaptation_set.get();
|
|
hd_adaptation_set_ptr->set_id(kHdAdaptationSetId);
|
|
|
|
InSequence in_sequence;
|
|
|
|
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(_, _, _))
|
|
.WillOnce(Return(ByMove(std::move(hd_adaptation_set))));
|
|
|
|
// Add main Role here for both.
|
|
EXPECT_CALL(*sd_adaptation_set_ptr, AddRole(AdaptationSet::kRoleMain));
|
|
EXPECT_CALL(*hd_adaptation_set_ptr, AddRole(AdaptationSet::kRoleMain));
|
|
|
|
EXPECT_CALL(*hd_adaptation_set_ptr, AddContentProtectionElement(_))
|
|
.Times(2);
|
|
|
|
EXPECT_CALL(*sd_adaptation_set_ptr,
|
|
AddAdaptationSetSwitching(hd_adaptation_set_ptr));
|
|
EXPECT_CALL(*hd_adaptation_set_ptr,
|
|
AddAdaptationSetSwitching(sd_adaptation_set_ptr));
|
|
}
|
|
|
|
ASSERT_EQ(sd_adaptation_set_ptr, testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kSdProtectedContent),
|
|
content_protection_in_adaptation_set_));
|
|
ASSERT_EQ(content_protection_in_adaptation_set_ ? hd_adaptation_set_ptr
|
|
: sd_adaptation_set_ptr,
|
|
testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kHdProtectedContent),
|
|
content_protection_in_adaptation_set_));
|
|
|
|
// Add another content that has the same protected content and make sure that
|
|
// adaptation set switching is set correctly.
|
|
const char k4kProtectedContent[] =
|
|
"video_info {\n"
|
|
" codec: 'avc1'\n"
|
|
" width: 4096\n"
|
|
" height: 2160\n"
|
|
" time_scale: 10\n"
|
|
" frame_duration: 10\n"
|
|
" pixel_width: 1\n"
|
|
" pixel_height: 1\n"
|
|
"}\n"
|
|
"protected_content {\n"
|
|
" content_protection_entry {\n"
|
|
" uuid: 'myuuid'\n"
|
|
" name_version: 'MyContentProtection version 1'\n"
|
|
" pssh: 'pssh_4k'\n"
|
|
" }\n"
|
|
" default_key_id: 'some16bytestring'\n"
|
|
"}\n"
|
|
"container_type: 1\n";
|
|
|
|
const uint32_t k4kAdaptationSetId = 4000u;
|
|
std::unique_ptr<StrictMock<MockAdaptationSet>> fourk_adaptation_set(
|
|
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(_, _, _))
|
|
.WillOnce(Return(ByMove(std::move(fourk_adaptation_set))));
|
|
|
|
EXPECT_CALL(*fourk_adaptation_set_ptr, AddRole(AdaptationSet::kRoleMain));
|
|
EXPECT_CALL(*fourk_adaptation_set_ptr, AddContentProtectionElement(_))
|
|
.Times(2);
|
|
|
|
EXPECT_CALL(*sd_adaptation_set_ptr,
|
|
AddAdaptationSetSwitching(fourk_adaptation_set_ptr));
|
|
EXPECT_CALL(*fourk_adaptation_set_ptr,
|
|
AddAdaptationSetSwitching(sd_adaptation_set_ptr));
|
|
EXPECT_CALL(*hd_adaptation_set_ptr,
|
|
AddAdaptationSetSwitching(fourk_adaptation_set_ptr));
|
|
EXPECT_CALL(*fourk_adaptation_set_ptr,
|
|
AddAdaptationSetSwitching(hd_adaptation_set_ptr));
|
|
}
|
|
|
|
ASSERT_EQ(content_protection_in_adaptation_set_ ? fourk_adaptation_set_ptr
|
|
: sd_adaptation_set_ptr,
|
|
testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(k4kProtectedContent),
|
|
content_protection_in_adaptation_set_));
|
|
}
|
|
|
|
// Even if the UUIDs match, video and audio AdaptationSets should not be
|
|
// switchable.
|
|
TEST_P(PeriodTestWithContentProtection,
|
|
DoNotSetAdaptationSetSwitchingIfContentTypesDifferent) {
|
|
// These have the same default key ID and PSSH.
|
|
const char kVideoContent[] =
|
|
"video_info {\n"
|
|
" codec: 'avc1'\n"
|
|
" width: 640\n"
|
|
" height: 360\n"
|
|
" time_scale: 10\n"
|
|
" frame_duration: 10\n"
|
|
" pixel_width: 1\n"
|
|
" pixel_height: 1\n"
|
|
"}\n"
|
|
"protected_content {\n"
|
|
" content_protection_entry {\n"
|
|
" uuid: 'myuuid'\n"
|
|
" name_version: 'MyContentProtection version 1'\n"
|
|
" pssh: 'pssh_video'\n"
|
|
" }\n"
|
|
" default_key_id: '_default_key_id_'\n"
|
|
"}\n"
|
|
"container_type: 1\n";
|
|
const char kAudioContent[] =
|
|
"audio_info {\n"
|
|
" codec: 'mp4a.40.2'\n"
|
|
" sampling_frequency: 44100\n"
|
|
" time_scale: 1200\n"
|
|
" num_channels: 2\n"
|
|
"}\n"
|
|
"protected_content {\n"
|
|
" content_protection_entry {\n"
|
|
" uuid: 'myuuid'\n"
|
|
" name_version: 'MyContentProtection version 1'\n"
|
|
" pssh: 'pssh_audio'\n"
|
|
" }\n"
|
|
" default_key_id: '_default_key_id_'\n"
|
|
"}\n"
|
|
"reference_time_scale: 50\n"
|
|
"container_type: 1\n"
|
|
"media_duration_seconds: 10.5\n";
|
|
|
|
const uint32_t kVideoAdaptationSetId = 6u;
|
|
const uint32_t kAudioAdaptationSetId = 7u;
|
|
std::unique_ptr<StrictMock<MockAdaptationSet>> video_adaptation_set(
|
|
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>());
|
|
auto* audio_adaptation_set_ptr = audio_adaptation_set.get();
|
|
audio_adaptation_set_ptr->set_id(kAudioAdaptationSetId);
|
|
|
|
InSequence in_sequence;
|
|
|
|
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(_, _, _))
|
|
.WillOnce(Return(ByMove(std::move(audio_adaptation_set))));
|
|
if (content_protection_in_adaptation_set_) {
|
|
EXPECT_CALL(*audio_adaptation_set_ptr, AddContentProtectionElement(_))
|
|
.Times(2);
|
|
}
|
|
|
|
ASSERT_EQ(video_adaptation_set_ptr,
|
|
testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kVideoContent),
|
|
content_protection_in_adaptation_set_));
|
|
ASSERT_EQ(audio_adaptation_set_ptr,
|
|
testable_period_.GetOrCreateAdaptationSet(
|
|
ConvertToMediaInfo(kAudioContent),
|
|
content_protection_in_adaptation_set_));
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(ContentProtectionInAdaptationSet,
|
|
PeriodTestWithContentProtection,
|
|
::testing::Bool());
|
|
|
|
} // namespace shaka
|