From 5aaae303e8b5881dfb49488ad77bc5c888cacb60 Mon Sep 17 00:00:00 2001 From: Kongqun Yang Date: Wed, 21 Dec 2016 15:28:56 -0800 Subject: [PATCH] Part one of supporting live profile with static mpd - Added two new fields in MpdOptions: dash_profile and mpd_type - Updated MpdBuilder to support LiveProfile with static mpd - Command line arguments will be updated in the next CL Issue #142 Change-Id: Ibd35e5c9e1b1ef98043392e3f04a0af4700135c1 --- packager/app/packager_main.cc | 10 +- packager/app/packager_util.cc | 6 + .../bear-320x240-opus-vp9-cenc-golden.mpd | 2 +- .../bear-320x240-vorbis-webm-golden.mpd | 2 +- .../bear-320x240-vp9-opus-webm-golden.mpd | 2 +- .../testdata/bear-640x360-av-cbc1-golden.mpd | 2 +- .../testdata/bear-640x360-av-cbcs-golden.mpd | 2 +- .../testdata/bear-640x360-av-cenc-golden.mpd | 2 +- .../bear-640x360-av-cenc-iop-golden.mpd | 2 +- .../testdata/bear-640x360-av-cens-golden.mpd | 2 +- .../test/testdata/bear-640x360-av-golden.mpd | 2 +- .../bear-640x360-av-live-cenc-golden.mpd | 2 +- .../bear-640x360-av-live-cenc-iop-golden.mpd | 2 +- ...r-640x360-av-live-cenc-rotation-golden.mpd | 2 +- ...0x360-av-live-cenc-rotation-iop-golden.mpd | 2 +- .../testdata/bear-640x360-av-live-golden.mpd | 2 +- .../test/testdata/bear-640x360-avt-golden.mpd | 2 +- .../bear-640x360-hevc-cenc-golden.mpd | 2 +- .../test/testdata/bear-640x360-v-golden.mpd | 2 +- .../testdata/bear-640x360-vp8-cenc-golden.mpd | 2 +- .../bear-640x360-vp8-cenc-webm-golden.mpd | 2 +- .../testdata/bear-640x360-vp8-webm-golden.mpd | 2 +- .../testdata/subtitle-english-vtt-golden.mpd | 2 +- .../media/event/mpd_notify_muxer_listener.cc | 17 +- .../mpd_notify_muxer_listener_unittest.cc | 42 +++- packager/mpd/base/dash_iop_mpd_notifier.cc | 9 +- packager/mpd/base/dash_iop_mpd_notifier.h | 3 +- .../base/dash_iop_mpd_notifier_unittest.cc | 119 ++++------ packager/mpd/base/mock_mpd_builder.cc | 5 +- packager/mpd/base/mock_mpd_builder.h | 4 +- packager/mpd/base/mock_mpd_notifier.cc | 3 +- packager/mpd/base/mock_mpd_notifier.h | 2 +- packager/mpd/base/mpd_builder.cc | 66 +++--- packager/mpd/base/mpd_builder.h | 26 +-- packager/mpd/base/mpd_builder_unittest.cc | 205 ++++++++++-------- packager/mpd/base/mpd_notifier.h | 19 +- packager/mpd/base/mpd_options.h | 32 +-- packager/mpd/base/simple_mpd_notifier.cc | 11 +- packager/mpd/base/simple_mpd_notifier.h | 3 +- .../mpd/base/simple_mpd_notifier_unittest.cc | 115 ++-------- packager/mpd/util/mpd_writer.cc | 18 +- packager/mpd/util/mpd_writer.h | 1 - packager/mpd/util/mpd_writer_unittest.cc | 5 +- 43 files changed, 333 insertions(+), 430 deletions(-) diff --git a/packager/app/packager_main.cc b/packager/app/packager_main.cc index 28343514fd..5a3b1a3eac 100644 --- a/packager/app/packager_main.cc +++ b/packager/app/packager_main.cc @@ -460,16 +460,14 @@ bool RunPackager(const StreamDescriptorList& stream_descriptors) { std::unique_ptr mpd_notifier; if (!FLAGS_mpd_output.empty()) { - DashProfile profile = - FLAGS_single_segment ? kOnDemandProfile : kLiveProfile; std::vector base_urls = base::SplitString( FLAGS_base_urls, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); if (FLAGS_generate_dash_if_iop_compliant_mpd) { - mpd_notifier.reset(new DashIopMpdNotifier(profile, mpd_options, base_urls, - FLAGS_mpd_output)); + mpd_notifier.reset( + new DashIopMpdNotifier(mpd_options, base_urls, FLAGS_mpd_output)); } else { - mpd_notifier.reset(new SimpleMpdNotifier(profile, mpd_options, base_urls, - FLAGS_mpd_output)); + mpd_notifier.reset( + new SimpleMpdNotifier(mpd_options, base_urls, FLAGS_mpd_output)); } if (!mpd_notifier->Init()) { LOG(ERROR) << "MpdNotifier failed to initialize."; diff --git a/packager/app/packager_util.cc b/packager/app/packager_util.cc index f55224f8cf..8962d4cc27 100644 --- a/packager/app/packager_util.cc +++ b/packager/app/packager_util.cc @@ -171,6 +171,12 @@ bool GetMuxerOptions(MuxerOptions* muxer_options) { bool GetMpdOptions(MpdOptions* mpd_options) { DCHECK(mpd_options); + mpd_options->dash_profile = + FLAGS_single_segment ? DashProfile::kOnDemand : DashProfile::kLive; + // Single segment does not always mean static mpd. + // TODO(kqyang): Add a new flag for mpd type and update the code. + mpd_options->mpd_type = + FLAGS_single_segment ? MpdType::kStatic : MpdType::kDynamic; mpd_options->availability_time_offset = FLAGS_availability_time_offset; mpd_options->minimum_update_period = FLAGS_minimum_update_period; mpd_options->min_buffer_time = FLAGS_min_buffer_time; diff --git a/packager/app/test/testdata/bear-320x240-opus-vp9-cenc-golden.mpd b/packager/app/test/testdata/bear-320x240-opus-vp9-cenc-golden.mpd index 9da678f1b3..a2b01241cb 100644 --- a/packager/app/test/testdata/bear-320x240-opus-vp9-cenc-golden.mpd +++ b/packager/app/test/testdata/bear-320x240-opus-vp9-cenc-golden.mpd @@ -1,6 +1,6 @@ - + diff --git a/packager/app/test/testdata/bear-320x240-vorbis-webm-golden.mpd b/packager/app/test/testdata/bear-320x240-vorbis-webm-golden.mpd index 69dfdd1dc1..d2fb244afc 100644 --- a/packager/app/test/testdata/bear-320x240-vorbis-webm-golden.mpd +++ b/packager/app/test/testdata/bear-320x240-vorbis-webm-golden.mpd @@ -1,6 +1,6 @@ - + diff --git a/packager/app/test/testdata/bear-320x240-vp9-opus-webm-golden.mpd b/packager/app/test/testdata/bear-320x240-vp9-opus-webm-golden.mpd index 39376b4a83..d6994a1f04 100644 --- a/packager/app/test/testdata/bear-320x240-vp9-opus-webm-golden.mpd +++ b/packager/app/test/testdata/bear-320x240-vp9-opus-webm-golden.mpd @@ -1,6 +1,6 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-av-cbc1-golden.mpd b/packager/app/test/testdata/bear-640x360-av-cbc1-golden.mpd index 34adc7f2b1..74b497555e 100644 --- a/packager/app/test/testdata/bear-640x360-av-cbc1-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-cbc1-golden.mpd @@ -1,6 +1,6 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-av-cbcs-golden.mpd b/packager/app/test/testdata/bear-640x360-av-cbcs-golden.mpd index 51db761ee3..e0e82abcbf 100644 --- a/packager/app/test/testdata/bear-640x360-av-cbcs-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-cbcs-golden.mpd @@ -1,6 +1,6 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-av-cenc-golden.mpd b/packager/app/test/testdata/bear-640x360-av-cenc-golden.mpd index d0e55e4035..a8feca0925 100644 --- a/packager/app/test/testdata/bear-640x360-av-cenc-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-cenc-golden.mpd @@ -1,6 +1,6 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-av-cenc-iop-golden.mpd b/packager/app/test/testdata/bear-640x360-av-cenc-iop-golden.mpd index bdc089982a..dee44d5362 100644 --- a/packager/app/test/testdata/bear-640x360-av-cenc-iop-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-cenc-iop-golden.mpd @@ -1,6 +1,6 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-av-cens-golden.mpd b/packager/app/test/testdata/bear-640x360-av-cens-golden.mpd index 0a2817bbd0..c11c8016b8 100644 --- a/packager/app/test/testdata/bear-640x360-av-cens-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-cens-golden.mpd @@ -1,6 +1,6 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-av-golden.mpd b/packager/app/test/testdata/bear-640x360-av-golden.mpd index 8138233e10..1b22d067cc 100644 --- a/packager/app/test/testdata/bear-640x360-av-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-golden.mpd @@ -1,6 +1,6 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-av-live-cenc-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-cenc-golden.mpd index be89be88dd..d6c6df6147 100644 --- a/packager/app/test/testdata/bear-640x360-av-live-cenc-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-live-cenc-golden.mpd @@ -1,6 +1,6 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-av-live-cenc-iop-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-cenc-iop-golden.mpd index 09b3a9465c..74c7f215b7 100644 --- a/packager/app/test/testdata/bear-640x360-av-live-cenc-iop-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-live-cenc-iop-golden.mpd @@ -1,6 +1,6 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-golden.mpd index 57cf0d914f..acac1aef23 100644 --- a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-golden.mpd @@ -1,6 +1,6 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-iop-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-iop-golden.mpd index ba89d8e04d..ee85437b08 100644 --- a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-iop-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-iop-golden.mpd @@ -1,6 +1,6 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-av-live-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-golden.mpd index b1c1185524..3d2b5a017e 100644 --- a/packager/app/test/testdata/bear-640x360-av-live-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-live-golden.mpd @@ -1,6 +1,6 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-avt-golden.mpd b/packager/app/test/testdata/bear-640x360-avt-golden.mpd index cd25c4be61..1247ce8bb5 100644 --- a/packager/app/test/testdata/bear-640x360-avt-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-avt-golden.mpd @@ -1,6 +1,6 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-hevc-cenc-golden.mpd b/packager/app/test/testdata/bear-640x360-hevc-cenc-golden.mpd index 94dd273f9b..332dea306a 100644 --- a/packager/app/test/testdata/bear-640x360-hevc-cenc-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-hevc-cenc-golden.mpd @@ -1,6 +1,6 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-v-golden.mpd b/packager/app/test/testdata/bear-640x360-v-golden.mpd index 7069a8538f..ff72edae50 100644 --- a/packager/app/test/testdata/bear-640x360-v-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-v-golden.mpd @@ -1,6 +1,6 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-vp8-cenc-golden.mpd b/packager/app/test/testdata/bear-640x360-vp8-cenc-golden.mpd index 817ff0c2d5..fcad14159d 100644 --- a/packager/app/test/testdata/bear-640x360-vp8-cenc-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-vp8-cenc-golden.mpd @@ -1,6 +1,6 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-vp8-cenc-webm-golden.mpd b/packager/app/test/testdata/bear-640x360-vp8-cenc-webm-golden.mpd index d2f0a15b71..10c96f78a4 100644 --- a/packager/app/test/testdata/bear-640x360-vp8-cenc-webm-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-vp8-cenc-webm-golden.mpd @@ -1,6 +1,6 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-vp8-webm-golden.mpd b/packager/app/test/testdata/bear-640x360-vp8-webm-golden.mpd index 68a6b1d3ec..4622bff4c9 100644 --- a/packager/app/test/testdata/bear-640x360-vp8-webm-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-vp8-webm-golden.mpd @@ -1,6 +1,6 @@ - + diff --git a/packager/app/test/testdata/subtitle-english-vtt-golden.mpd b/packager/app/test/testdata/subtitle-english-vtt-golden.mpd index 62f2e3a3ed..7ac6252878 100644 --- a/packager/app/test/testdata/subtitle-english-vtt-golden.mpd +++ b/packager/app/test/testdata/subtitle-english-vtt-golden.mpd @@ -1,6 +1,6 @@ - + diff --git a/packager/media/event/mpd_notify_muxer_listener.cc b/packager/media/event/mpd_notify_muxer_listener.cc index 53d2c39069..39bfbf1d6b 100644 --- a/packager/media/event/mpd_notify_muxer_listener.cc +++ b/packager/media/event/mpd_notify_muxer_listener.cc @@ -22,8 +22,8 @@ namespace media { MpdNotifyMuxerListener::MpdNotifyMuxerListener(MpdNotifier* mpd_notifier) : mpd_notifier_(mpd_notifier), notification_id_(0), is_encrypted_(false) { DCHECK(mpd_notifier); - DCHECK(mpd_notifier->dash_profile() == kOnDemandProfile || - mpd_notifier->dash_profile() == kLiveProfile); + DCHECK(mpd_notifier->dash_profile() == DashProfile::kOnDemand || + mpd_notifier->dash_profile() == DashProfile::kLive); } MpdNotifyMuxerListener::~MpdNotifyMuxerListener() {} @@ -76,7 +76,7 @@ void MpdNotifyMuxerListener::OnMediaStart( key_system_info_, media_info.get()); } - if (mpd_notifier_->dash_profile() == kLiveProfile) { + if (mpd_notifier_->dash_profile() == DashProfile::kLive) { // TODO(kqyang): Check return result. mpd_notifier_->NotifyNewContainer(*media_info, ¬ification_id_); } else { @@ -88,7 +88,7 @@ void MpdNotifyMuxerListener::OnMediaStart( // the information is in the media info. void MpdNotifyMuxerListener::OnSampleDurationReady( uint32_t sample_duration) { - if (mpd_notifier_->dash_profile() == kLiveProfile) { + if (mpd_notifier_->dash_profile() == DashProfile::kLive) { mpd_notifier_->NotifySampleDuration(notification_id_, sample_duration); return; } @@ -117,8 +117,10 @@ void MpdNotifyMuxerListener::OnMediaEnd(bool has_init_range, uint64_t index_range_end, float duration_seconds, uint64_t file_size) { - if (mpd_notifier_->dash_profile() == kLiveProfile) { + if (mpd_notifier_->dash_profile() == DashProfile::kLive) { DCHECK(subsegments_.empty()); + if (mpd_notifier_->mpd_type() == MpdType::kStatic) + mpd_notifier_->Flush(); return; } @@ -152,11 +154,12 @@ void MpdNotifyMuxerListener::OnNewSegment(const std::string& file_name, uint64_t start_time, uint64_t duration, uint64_t segment_file_size) { - if (mpd_notifier_->dash_profile() == kLiveProfile) { + if (mpd_notifier_->dash_profile() == DashProfile::kLive) { // TODO(kqyang): Check return result. mpd_notifier_->NotifyNewSegment( notification_id_, start_time, duration, segment_file_size); - mpd_notifier_->Flush(); + if (mpd_notifier_->mpd_type() == MpdType::kDynamic) + mpd_notifier_->Flush(); } else { SubsegmentInfo subsegment = {start_time, duration, segment_file_size}; subsegments_.push_back(subsegment); diff --git a/packager/media/event/mpd_notify_muxer_listener_unittest.cc b/packager/media/event/mpd_notify_muxer_listener_unittest.cc index 14712ac3a2..768719a864 100644 --- a/packager/media/event/mpd_notify_muxer_listener_unittest.cc +++ b/packager/media/event/mpd_notify_muxer_listener_unittest.cc @@ -64,17 +64,25 @@ const uint8_t kBogusIv[] = { namespace media { -class MpdNotifyMuxerListenerTest : public ::testing::Test { +class MpdNotifyMuxerListenerTest : public ::testing::TestWithParam { public: void SetupForVod() { - notifier_.reset(new MockMpdNotifier(kOnDemandProfile)); + MpdOptions mpd_options; + mpd_options.dash_profile = DashProfile::kOnDemand; + // On-demand profile should be static. + mpd_options.mpd_type = MpdType::kStatic; + notifier_.reset(new MockMpdNotifier(mpd_options)); listener_.reset( new MpdNotifyMuxerListener(notifier_.get())); } void SetupForLive() { - notifier_.reset(new MockMpdNotifier(kLiveProfile)); + MpdOptions mpd_options; + mpd_options.dash_profile = DashProfile::kLive; + // Live profile can be static or dynamic. + mpd_options.mpd_type = GetParam(); + notifier_.reset(new MockMpdNotifier(mpd_options)); listener_.reset(new MpdNotifyMuxerListener(notifier_.get())); } @@ -271,7 +279,7 @@ TEST_F(MpdNotifyMuxerListenerTest, VodOnNewSegment) { // Live without key rotation. Note that OnEncryptionInfoReady() is called before // OnMediaStart() but no more calls. -TEST_F(MpdNotifyMuxerListenerTest, LiveNoKeyRotation) { +TEST_P(MpdNotifyMuxerListenerTest, LiveNoKeyRotation) { SetupForLive(); MuxerOptions muxer_options; SetDefaultLiveMuxerOptionsValues(&muxer_options); @@ -317,10 +325,13 @@ TEST_F(MpdNotifyMuxerListenerTest, LiveNoKeyRotation) { .Times(1); EXPECT_CALL(*notifier_, NotifyNewSegment(_, kStartTime1, kDuration1, kSegmentFileSize1)); - EXPECT_CALL(*notifier_, Flush()); + // Flush should only be called once in OnMediaEnd. + if (GetParam() == MpdType::kDynamic) + EXPECT_CALL(*notifier_, Flush()); EXPECT_CALL(*notifier_, NotifyNewSegment(_, kStartTime2, kDuration2, kSegmentFileSize2)); - EXPECT_CALL(*notifier_, Flush()); + if (GetParam() == MpdType::kDynamic) + EXPECT_CALL(*notifier_, Flush()); std::vector iv(kBogusIv, kBogusIv + arraysize(kBogusIv)); listener_->OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cbcs, @@ -333,13 +344,14 @@ TEST_F(MpdNotifyMuxerListenerTest, LiveNoKeyRotation) { listener_->OnNewSegment("", kStartTime2, kDuration2, kSegmentFileSize2); ::testing::Mock::VerifyAndClearExpectations(notifier_.get()); - EXPECT_CALL(*notifier_, Flush()).Times(0); + EXPECT_CALL(*notifier_, Flush()) + .Times(GetParam() == MpdType::kDynamic ? 0 : 1); FireOnMediaEndWithParams(GetDefaultOnMediaEndParams()); } // Live with key rotation. Note that OnEncryptionInfoReady() is called before // and after OnMediaStart(). -TEST_F(MpdNotifyMuxerListenerTest, LiveWithKeyRotation) { +TEST_P(MpdNotifyMuxerListenerTest, LiveWithKeyRotation) { SetupForLive(); MuxerOptions muxer_options; SetDefaultLiveMuxerOptionsValues(&muxer_options); @@ -382,10 +394,13 @@ TEST_F(MpdNotifyMuxerListenerTest, LiveWithKeyRotation) { EXPECT_CALL(*notifier_, NotifyEncryptionUpdate(_, _, _, _)).Times(1); EXPECT_CALL(*notifier_, NotifyNewSegment(_, kStartTime1, kDuration1, kSegmentFileSize1)); - EXPECT_CALL(*notifier_, Flush()); + // Flush should only be called once in OnMediaEnd. + if (GetParam() == MpdType::kDynamic) + EXPECT_CALL(*notifier_, Flush()); EXPECT_CALL(*notifier_, NotifyNewSegment(_, kStartTime2, kDuration2, kSegmentFileSize2)); - EXPECT_CALL(*notifier_, Flush()); + if (GetParam() == MpdType::kDynamic) + EXPECT_CALL(*notifier_, Flush()); std::vector iv(kBogusIv, kBogusIv + arraysize(kBogusIv)); listener_->OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cbc1, @@ -401,9 +416,14 @@ TEST_F(MpdNotifyMuxerListenerTest, LiveWithKeyRotation) { listener_->OnNewSegment("", kStartTime2, kDuration2, kSegmentFileSize2); ::testing::Mock::VerifyAndClearExpectations(notifier_.get()); - EXPECT_CALL(*notifier_, Flush()).Times(0); + EXPECT_CALL(*notifier_, Flush()) + .Times(GetParam() == MpdType::kDynamic ? 0 : 1); FireOnMediaEndWithParams(GetDefaultOnMediaEndParams()); } +INSTANTIATE_TEST_CASE_P(StaticAndDynamic, + MpdNotifyMuxerListenerTest, + ::testing::Values(MpdType::kStatic, MpdType::kDynamic)); + } // namespace media } // namespace shaka diff --git a/packager/mpd/base/dash_iop_mpd_notifier.cc b/packager/mpd/base/dash_iop_mpd_notifier.cc index 62e4810772..f48dc854a2 100644 --- a/packager/mpd/base/dash_iop_mpd_notifier.cc +++ b/packager/mpd/base/dash_iop_mpd_notifier.cc @@ -39,17 +39,12 @@ std::set GetUUIDs( } // namespace DashIopMpdNotifier::DashIopMpdNotifier( - DashProfile dash_profile, const MpdOptions& mpd_options, const std::vector& base_urls, const std::string& output_path) - : MpdNotifier(dash_profile), + : MpdNotifier(mpd_options), output_path_(output_path), - mpd_builder_(new MpdBuilder(dash_profile == kLiveProfile - ? MpdBuilder::kDynamic - : MpdBuilder::kStatic, - mpd_options)) { - DCHECK(dash_profile == kLiveProfile || dash_profile == kOnDemandProfile); + mpd_builder_(new MpdBuilder(mpd_options)) { for (size_t i = 0; i < base_urls.size(); ++i) mpd_builder_->AddBaseUrl(base_urls[i]); } diff --git a/packager/mpd/base/dash_iop_mpd_notifier.h b/packager/mpd/base/dash_iop_mpd_notifier.h index c454aa70f0..a9cc1ebe01 100644 --- a/packager/mpd/base/dash_iop_mpd_notifier.h +++ b/packager/mpd/base/dash_iop_mpd_notifier.h @@ -29,8 +29,7 @@ namespace shaka { /// All video Adaptation Sets have Role set to "main". class DashIopMpdNotifier : public MpdNotifier { public: - DashIopMpdNotifier(DashProfile dash_profile, - const MpdOptions& mpd_options, + DashIopMpdNotifier(const MpdOptions& mpd_options, const std::vector& base_urls, const std::string& output_path); ~DashIopMpdNotifier() override; diff --git a/packager/mpd/base/dash_iop_mpd_notifier_unittest.cc b/packager/mpd/base/dash_iop_mpd_notifier_unittest.cc index ce9693fa8e..8a08437846 100644 --- a/packager/mpd/base/dash_iop_mpd_notifier_unittest.cc +++ b/packager/mpd/base/dash_iop_mpd_notifier_unittest.cc @@ -91,8 +91,7 @@ MATCHER_P(ContentProtectionElementEq, expected, "") { // (https://code.google.com/p/googletest/wiki/AdvancedGuide#Typed_Tests); // also because SimpleMpdNotifier and DashIopMpdNotifier have common behavior // for most of the public functions. -class DashIopMpdNotifierTest - : public ::testing::TestWithParam { +class DashIopMpdNotifierTest : public ::testing::Test { protected: DashIopMpdNotifierTest() : default_mock_adaptation_set_( @@ -109,23 +108,11 @@ class DashIopMpdNotifierTest base::DeleteFile(temp_file_path_, false /* non recursive, just 1 file */); } - MpdBuilder::MpdType GetMpdBuilderType(const DashIopMpdNotifier& notifier) { - return notifier.MpdBuilderForTesting()->type(); - } - void SetMpdBuilder(DashIopMpdNotifier* notifier, std::unique_ptr mpd_builder) { notifier->SetMpdBuilderForTesting(std::move(mpd_builder)); } - MpdBuilder::MpdType mpd_type() { - return GetParam(); - } - - DashProfile dash_profile() { - return mpd_type() == MpdBuilder::kStatic ? kOnDemandProfile : kLiveProfile; - } - // Use output_path_ for specifying the MPD output path so that // WriteMpdToFile() doesn't crash. std::string output_path_; @@ -142,27 +129,13 @@ class DashIopMpdNotifierTest base::FilePath temp_file_path_; }; -// Verify that it creates the correct MpdBuilder type using DashProfile passed -// to the constructor. -TEST_F(DashIopMpdNotifierTest, CreateCorrectMpdBuilderType) { - DashIopMpdNotifier on_demand_notifier(kOnDemandProfile, empty_mpd_option_, - empty_base_urls_, output_path_); - EXPECT_TRUE(on_demand_notifier.Init()); - EXPECT_EQ(MpdBuilder::kStatic, GetMpdBuilderType(on_demand_notifier)); - DashIopMpdNotifier live_notifier(kLiveProfile, empty_mpd_option_, - empty_base_urls_, output_path_); - EXPECT_TRUE(live_notifier.Init()); - EXPECT_EQ(MpdBuilder::kDynamic, GetMpdBuilderType(live_notifier)); -} - // Verify that basic VOD NotifyNewContainer() operation works. // No encrypted contents. -TEST_P(DashIopMpdNotifierTest, NotifyNewContainer) { - DashIopMpdNotifier notifier(dash_profile(), empty_mpd_option_, - empty_base_urls_, output_path_); +TEST_F(DashIopMpdNotifierTest, NotifyNewContainer) { + DashIopMpdNotifier notifier(empty_mpd_option_, empty_base_urls_, + output_path_); - std::unique_ptr mock_mpd_builder( - new MockMpdBuilder(mpd_type())); + std::unique_ptr mock_mpd_builder(new MockMpdBuilder()); EXPECT_CALL(*mock_mpd_builder, AddAdaptationSet(_)) .WillOnce(Return(default_mock_adaptation_set_.get())); @@ -183,18 +156,17 @@ TEST_P(DashIopMpdNotifierTest, NotifyNewContainer) { // Verify that if the MediaInfo contains text information, then // MpdBuilder::ForceSetSegmentAlignment() is called. -TEST_P(DashIopMpdNotifierTest, NotifyNewTextContainer) { +TEST_F(DashIopMpdNotifierTest, NotifyNewTextContainer) { const char kTextMediaInfo[] = "text_info {\n" " format: 'ttml'\n" " language: 'en'\n" "}\n" "container_type: CONTAINER_TEXT\n"; - DashIopMpdNotifier notifier(dash_profile(), empty_mpd_option_, - empty_base_urls_, output_path_); + DashIopMpdNotifier notifier(empty_mpd_option_, empty_base_urls_, + output_path_); - std::unique_ptr mock_mpd_builder( - new MockMpdBuilder(mpd_type())); + std::unique_ptr mock_mpd_builder(new MockMpdBuilder()); EXPECT_CALL(*mock_mpd_builder, AddAdaptationSet(StrEq("en"))) .WillOnce(Return(default_mock_adaptation_set_.get())); @@ -218,12 +190,11 @@ TEST_P(DashIopMpdNotifierTest, NotifyNewTextContainer) { // MediaInfo::ProtectedContent. // Two AdaptationSets should be created. // AdaptationSets with different DRM won't be switchable. -TEST_P(DashIopMpdNotifierTest, +TEST_F(DashIopMpdNotifierTest, NotifyNewContainersWithDifferentProtectedContent) { - DashIopMpdNotifier notifier(dash_profile(), empty_mpd_option_, - empty_base_urls_, output_path_); - std::unique_ptr mock_mpd_builder( - new MockMpdBuilder(mpd_type())); + DashIopMpdNotifier notifier(empty_mpd_option_, empty_base_urls_, + output_path_); + std::unique_ptr mock_mpd_builder(new MockMpdBuilder()); // Note they both have different (bogus) pssh, like real use case. // default Key ID = _default_key_id_ @@ -338,11 +309,10 @@ TEST_P(DashIopMpdNotifierTest, // Verify VOD NotifyNewContainer() operation works with same // MediaInfo::ProtectedContent. Only one AdaptationSet should be // created. -TEST_P(DashIopMpdNotifierTest, NotifyNewContainersWithSameProtectedContent) { - DashIopMpdNotifier notifier(dash_profile(), empty_mpd_option_, - empty_base_urls_, output_path_); - std::unique_ptr mock_mpd_builder( - new MockMpdBuilder(mpd_type())); +TEST_F(DashIopMpdNotifierTest, NotifyNewContainersWithSameProtectedContent) { + DashIopMpdNotifier notifier(empty_mpd_option_, empty_base_urls_, + output_path_); + std::unique_ptr mock_mpd_builder(new MockMpdBuilder()); // These have the same default key ID and PSSH. const char kSdProtectedContent[] = @@ -441,12 +411,11 @@ TEST_P(DashIopMpdNotifierTest, NotifyNewContainersWithSameProtectedContent) { } // AddContentProtection() should not work and should always return false. -TEST_P(DashIopMpdNotifierTest, AddContentProtection) { - DashIopMpdNotifier notifier(dash_profile(), empty_mpd_option_, - empty_base_urls_, output_path_); +TEST_F(DashIopMpdNotifierTest, AddContentProtection) { + DashIopMpdNotifier notifier(empty_mpd_option_, empty_base_urls_, + output_path_); - std::unique_ptr mock_mpd_builder( - new MockMpdBuilder(mpd_type())); + std::unique_ptr mock_mpd_builder(new MockMpdBuilder()); EXPECT_CALL(*mock_mpd_builder, AddAdaptationSet(_)) .WillOnce(Return(default_mock_adaptation_set_.get())); @@ -473,11 +442,10 @@ TEST_P(DashIopMpdNotifierTest, AddContentProtection) { // should be switchable. // 3. Add a 4k protected content. This should also make a new AdaptationSet. // It should be switchable with SD/HD AdaptationSet. -TEST_P(DashIopMpdNotifierTest, SetAdaptationSetSwitching) { - DashIopMpdNotifier notifier(dash_profile(), empty_mpd_option_, - empty_base_urls_, output_path_); - std::unique_ptr mock_mpd_builder( - new MockMpdBuilder(mpd_type())); +TEST_F(DashIopMpdNotifierTest, SetAdaptationSetSwitching) { + DashIopMpdNotifier notifier(empty_mpd_option_, empty_base_urls_, + output_path_); + std::unique_ptr mock_mpd_builder(new MockMpdBuilder()); // These have the same default key ID and PSSH. const char kSdProtectedContent[] = @@ -610,12 +578,11 @@ TEST_P(DashIopMpdNotifierTest, SetAdaptationSetSwitching) { // Even if the UUIDs match, video and audio AdaptationSets should not be // switchable. -TEST_P(DashIopMpdNotifierTest, +TEST_F(DashIopMpdNotifierTest, DoNotSetAdaptationSetSwitchingIfContentTypesDifferent) { - DashIopMpdNotifier notifier(dash_profile(), empty_mpd_option_, - empty_base_urls_, output_path_); - std::unique_ptr mock_mpd_builder( - new MockMpdBuilder(mpd_type())); + DashIopMpdNotifier notifier(empty_mpd_option_, empty_base_urls_, + output_path_); + std::unique_ptr mock_mpd_builder(new MockMpdBuilder()); // These have the same default key ID and PSSH. const char kVideoContent[] = @@ -699,7 +666,7 @@ TEST_P(DashIopMpdNotifierTest, ElementsAre()); } -TEST_P(DashIopMpdNotifierTest, UpdateEncryption) { +TEST_F(DashIopMpdNotifierTest, UpdateEncryption) { const char kProtectedContent[] = "video_info {\n" " codec: 'avc1'\n" @@ -720,11 +687,10 @@ TEST_P(DashIopMpdNotifierTest, UpdateEncryption) { "}\n" "container_type: 1\n"; - DashIopMpdNotifier notifier(dash_profile(), empty_mpd_option_, - empty_base_urls_, output_path_); + DashIopMpdNotifier notifier(empty_mpd_option_, empty_base_urls_, + output_path_); - std::unique_ptr mock_mpd_builder( - new MockMpdBuilder(mpd_type())); + std::unique_ptr mock_mpd_builder(new MockMpdBuilder()); EXPECT_CALL(*mock_mpd_builder, AddAdaptationSet(_)) .WillOnce(Return(default_mock_adaptation_set_.get())); @@ -761,8 +727,8 @@ TEST_P(DashIopMpdNotifierTest, UpdateEncryption) { // This issue identified a bug where using SimpleMpdNotifier with multiple // threads causes a deadlock. This tests with DashIopMpdNotifier. TEST_F(DashIopMpdNotifierTest, NotifyNewContainerAndSampleDurationNoMock) { - DashIopMpdNotifier notifier(kOnDemandProfile, empty_mpd_option_, - empty_base_urls_, output_path_); + DashIopMpdNotifier notifier(empty_mpd_option_, empty_base_urls_, + output_path_); uint32_t container_id; EXPECT_TRUE(notifier.NotifyNewContainer(ConvertToMediaInfo(kValidMediaInfo), &container_id)); @@ -772,7 +738,7 @@ TEST_F(DashIopMpdNotifierTest, NotifyNewContainerAndSampleDurationNoMock) { } // Don't put different audio languages or codecs in the same AdaptationSet. -TEST_P(DashIopMpdNotifierTest, SplitAdaptationSetsByLanguageAndCodec) { +TEST_F(DashIopMpdNotifierTest, SplitAdaptationSetsByLanguageAndCodec) { // MP4, English const char kAudioContent1[] = "audio_info {\n" @@ -825,10 +791,9 @@ TEST_P(DashIopMpdNotifierTest, SplitAdaptationSetsByLanguageAndCodec) { "container_type: CONTAINER_WEBM\n" "media_duration_seconds: 10.5\n"; - DashIopMpdNotifier notifier(dash_profile(), empty_mpd_option_, - empty_base_urls_, output_path_); - std::unique_ptr mock_mpd_builder( - new MockMpdBuilder(mpd_type())); + DashIopMpdNotifier notifier(empty_mpd_option_, empty_base_urls_, + output_path_); + std::unique_ptr mock_mpd_builder(new MockMpdBuilder()); std::unique_ptr adaptation_set1(new MockAdaptationSet(1)); std::unique_ptr adaptation_set2(new MockAdaptationSet(2)); @@ -871,10 +836,4 @@ TEST_P(DashIopMpdNotifierTest, SplitAdaptationSetsByLanguageAndCodec) { ConvertToMediaInfo(kAudioContent4), &unused_container_id)); } - -INSTANTIATE_TEST_CASE_P(StaticAndDynamic, - DashIopMpdNotifierTest, - ::testing::Values(MpdBuilder::kStatic, - MpdBuilder::kDynamic)); - } // namespace shaka diff --git a/packager/mpd/base/mock_mpd_builder.cc b/packager/mpd/base/mock_mpd_builder.cc index e229d00764..09a851bc8e 100644 --- a/packager/mpd/base/mock_mpd_builder.cc +++ b/packager/mpd/base/mock_mpd_builder.cc @@ -6,20 +6,17 @@ namespace shaka { namespace { const char kEmptyLang[] = ""; const MpdOptions kDefaultMpdOptions; -const MpdBuilder::MpdType kDefaultMpdType = MpdBuilder::kStatic; } // namespace // Doesn't matter what values get passed to the super class' constructor. // All methods used for testing should be mocked. -MockMpdBuilder::MockMpdBuilder(MpdType type) - : MpdBuilder(type, kDefaultMpdOptions) {} +MockMpdBuilder::MockMpdBuilder() : MpdBuilder(kDefaultMpdOptions) {} MockMpdBuilder::~MockMpdBuilder() {} MockAdaptationSet::MockAdaptationSet(uint32_t adaptation_set_id) : AdaptationSet(adaptation_set_id, kEmptyLang, kDefaultMpdOptions, - kDefaultMpdType, &sequence_counter_) {} MockAdaptationSet::~MockAdaptationSet() {} diff --git a/packager/mpd/base/mock_mpd_builder.h b/packager/mpd/base/mock_mpd_builder.h index 97f93c4055..e74e6e94ec 100644 --- a/packager/mpd/base/mock_mpd_builder.h +++ b/packager/mpd/base/mock_mpd_builder.h @@ -18,9 +18,7 @@ namespace shaka { class MockMpdBuilder : public MpdBuilder { public: - // |type| indicates whether the MPD should be for VOD or live content (kStatic - // for VOD profile, or kDynamic for live profile). - explicit MockMpdBuilder(MpdType type); + MockMpdBuilder(); ~MockMpdBuilder() override; MOCK_METHOD1(AddAdaptationSet, AdaptationSet*(const std::string& lang)); diff --git a/packager/mpd/base/mock_mpd_notifier.cc b/packager/mpd/base/mock_mpd_notifier.cc index 6e87d9da0b..366ee9266d 100644 --- a/packager/mpd/base/mock_mpd_notifier.cc +++ b/packager/mpd/base/mock_mpd_notifier.cc @@ -2,7 +2,8 @@ namespace shaka { -MockMpdNotifier::MockMpdNotifier(DashProfile profile) : MpdNotifier(profile) {} +MockMpdNotifier::MockMpdNotifier(const MpdOptions& mpd_options) + : MpdNotifier(mpd_options) {} MockMpdNotifier::~MockMpdNotifier() {} } // namespace shaka diff --git a/packager/mpd/base/mock_mpd_notifier.h b/packager/mpd/base/mock_mpd_notifier.h index 45897c5bb0..d4a78859dc 100644 --- a/packager/mpd/base/mock_mpd_notifier.h +++ b/packager/mpd/base/mock_mpd_notifier.h @@ -18,7 +18,7 @@ namespace shaka { class MockMpdNotifier : public MpdNotifier { public: - MockMpdNotifier(DashProfile profile); + explicit MockMpdNotifier(const MpdOptions& mpd_options); virtual ~MockMpdNotifier(); MOCK_METHOD0(Init, bool()); diff --git a/packager/mpd/base/mpd_builder.cc b/packager/mpd/base/mpd_builder.cc index 6a5a42d884..0bea87df40 100644 --- a/packager/mpd/base/mpd_builder.cc +++ b/packager/mpd/base/mpd_builder.cc @@ -396,10 +396,8 @@ class RepresentationStateChangeListenerImpl } // namespace -MpdBuilder::MpdBuilder(MpdType type, const MpdOptions& mpd_options) - : type_(type), - mpd_options_(mpd_options), - clock_(new base::DefaultClock()) {} +MpdBuilder::MpdBuilder(const MpdOptions& mpd_options) + : mpd_options_(mpd_options), clock_(new base::DefaultClock()) {} MpdBuilder::~MpdBuilder() {} @@ -410,7 +408,7 @@ void MpdBuilder::AddBaseUrl(const std::string& base_url) { AdaptationSet* MpdBuilder::AddAdaptationSet(const std::string& lang) { std::unique_ptr adaptation_set( new AdaptationSet(adaptation_set_counter_.GetNext(), lang, mpd_options_, - type_, &representation_counter_)); + &representation_counter_)); DCHECK(adaptation_set); if (!lang.empty() && lang == mpd_options_.default_language) { @@ -482,7 +480,8 @@ xmlDocPtr MpdBuilder::GenerateMpd() { return NULL; } - if (type_ == kDynamic) { + // TODO(kqyang): Should we set @start unconditionally to 0? + if (mpd_options_.mpd_type == MpdType::kDynamic) { // This is the only Period and it is a regular period. period.SetStringAttribute("start", "PT0S"); } @@ -491,16 +490,35 @@ xmlDocPtr MpdBuilder::GenerateMpd() { return NULL; AddMpdNameSpaceInfo(&mpd); + + static const char kOnDemandProfile[] = + "urn:mpeg:dash:profile:isoff-on-demand:2011"; + static const char kLiveProfile[] = + "urn:mpeg:dash:profile:isoff-live:2011"; + switch (mpd_options_.dash_profile) { + case DashProfile::kOnDemand: + mpd.SetStringAttribute("profiles", kOnDemandProfile); + break; + case DashProfile::kLive: + mpd.SetStringAttribute("profiles", kLiveProfile); + break; + default: + NOTREACHED() << "Unknown DASH profile: " + << static_cast(mpd_options_.dash_profile); + break; + } + AddCommonMpdInfo(&mpd); - switch (type_) { - case kStatic: + switch (mpd_options_.mpd_type) { + case MpdType::kStatic: AddStaticMpdInfo(&mpd); break; - case kDynamic: + case MpdType::kDynamic: AddDynamicMpdInfo(&mpd); break; default: - NOTREACHED() << "Unknown MPD type: " << type_; + NOTREACHED() << "Unknown MPD type: " + << static_cast(mpd_options_.mpd_type); break; } @@ -532,13 +550,10 @@ void MpdBuilder::AddCommonMpdInfo(XmlNode* mpd_node) { void MpdBuilder::AddStaticMpdInfo(XmlNode* mpd_node) { DCHECK(mpd_node); - DCHECK_EQ(MpdBuilder::kStatic, type_); + DCHECK_EQ(MpdType::kStatic, mpd_options_.mpd_type); static const char kStaticMpdType[] = "static"; - static const char kStaticMpdProfile[] = - "urn:mpeg:dash:profile:isoff-on-demand:2011"; mpd_node->SetStringAttribute("type", kStaticMpdType); - mpd_node->SetStringAttribute("profiles", kStaticMpdProfile); mpd_node->SetStringAttribute( "mediaPresentationDuration", SecondsToXmlDuration(GetStaticMpdDuration(mpd_node))); @@ -546,13 +561,10 @@ void MpdBuilder::AddStaticMpdInfo(XmlNode* mpd_node) { void MpdBuilder::AddDynamicMpdInfo(XmlNode* mpd_node) { DCHECK(mpd_node); - DCHECK_EQ(MpdBuilder::kDynamic, type_); + DCHECK_EQ(MpdType::kDynamic, mpd_options_.mpd_type); static const char kDynamicMpdType[] = "dynamic"; - static const char kDynamicMpdProfile[] = - "urn:mpeg:dash:profile:isoff-live:2011"; mpd_node->SetStringAttribute("type", kDynamicMpdType); - mpd_node->SetStringAttribute("profiles", kDynamicMpdProfile); // No offset from NOW. mpd_node->SetStringAttribute("publishTime", @@ -594,12 +606,13 @@ void MpdBuilder::AddDynamicMpdInfo(XmlNode* mpd_node) { float MpdBuilder::GetStaticMpdDuration(XmlNode* mpd_node) { DCHECK(mpd_node); - DCHECK_EQ(MpdBuilder::kStatic, type_); + DCHECK_EQ(MpdType::kStatic, mpd_options_.mpd_type); xmlNodePtr period_node = FindPeriodNode(mpd_node); DCHECK(period_node) << "Period element must be a child of mpd_node."; DCHECK(IsPeriodNode(period_node)); + // TODO(kqyang): Verify if this works for static + live profile. // Attribute mediaPresentationDuration must be present for 'static' MPD. So // setting "PT0S" is required even if none of the representaions have duration // attribute. @@ -673,13 +686,11 @@ void MpdBuilder::MakePathsRelativeToMpd(const std::string& mpd_path, AdaptationSet::AdaptationSet(uint32_t adaptation_set_id, const std::string& lang, const MpdOptions& mpd_options, - MpdBuilder::MpdType mpd_type, base::AtomicSequenceNumber* counter) : representation_counter_(counter), id_(adaptation_set_id), lang_(lang), mpd_options_(mpd_options), - mpd_type_(mpd_type), segments_aligned_(kSegmentAlignmentUnknown), force_set_segment_alignment_(false) { DCHECK(counter); @@ -791,15 +802,16 @@ xml::scoped_xml_ptr AdaptationSet::GetXml() { // Note: must be checked before checking segments_aligned_ (below). So that // segments_aligned_ is set before checking below. - if (mpd_type_ == MpdBuilder::kStatic) { + if (mpd_options_.dash_profile == DashProfile::kOnDemand) { CheckVodSegmentAlignment(); } if (segments_aligned_ == kSegmentAlignmentTrue) { - adaptation_set.SetStringAttribute(mpd_type_ == MpdBuilder::kStatic - ? "subsegmentAlignment" - : "segmentAlignment", - "true"); + adaptation_set.SetStringAttribute( + mpd_options_.dash_profile == DashProfile::kOnDemand + ? "subsegmentAlignment" + : "segmentAlignment", + "true"); } if (picture_aspect_ratio_.size() == 1) @@ -860,7 +872,7 @@ void AdaptationSet::AddAdaptationSetSwitching(uint32_t adaptation_set_id) { void AdaptationSet::OnNewSegmentForRepresentation(uint32_t representation_id, uint64_t start_time, uint64_t duration) { - if (mpd_type_ == MpdBuilder::kDynamic) { + if (mpd_options_.dash_profile == DashProfile::kLive) { CheckLiveSegmentAlignment(representation_id, start_time, duration); } else { representation_segment_start_times_[representation_id].push_back( diff --git a/packager/mpd/base/mpd_builder.h b/packager/mpd/base/mpd_builder.h index 185bf0323f..965f1a1924 100644 --- a/packager/mpd/base/mpd_builder.h +++ b/packager/mpd/base/mpd_builder.h @@ -56,15 +56,9 @@ class RepresentationXmlNode; /// This class generates DASH MPDs (Media Presentation Descriptions). class MpdBuilder { public: - enum MpdType { - kStatic = 0, - kDynamic - }; - /// Constructs MpdBuilder. - /// @param type indicates whether the MPD should be for VOD or live content - /// (kStatic for VOD profile, or kDynamic for live profile). - MpdBuilder(MpdType type, const MpdOptions& mpd_options); + /// @param mpd_options contains options on how this MPD should be built. + explicit MpdBuilder(const MpdOptions& mpd_options); virtual ~MpdBuilder(); /// Add entry to the MPD. @@ -88,9 +82,6 @@ class MpdBuilder { /// @return true on success, false otherwise. virtual bool ToString(std::string* output); - /// @return The mpd type. - MpdType type() const { return type_; } - /// Adjusts the fields of MediaInfo so that paths are relative to the /// specified MPD path. /// @param mpd_path is the file path of the MPD file. @@ -106,9 +97,11 @@ class MpdBuilder { } private: - // DynamicMpdBuilderTest needs to set availabilityStartTime so that the test + // LiveMpdBuilderTest needs to set availabilityStartTime so that the test // doesn't need to depend on current time. - friend class DynamicMpdBuilderTest; + friend class LiveMpdBuilderTest; + template + friend class MpdBuilderTest; bool ToStringImpl(std::string* output); @@ -143,7 +136,6 @@ class MpdBuilder { // successful, false otherwise. bool GetEarliestTimestamp(double* timestamp_seconds); - MpdType type_; MpdOptions mpd_options_; std::list> adaptation_sets_; @@ -286,12 +278,11 @@ class AdaptationSet { AdaptationSet(uint32_t adaptation_set_id, const std::string& lang, const MpdOptions& mpd_options, - MpdBuilder::MpdType mpd_type, base::AtomicSequenceNumber* representation_counter); private: friend class MpdBuilder; - template + template friend class MpdBuilderTest; // kSegmentAlignmentUnknown means that it is uncertain if the @@ -345,7 +336,6 @@ class AdaptationSet { const uint32_t id_; const std::string lang_; const MpdOptions& mpd_options_; - const MpdBuilder::MpdType mpd_type_; // The ids of the adaptation sets this adaptation set can switch to. std::vector adaptation_set_switching_ids_; @@ -513,7 +503,7 @@ class Representation { private: friend class AdaptationSet; - template + template friend class MpdBuilderTest; bool AddLiveInfo(xml::RepresentationXmlNode* representation); diff --git a/packager/mpd/base/mpd_builder_unittest.cc b/packager/mpd/base/mpd_builder_unittest.cc index f27e8b9c9d..c6c18d7be5 100644 --- a/packager/mpd/base/mpd_builder_unittest.cc +++ b/packager/mpd/base/mpd_builder_unittest.cc @@ -104,12 +104,16 @@ class MockRepresentationStateChangeListener }; } // namespace -template +template class MpdBuilderTest : public ::testing::Test { public: - MpdBuilderTest() : mpd_(type, MpdOptions()), representation_() {} + MpdBuilderTest() : mpd_(MpdOptions()), representation_() { + mpd_.mpd_options_.dash_profile = profile; + } ~MpdBuilderTest() override {} + MpdOptions* mutable_mpd_options() { return &mpd_.mpd_options_; } + void CheckMpd(const std::string& expected_output_file) { std::string mpd_doc; ASSERT_TRUE(mpd_.ToString(&mpd_doc)); @@ -138,24 +142,20 @@ class MpdBuilderTest : public ::testing::Test { // constructor signatures. std::unique_ptr CreateRepresentation( const MediaInfo& media_info, - const MpdOptions& mpd_options, uint32_t representation_id, std::unique_ptr state_change_listener) { return std::unique_ptr( - new Representation(media_info, mpd_options, representation_id, + new Representation(media_info, mpd_options_, representation_id, std::move(state_change_listener))); } std::unique_ptr CreateAdaptationSet( uint32_t adaptation_set_id, const std::string& lang, - const MpdOptions& mpd_options, - MpdBuilder::MpdType mpd_type, base::AtomicSequenceNumber* representation_counter) { - return std::unique_ptr( - new AdaptationSet(adaptation_set_id, lang, mpd_options, mpd_type, - representation_counter)); + return std::unique_ptr(new AdaptationSet( + adaptation_set_id, lang, mpd_options_, representation_counter)); } // Helper function to return an empty listener for tests that don't need @@ -170,29 +170,30 @@ class MpdBuilderTest : public ::testing::Test { Representation* representation_; // Owned by |mpd_|. private: + MpdOptions mpd_options_; + DISALLOW_COPY_AND_ASSIGN(MpdBuilderTest); }; -class StaticMpdBuilderTest : public MpdBuilderTest {}; +class OnDemandMpdBuilderTest : public MpdBuilderTest {}; // Use this test name for things that are common to both static an dynamic // mpd builder tests. -typedef StaticMpdBuilderTest CommonMpdBuilderTest; +typedef OnDemandMpdBuilderTest CommonMpdBuilderTest; -class DynamicMpdBuilderTest : public MpdBuilderTest { +class LiveMpdBuilderTest : public MpdBuilderTest { public: - ~DynamicMpdBuilderTest() override {} + ~LiveMpdBuilderTest() override {} // Anchors availabilityStartTime so that the test result doesn't depend on the // current time. void SetUp() override { SetPackagerVersionForTesting("--"); + mpd_.mpd_options_.mpd_type = MpdType::kDynamic; mpd_.availability_start_time_ = "2011-12-25T12:30:00"; InjectTestClock(); } - MpdOptions* mutable_mpd_options() { return &mpd_.mpd_options_; } - // Injects a clock that always returns 2016 Jan 11 15:10:24 in UTC. void InjectTestClock() { base::Time::Exploded test_time = { 2016, // year. @@ -231,14 +232,14 @@ class DynamicMpdBuilderTest : public MpdBuilderTest { uint32_t DefaultTimeScale() const { return 1000; }; }; -class SegmentTemplateTest : public DynamicMpdBuilderTest { +class SegmentTemplateTest : public LiveMpdBuilderTest { public: SegmentTemplateTest() : bandwidth_estimator_(BandwidthEstimator::kUseAllBlocks) {} ~SegmentTemplateTest() override {} void SetUp() override { - DynamicMpdBuilderTest::SetUp(); + LiveMpdBuilderTest::SetUp(); ASSERT_NO_FATAL_FAILURE(AddRepresentationWithDefaultMediaInfo()); } @@ -282,8 +283,9 @@ class SegmentTemplateTest : public DynamicMpdBuilderTest { " 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\" " - " availabilityStartTime=\"2011-12-25T12:30:00\" minBufferTime=\"PT2S\" " - " type=\"dynamic\" profiles=\"urn:mpeg:dash:profile:isoff-live:2011\" " + " availabilityStartTime=\"2011-12-25T12:30:00\" " + " profiles=\"urn:mpeg:dash:profile:isoff-live:2011\" " + " minBufferTime=\"PT2S\" type=\"dynamic\" " " publishTime=\"2016-01-11T15:10:24Z\">\n" " \n" " \n" " \n" @@ -405,7 +408,7 @@ class TimeShiftBufferDepthTest : public SegmentTemplateTest { }; TEST_F(CommonMpdBuilderTest, AddAdaptationSetSwitching) { - MpdBuilder mpd_builder(MpdBuilder::kStatic, MpdOptions()); + MpdBuilder mpd_builder(MpdOptions{}); AdaptationSet* adaptation_set = mpd_builder.AddAdaptationSet(""); adaptation_set->AddAdaptationSetSwitching(1); adaptation_set->AddAdaptationSetSwitching(2); @@ -420,8 +423,8 @@ TEST_F(CommonMpdBuilderTest, AddAdaptationSetSwitching) { " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" " xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n" " xsi:schemaLocation=\"urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd\"\n" - " minBufferTime=\"PT2S\" type=\"static\"\n" " profiles=\"urn:mpeg:dash:profile:isoff-on-demand:2011\"\n" + " minBufferTime=\"PT2S\" type=\"static\"\n" " mediaPresentationDuration=\"PT0S\">\n" " \n" " \n" @@ -454,9 +457,8 @@ TEST_F(CommonMpdBuilderTest, ValidMediaInfo) { "}\n" "container_type: 1\n"; - auto representation = - CreateRepresentation(ConvertToMediaInfo(kTestMediaInfo), MpdOptions(), - kAnyRepresentationId, NoListener()); + auto representation = CreateRepresentation( + ConvertToMediaInfo(kTestMediaInfo), kAnyRepresentationId, NoListener()); EXPECT_TRUE(representation->Init()); } @@ -464,9 +466,8 @@ TEST_F(CommonMpdBuilderTest, ValidMediaInfo) { TEST_F(CommonMpdBuilderTest, VideoAudioTextInfoNotSet) { const char kTestMediaInfo[] = "container_type: 1"; - auto representation = - CreateRepresentation(ConvertToMediaInfo(kTestMediaInfo), MpdOptions(), - kAnyRepresentationId, NoListener()); + auto representation = CreateRepresentation( + ConvertToMediaInfo(kTestMediaInfo), kAnyRepresentationId, NoListener()); EXPECT_FALSE(representation->Init()); } @@ -490,9 +491,8 @@ TEST_F(CommonMpdBuilderTest, VideoAndAudioInfoSet) { "}\n" "container_type: CONTAINER_MP4\n"; - auto representation = - CreateRepresentation(ConvertToMediaInfo(kTestMediaInfo), MpdOptions(), - kAnyRepresentationId, NoListener()); + auto representation = CreateRepresentation( + ConvertToMediaInfo(kTestMediaInfo), kAnyRepresentationId, NoListener()); EXPECT_FALSE(representation->Init()); } @@ -509,9 +509,8 @@ TEST_F(CommonMpdBuilderTest, InvalidMediaInfo) { " pixel_height: 1\n" "}\n" "container_type: 1\n"; - auto representation = - CreateRepresentation(ConvertToMediaInfo(kTestMediaInfo), MpdOptions(), - kAnyRepresentationId, NoListener()); + auto representation = CreateRepresentation( + ConvertToMediaInfo(kTestMediaInfo), kAnyRepresentationId, NoListener()); EXPECT_FALSE(representation->Init()); } @@ -528,9 +527,8 @@ TEST_F(CommonMpdBuilderTest, CheckVideoInfoReflectedInXml) { " pixel_height: 1\n" "}\n" "container_type: 1\n"; - auto representation = - CreateRepresentation(ConvertToMediaInfo(kTestMediaInfo), MpdOptions(), - kAnyRepresentationId, NoListener()); + auto representation = CreateRepresentation( + ConvertToMediaInfo(kTestMediaInfo), kAnyRepresentationId, NoListener()); EXPECT_TRUE(representation->Init()); xml::scoped_xml_ptr node_xml(representation->GetXml()); EXPECT_NO_FATAL_FAILURE( @@ -559,7 +557,7 @@ TEST_F(CommonMpdBuilderTest, CheckVideoInfoVp8CodecInMp4) { "container_type: 1\n"; auto representation = CreateRepresentation(ConvertToMediaInfo(kTestMediaInfoCodecVp8), - MpdOptions(), kAnyRepresentationId, NoListener()); + kAnyRepresentationId, NoListener()); EXPECT_TRUE(representation->Init()); xml::scoped_xml_ptr node_xml(representation->GetXml()); EXPECT_NO_FATAL_FAILURE( @@ -582,7 +580,7 @@ TEST_F(CommonMpdBuilderTest, CheckVideoInfoVp8CodecInWebm) { "container_type: 3\n"; auto representation = CreateRepresentation(ConvertToMediaInfo(kTestMediaInfoCodecVp8), - MpdOptions(), kAnyRepresentationId, NoListener()); + kAnyRepresentationId, NoListener()); EXPECT_TRUE(representation->Init()); xml::scoped_xml_ptr node_xml(representation->GetXml()); EXPECT_NO_FATAL_FAILURE( @@ -605,7 +603,7 @@ TEST_F(CommonMpdBuilderTest, CheckVideoInfoVp9CodecInWebm) { "container_type: 3\n"; auto representation = CreateRepresentation(ConvertToMediaInfo(kTestMediaInfoCodecVp9), - MpdOptions(), kAnyRepresentationId, NoListener()); + kAnyRepresentationId, NoListener()); EXPECT_TRUE(representation->Init()); xml::scoped_xml_ptr node_xml(representation->GetXml()); EXPECT_NO_FATAL_FAILURE( @@ -635,7 +633,7 @@ TEST_F(CommonMpdBuilderTest, EXPECT_CALL(*listener, OnNewSegmentForRepresentation(kStartTime, kDuration)); auto representation = - CreateRepresentation(ConvertToMediaInfo(kTestMediaInfo), MpdOptions(), + CreateRepresentation(ConvertToMediaInfo(kTestMediaInfo), kAnyRepresentationId, std::move(listener)); EXPECT_TRUE(representation->Init()); @@ -666,7 +664,7 @@ TEST_F(CommonMpdBuilderTest, EXPECT_CALL(*listener, OnSetFrameRateForRepresentation(kFrameDuration, kTimeScale)); auto representation = - CreateRepresentation(ConvertToMediaInfo(kTestMediaInfo), MpdOptions(), + CreateRepresentation(ConvertToMediaInfo(kTestMediaInfo), kAnyRepresentationId, std::move(listener)); EXPECT_TRUE(representation->Init()); @@ -690,8 +688,7 @@ TEST_F(CommonMpdBuilderTest, CheckAdaptationSetVideoContentType) { "container_type: CONTAINER_MP4\n"; auto adaptation_set = - CreateAdaptationSet(kAnyAdaptationSetId, "", MpdOptions(), - MpdBuilder::kStatic, &sequence_counter); + CreateAdaptationSet(kAnyAdaptationSetId, "", &sequence_counter); adaptation_set->AddRepresentation(ConvertToMediaInfo(kVideoMediaInfo)); EXPECT_NO_FATAL_FAILURE(ExpectAttributeEqString( @@ -712,8 +709,7 @@ TEST_F(CommonMpdBuilderTest, CheckAdaptationSetAudioContentType) { "container_type: CONTAINER_MP4\n"; auto adaptation_set = - CreateAdaptationSet(kAnyAdaptationSetId, "", MpdOptions(), - MpdBuilder::kStatic, &sequence_counter); + CreateAdaptationSet(kAnyAdaptationSetId, "", &sequence_counter); adaptation_set->AddRepresentation(ConvertToMediaInfo(kAudioMediaInfo)); EXPECT_NO_FATAL_FAILURE(ExpectAttributeEqString( @@ -732,8 +728,7 @@ TEST_F(CommonMpdBuilderTest, CheckAdaptationSetTextContentType) { "container_type: CONTAINER_TEXT\n"; auto adaptation_set = - CreateAdaptationSet(kAnyAdaptationSetId, "", MpdOptions(), - MpdBuilder::kStatic, &sequence_counter); + CreateAdaptationSet(kAnyAdaptationSetId, "", &sequence_counter); adaptation_set->AddRepresentation(ConvertToMediaInfo(kTextMediaInfo)); EXPECT_NO_FATAL_FAILURE(ExpectAttributeEqString( @@ -748,7 +743,7 @@ TEST_F(CommonMpdBuilderTest, TtmlXmlMimeType) { "container_type: CONTAINER_TEXT\n"; auto representation = - CreateRepresentation(ConvertToMediaInfo(kTtmlXmlMediaInfo), MpdOptions(), + CreateRepresentation(ConvertToMediaInfo(kTtmlXmlMediaInfo), kAnyRepresentationId, NoListener()); EXPECT_TRUE(representation->Init()); EXPECT_NO_FATAL_FAILURE(ExpectAttributeEqString( @@ -763,7 +758,7 @@ TEST_F(CommonMpdBuilderTest, TtmlMp4MimeType) { "container_type: CONTAINER_MP4\n"; auto representation = - CreateRepresentation(ConvertToMediaInfo(kTtmlMp4MediaInfo), MpdOptions(), + CreateRepresentation(ConvertToMediaInfo(kTtmlMp4MediaInfo), kAnyRepresentationId, NoListener()); EXPECT_TRUE(representation->Init()); EXPECT_NO_FATAL_FAILURE(ExpectAttributeEqString( @@ -777,9 +772,8 @@ TEST_F(CommonMpdBuilderTest, WebVttMimeType) { "}\n" "container_type: CONTAINER_TEXT\n"; - auto representation = - CreateRepresentation(ConvertToMediaInfo(kWebVttMediaInfo), MpdOptions(), - kAnyRepresentationId, NoListener()); + auto representation = CreateRepresentation( + ConvertToMediaInfo(kWebVttMediaInfo), kAnyRepresentationId, NoListener()); EXPECT_TRUE(representation->Init()); EXPECT_NO_FATAL_FAILURE(ExpectAttributeEqString( "mimeType", "text/vtt", representation->GetXml().get())); @@ -796,8 +790,7 @@ TEST_F(CommonMpdBuilderTest, CheckLanguageAttributeSet) { "container_type: CONTAINER_TEXT\n"; auto adaptation_set = - CreateAdaptationSet(kAnyAdaptationSetId, "en", MpdOptions(), - MpdBuilder::kStatic, &sequence_counter); + CreateAdaptationSet(kAnyAdaptationSetId, "en", &sequence_counter); adaptation_set->AddRepresentation(ConvertToMediaInfo(kTextMediaInfo)); xml::scoped_xml_ptr node_xml(adaptation_set->GetXml()); @@ -818,8 +811,7 @@ TEST_F(CommonMpdBuilderTest, 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", MpdOptions(), - MpdBuilder::kStatic, &sequence_counter); + CreateAdaptationSet(kAnyAdaptationSetId, "por-BR", &sequence_counter); adaptation_set->AddRepresentation(ConvertToMediaInfo(kTextMediaInfo)); xml::scoped_xml_ptr node_xml(adaptation_set->GetXml()); @@ -831,14 +823,13 @@ TEST_F(CommonMpdBuilderTest, CheckAdaptationSetId) { base::AtomicSequenceNumber sequence_counter; const uint32_t kAdaptationSetId = 42; auto adaptation_set = - CreateAdaptationSet(kAdaptationSetId, "", MpdOptions(), - MpdBuilder::kStatic, &sequence_counter); + CreateAdaptationSet(kAdaptationSetId, "", &sequence_counter); ASSERT_NO_FATAL_FAILURE(CheckIdEqual(kAdaptationSetId, adaptation_set.get())); } // Verify AdaptationSet::AddRole() works for "main" role. TEST_F(CommonMpdBuilderTest, AdaptationAddRoleElementMain) { - MpdBuilder mpd_builder(MpdBuilder::kStatic, MpdOptions()); + MpdBuilder mpd_builder(MpdOptions{}); AdaptationSet* adaptation_set = mpd_builder.AddAdaptationSet(""); adaptation_set->AddRole(AdaptationSet::kRoleMain); @@ -851,8 +842,8 @@ TEST_F(CommonMpdBuilderTest, AdaptationAddRoleElementMain) { " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" " xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n" " xsi:schemaLocation=\"urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd\"\n" - " minBufferTime=\"PT2S\" type=\"static\"\n" " profiles=\"urn:mpeg:dash:profile:isoff-on-demand:2011\"\n" + " minBufferTime=\"PT2S\" type=\"static\"\n" " mediaPresentationDuration=\"PT0S\">\n" " \n" " \n" @@ -871,7 +862,7 @@ TEST_F(CommonMpdBuilderTest, AdaptationAddRoleElementMain) { // Add Role, ContentProtection, and Representation elements. Verify that // ContentProtection -> Role -> Representation are in order. TEST_F(CommonMpdBuilderTest, CheckContentProtectionRoleRepresentationOrder) { - MpdBuilder mpd_builder(MpdBuilder::kStatic, MpdOptions()); + MpdBuilder mpd_builder(MpdOptions{}); AdaptationSet* adaptation_set = mpd_builder.AddAdaptationSet(""); adaptation_set->AddRole(AdaptationSet::kRoleMain); ContentProtectionElement any_content_protection; @@ -894,8 +885,8 @@ TEST_F(CommonMpdBuilderTest, CheckContentProtectionRoleRepresentationOrder) { " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" " xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n" " xsi:schemaLocation=\"urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd\"\n" - " minBufferTime=\"PT2S\" type=\"static\"\n" " profiles=\"urn:mpeg:dash:profile:isoff-on-demand:2011\"\n" + " minBufferTime=\"PT2S\" type=\"static\"\n" " mediaPresentationDuration=\"PT0S\">\n" " \n" " \n" @@ -1024,8 +1015,7 @@ TEST_F(CommonMpdBuilderTest, "container_type: 1\n"; auto adaptation_set = - CreateAdaptationSet(kAnyAdaptationSetId, "", MpdOptions(), - MpdBuilder::kStatic, &sequence_counter); + CreateAdaptationSet(kAnyAdaptationSetId, "", &sequence_counter); Representation* representation_480p = adaptation_set->AddRepresentation(ConvertToMediaInfo(k480pMediaInfo)); Representation* representation_360p = @@ -1250,9 +1240,8 @@ TEST_F(CommonMpdBuilderTest, SuppressRepresentationAttributes) { "}\n" "container_type: 1\n"; - auto representation = - CreateRepresentation(ConvertToMediaInfo(kTestMediaInfo), MpdOptions(), - kAnyRepresentationId, NoListener()); + auto representation = CreateRepresentation( + ConvertToMediaInfo(kTestMediaInfo), kAnyRepresentationId, NoListener()); representation->SuppressOnce(Representation::kSuppressWidth); xml::scoped_xml_ptr no_width(representation->GetXml()); @@ -1382,7 +1371,7 @@ TEST_F(CommonMpdBuilderTest, BubbleUpAttributesToAdaptationSet) { // Also checking that not all Representations have to be added before calling // AddNewSegment() on a Representation. // The output MPD's schema is checked at the very end. -TEST_F(StaticMpdBuilderTest, SubsegmentAlignment) { +TEST_F(OnDemandMpdBuilderTest, SubsegmentAlignment) { base::AtomicSequenceNumber sequence_counter; const char k480pMediaInfo[] = "video_info {\n" @@ -1448,7 +1437,7 @@ TEST_F(StaticMpdBuilderTest, SubsegmentAlignment) { } // Verify that subsegmentAlignment can be force set to true. -TEST_F(StaticMpdBuilderTest, ForceSetsubsegmentAlignment) { +TEST_F(OnDemandMpdBuilderTest, ForceSetsubsegmentAlignment) { base::AtomicSequenceNumber sequence_counter; const char k480pMediaInfo[] = "video_info {\n" @@ -1472,9 +1461,9 @@ TEST_F(StaticMpdBuilderTest, ForceSetsubsegmentAlignment) { " pixel_height: 1\n" "}\n" "container_type: 1\n"; + MpdOptions mpd_options; auto adaptation_set = - CreateAdaptationSet(kAnyAdaptationSetId, "", MpdOptions(), - MpdBuilder::kStatic, &sequence_counter); + CreateAdaptationSet(kAnyAdaptationSetId, "", &sequence_counter); Representation* representation_480p = adaptation_set->AddRepresentation(ConvertToMediaInfo(k480pMediaInfo)); Representation* representation_360p = @@ -1502,7 +1491,7 @@ TEST_F(StaticMpdBuilderTest, ForceSetsubsegmentAlignment) { // Verify that segmentAlignment is set to true if all the Representations // segments' are aligned and the MPD type is dynamic. // The output MPD's schema is checked at the very end. -TEST_F(DynamicMpdBuilderTest, SegmentAlignment) { +TEST_F(LiveMpdBuilderTest, SegmentAlignment) { base::AtomicSequenceNumber sequence_counter; const char k480pMediaInfo[] = "video_info {\n" @@ -1559,7 +1548,7 @@ TEST_F(DynamicMpdBuilderTest, SegmentAlignment) { // Verify that the width and height attribute are set if all the video // representations have the same width and height. -TEST_F(StaticMpdBuilderTest, AdapatationSetWidthAndHeight) { +TEST_F(OnDemandMpdBuilderTest, AdapatationSetWidthAndHeight) { // Both 720p. const char kVideoMediaInfo1[] = "video_info {\n" @@ -1600,7 +1589,7 @@ TEST_F(StaticMpdBuilderTest, AdapatationSetWidthAndHeight) { // Verify that the maxWidth and maxHeight attribute are set if there are // multiple video resolutions. -TEST_F(StaticMpdBuilderTest, AdapatationSetMaxWidthAndMaxHeight) { +TEST_F(OnDemandMpdBuilderTest, AdapatationSetMaxWidthAndMaxHeight) { const char kVideoMediaInfo1080p[] = "video_info {\n" " codec: \"avc1\"\n" @@ -1642,8 +1631,8 @@ TEST_F(CommonMpdBuilderTest, CheckRepresentationId) { const MediaInfo video_media_info = GetTestMediaInfo(kFileNameVideoMediaInfo1); const uint32_t kRepresentationId = 1; - auto representation = CreateRepresentation(video_media_info, MpdOptions(), - kRepresentationId, NoListener()); + auto representation = + CreateRepresentation(video_media_info, kRepresentationId, NoListener()); EXPECT_TRUE(representation->Init()); ASSERT_NO_FATAL_FAILURE( CheckIdEqual(kRepresentationId, representation.get())); @@ -1664,8 +1653,7 @@ TEST_F(CommonMpdBuilderTest, SetSampleDuration) { base::AtomicSequenceNumber sequence_counter; auto adaptation_set = - CreateAdaptationSet(kAnyAdaptationSetId, "", MpdOptions(), - MpdBuilder::kStatic, &sequence_counter); + CreateAdaptationSet(kAnyAdaptationSetId, "", &sequence_counter); const MediaInfo video_media_info = ConvertToMediaInfo(kVideoMediaInfo); Representation* representation = @@ -1716,8 +1704,8 @@ TEST_F(CommonMpdBuilderTest, AdaptationSetAddContentProtectionAndUpdate) { " 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\"" + " minBufferTime=\"PT2S\" type=\"static\"" " mediaPresentationDuration=\"PT0S\">" " " " " " " " " " " " " " " " " " " " " @@ -1972,7 +1960,7 @@ TEST_F(StaticMpdBuilderTest, AudioChannelConfigurationWithContentProtection) { // Static profile requires bandwidth to be set because it has no other way to // get the bandwidth for the Representation. -TEST_F(StaticMpdBuilderTest, MediaInfoMissingBandwidth) { +TEST_F(OnDemandMpdBuilderTest, MediaInfoMissingBandwidth) { MediaInfo video_media_info = GetTestMediaInfo(kFileNameVideoMediaInfo1); video_media_info.clear_bandwidth(); AddRepresentation(video_media_info); @@ -1981,7 +1969,7 @@ TEST_F(StaticMpdBuilderTest, MediaInfoMissingBandwidth) { ASSERT_FALSE(mpd_.ToString(&mpd_doc)); } -TEST_F(StaticMpdBuilderTest, WriteToFile) { +TEST_F(OnDemandMpdBuilderTest, WriteToFile) { MediaInfo video_media_info = GetTestMediaInfo(kFileNameVideoMediaInfo1); AdaptationSet* video_adaptation_set = mpd_.AddAdaptationSet(""); ASSERT_TRUE(video_adaptation_set); @@ -2007,7 +1995,7 @@ TEST_F(StaticMpdBuilderTest, WriteToFile) { } // Verify that a text path works. -TEST_F(StaticMpdBuilderTest, Text) { +TEST_F(OnDemandMpdBuilderTest, Text) { const char kTextMediaInfo[] = "text_info {\n" " format: 'ttml'\n" @@ -2026,8 +2014,8 @@ TEST_F(StaticMpdBuilderTest, Text) { " 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\"" + " minBufferTime=\"PT2S\" type=\"static\"" " mediaPresentationDuration=\"PT35S\">" " " " " @@ -2057,7 +2045,7 @@ TEST_F(StaticMpdBuilderTest, Text) { // Check whether the attributes are set correctly for dynamic element. // This test must use ASSERT_EQ for comparison because XmlEqual() cannot // handle namespaces correctly yet. -TEST_F(DynamicMpdBuilderTest, CheckMpdAttributes) { +TEST_F(LiveMpdBuilderTest, DynamicCheckMpdAttributes) { static const char kExpectedOutput[] = "\n" "\n" + "\n" + " \n" + "\n"; + + std::string mpd_doc; + mutable_mpd_options()->mpd_type = MpdType::kStatic; ASSERT_TRUE(mpd_.ToString(&mpd_doc)); ASSERT_EQ(kExpectedOutput, mpd_doc); } diff --git a/packager/mpd/base/mpd_notifier.h b/packager/mpd/base/mpd_notifier.h index 46c27a113f..a9328426bd 100644 --- a/packager/mpd/base/mpd_notifier.h +++ b/packager/mpd/base/mpd_notifier.h @@ -15,24 +15,20 @@ #include #include "packager/base/macros.h" +#include "packager/mpd/base/mpd_options.h" namespace shaka { class MediaInfo; struct ContentProtectionElement; -enum DashProfile { - kUnknownProfile, - kOnDemandProfile, - kLiveProfile, -}; - /// Interface for publish/subscribe publisher class which notifies MpdBuilder /// of media-related events. class MpdNotifier { public: - MpdNotifier(DashProfile dash_profile) : dash_profile_(dash_profile) {}; - virtual ~MpdNotifier() {}; + explicit MpdNotifier(const MpdOptions& mpd_options) + : mpd_options_(mpd_options) {} + virtual ~MpdNotifier() {} /// Initializes the notifier. For example, if this notifier uses a network for /// notification, then this would set up the connection with the remote host. @@ -105,10 +101,13 @@ class MpdNotifier { virtual bool Flush() = 0; /// @return The dash profile for this object. - DashProfile dash_profile() const { return dash_profile_; } + DashProfile dash_profile() const { return mpd_options_.dash_profile; } + + /// @return The mpd type for this object. + MpdType mpd_type() const { return mpd_options_.mpd_type; } private: - const DashProfile dash_profile_; + const MpdOptions mpd_options_; DISALLOW_COPY_AND_ASSIGN(MpdNotifier); }; diff --git a/packager/mpd/base/mpd_options.h b/packager/mpd/base/mpd_options.h index 03bf117961..5fdfee2899 100644 --- a/packager/mpd/base/mpd_options.h +++ b/packager/mpd/base/mpd_options.h @@ -11,27 +11,27 @@ namespace shaka { +enum class DashProfile { + kUnknown, + kOnDemand, + kLive, +}; + +enum class MpdType { kStatic, kDynamic }; + /// Defines Mpd Options. struct MpdOptions { - MpdOptions() - : availability_time_offset(0), - minimum_update_period(0), - // TODO(tinskip): Set min_buffer_time in unit tests rather than here. - min_buffer_time(2.0), - time_shift_buffer_depth(0), - suggested_presentation_delay(0) {} - - ~MpdOptions() {}; - - double availability_time_offset; - double minimum_update_period; - double min_buffer_time; - double time_shift_buffer_depth; - double suggested_presentation_delay; + DashProfile dash_profile = DashProfile::kOnDemand; + MpdType mpd_type = MpdType::kStatic; + double availability_time_offset = 0; + double minimum_update_period = 0; + // TODO(tinskip): Set min_buffer_time in unit tests rather than here. + double min_buffer_time = 2.0; + double time_shift_buffer_depth = 0; + double suggested_presentation_delay = 0; std::string default_language; }; } // namespace shaka #endif // MPD_BASE_MPD_OPTIONS_H_ - diff --git a/packager/mpd/base/simple_mpd_notifier.cc b/packager/mpd/base/simple_mpd_notifier.cc index 90b620c5a2..8b148c4d00 100644 --- a/packager/mpd/base/simple_mpd_notifier.cc +++ b/packager/mpd/base/simple_mpd_notifier.cc @@ -14,17 +14,12 @@ namespace shaka { -SimpleMpdNotifier::SimpleMpdNotifier(DashProfile dash_profile, - const MpdOptions& mpd_options, +SimpleMpdNotifier::SimpleMpdNotifier(const MpdOptions& mpd_options, const std::vector& base_urls, const std::string& output_path) - : MpdNotifier(dash_profile), + : MpdNotifier(mpd_options), output_path_(output_path), - mpd_builder_(new MpdBuilder(dash_profile == kLiveProfile - ? MpdBuilder::kDynamic - : MpdBuilder::kStatic, - mpd_options)) { - DCHECK(dash_profile == kLiveProfile || dash_profile == kOnDemandProfile); + mpd_builder_(new MpdBuilder(mpd_options)) { for (size_t i = 0; i < base_urls.size(); ++i) mpd_builder_->AddBaseUrl(base_urls[i]); } diff --git a/packager/mpd/base/simple_mpd_notifier.h b/packager/mpd/base/simple_mpd_notifier.h index 32bbcc680f..5871be239c 100644 --- a/packager/mpd/base/simple_mpd_notifier.h +++ b/packager/mpd/base/simple_mpd_notifier.h @@ -30,8 +30,7 @@ struct MpdOptions; /// generates an Mpd file. class SimpleMpdNotifier : public MpdNotifier { public: - SimpleMpdNotifier(DashProfile dash_profile, - const MpdOptions& mpd_options, + SimpleMpdNotifier(const MpdOptions& mpd_options, const std::vector& base_urls, const std::string& output_path); ~SimpleMpdNotifier() override; diff --git a/packager/mpd/base/simple_mpd_notifier_unittest.cc b/packager/mpd/base/simple_mpd_notifier_unittest.cc index 0847537cf6..471c774bd2 100644 --- a/packager/mpd/base/simple_mpd_notifier_unittest.cc +++ b/packager/mpd/base/simple_mpd_notifier_unittest.cc @@ -35,8 +35,7 @@ const char kValidMediaInfo[] = const uint32_t kDefaultAdaptationSetId = 0u; } // namespace -class SimpleMpdNotifierTest - : public ::testing::TestWithParam { +class SimpleMpdNotifierTest : public ::testing::Test { protected: SimpleMpdNotifierTest() : default_mock_adaptation_set_( @@ -51,20 +50,6 @@ class SimpleMpdNotifierTest base::DeleteFile(temp_file_path_, false /* non recursive, just 1 file */); } - std::unique_ptr StaticMpdBuilderMock() { - return std::unique_ptr( - new MockMpdBuilder(MpdBuilder::kStatic)); - } - - std::unique_ptr DynamicMpdBuilderMock() { - return std::unique_ptr( - new MockMpdBuilder(MpdBuilder::kDynamic)); - } - - MpdBuilder::MpdType GetMpdBuilderType(const SimpleMpdNotifier& notifier) { - return notifier.MpdBuilderForTesting()->type(); - } - void SetMpdBuilder(SimpleMpdNotifier* notifier, std::unique_ptr mpd_builder) { notifier->SetMpdBuilderForTesting(std::move(mpd_builder)); @@ -83,29 +68,12 @@ class SimpleMpdNotifierTest base::FilePath temp_file_path_; }; -// Verify that it creates the correct MpdBuilder type using DashProfile passed -// to the constructor. -TEST_F(SimpleMpdNotifierTest, CreateCorrectMpdBuilderType) { - SimpleMpdNotifier on_demand_notifier(kOnDemandProfile, empty_mpd_option_, - empty_base_urls_, output_path_); - EXPECT_TRUE(on_demand_notifier.Init()); - EXPECT_EQ(MpdBuilder::kStatic, - GetMpdBuilderType(on_demand_notifier)); - SimpleMpdNotifier live_notifier(kLiveProfile, empty_mpd_option_, - empty_base_urls_, output_path_); - EXPECT_TRUE(live_notifier.Init()); - EXPECT_EQ(MpdBuilder::kDynamic, GetMpdBuilderType(live_notifier)); -} - // Verify NotifyNewContainer() works as expected for VOD. -TEST_P(SimpleMpdNotifierTest, NotifyNewContainer) { - SimpleMpdNotifier notifier(kOnDemandProfile, empty_mpd_option_, - empty_base_urls_, output_path_); +TEST_F(SimpleMpdNotifierTest, NotifyNewContainer) { + SimpleMpdNotifier notifier(empty_mpd_option_, empty_base_urls_, output_path_); const uint32_t kRepresentationId = 1u; - const MpdBuilder::MpdType mpd_type = GetParam(); - std::unique_ptr mock_mpd_builder( - new MockMpdBuilder(mpd_type)); + std::unique_ptr mock_mpd_builder(new MockMpdBuilder()); std::unique_ptr mock_representation( new MockRepresentation(kRepresentationId)); @@ -125,44 +93,11 @@ TEST_P(SimpleMpdNotifierTest, NotifyNewContainer) { EXPECT_TRUE(notifier.Flush()); } -// Verify NotifySampleDuration() works as expected for Live. -TEST_F(SimpleMpdNotifierTest, LiveNotifySampleDuration) { - SimpleMpdNotifier notifier(kLiveProfile, empty_mpd_option_, empty_base_urls_, - output_path_); +TEST_F(SimpleMpdNotifierTest, NotifySampleDuration) { + SimpleMpdNotifier notifier(empty_mpd_option_, empty_base_urls_, output_path_); const uint32_t kRepresentationId = 8u; - std::unique_ptr mock_mpd_builder(DynamicMpdBuilderMock()); - std::unique_ptr mock_representation( - new MockRepresentation(kRepresentationId)); - - EXPECT_CALL(*mock_mpd_builder, AddAdaptationSet(_)) - .WillOnce(Return(default_mock_adaptation_set_.get())); - EXPECT_CALL(*default_mock_adaptation_set_, AddRepresentation(_)) - .WillOnce(Return(mock_representation.get())); - - uint32_t container_id; - SetMpdBuilder(¬ifier, std::move(mock_mpd_builder)); - EXPECT_TRUE(notifier.NotifyNewContainer(ConvertToMediaInfo(kValidMediaInfo), - &container_id)); - EXPECT_EQ(kRepresentationId, container_id); - - const uint32_t kSampleDuration = 100; - EXPECT_CALL(*mock_representation, SetSampleDuration(kSampleDuration)); - EXPECT_TRUE( - notifier.NotifySampleDuration(kRepresentationId, kSampleDuration)); -} - -// Verify that NotifySampleDuration works for OnDemand profile. -// TODO(rkuroiwa): SimpleMpdNotifier returns a container ID but does not -// register it to its map for VOD. Must fix and enable this test. -// This test can be also parmeterized just like NotifyNewContainer() test, once -// it is fixed. -TEST_F(SimpleMpdNotifierTest, OnDemandNotifySampleDuration) { - SimpleMpdNotifier notifier(kOnDemandProfile, empty_mpd_option_, - empty_base_urls_, output_path_); - - const uint32_t kRepresentationId = 14u; - std::unique_ptr mock_mpd_builder(StaticMpdBuilderMock()); + std::unique_ptr mock_mpd_builder(new MockMpdBuilder()); std::unique_ptr mock_representation( new MockRepresentation(kRepresentationId)); @@ -189,8 +124,7 @@ TEST_F(SimpleMpdNotifierTest, OnDemandNotifySampleDuration) { // This issue identified a bug where using SimpleMpdNotifier with multiple // threads causes a deadlock. TEST_F(SimpleMpdNotifierTest, NotifyNewContainerAndSampleDurationNoMock) { - SimpleMpdNotifier notifier(kOnDemandProfile, empty_mpd_option_, - empty_base_urls_, output_path_); + SimpleMpdNotifier notifier(empty_mpd_option_, empty_base_urls_, output_path_); uint32_t container_id; EXPECT_TRUE(notifier.NotifyNewContainer(ConvertToMediaInfo(kValidMediaInfo), &container_id)); @@ -199,13 +133,11 @@ TEST_F(SimpleMpdNotifierTest, NotifyNewContainerAndSampleDurationNoMock) { EXPECT_TRUE(notifier.Flush()); } -// Verify that NotifyNewSegment() for live works. -TEST_F(SimpleMpdNotifierTest, LiveNotifyNewSegment) { - SimpleMpdNotifier notifier(kLiveProfile, empty_mpd_option_, empty_base_urls_, - output_path_); +TEST_F(SimpleMpdNotifierTest, NotifyNewSegment) { + SimpleMpdNotifier notifier(empty_mpd_option_, empty_base_urls_, output_path_); const uint32_t kRepresentationId = 447834u; - std::unique_ptr mock_mpd_builder(DynamicMpdBuilderMock()); + std::unique_ptr mock_mpd_builder(new MockMpdBuilder()); std::unique_ptr mock_representation( new MockRepresentation(kRepresentationId)); @@ -230,13 +162,11 @@ TEST_F(SimpleMpdNotifierTest, LiveNotifyNewSegment) { kSegmentDuration, kSegmentSize)); } -// Verify AddContentProtectionElement() works. Profile doesn't matter. TEST_F(SimpleMpdNotifierTest, AddContentProtectionElement) { - SimpleMpdNotifier notifier(kOnDemandProfile, empty_mpd_option_, - empty_base_urls_, output_path_); + SimpleMpdNotifier notifier(empty_mpd_option_, empty_base_urls_, output_path_); const uint32_t kRepresentationId = 0u; - std::unique_ptr mock_mpd_builder(StaticMpdBuilderMock()); + std::unique_ptr mock_mpd_builder(new MockMpdBuilder()); std::unique_ptr mock_representation( new MockRepresentation(kRepresentationId)); @@ -256,7 +186,7 @@ TEST_F(SimpleMpdNotifierTest, AddContentProtectionElement) { EXPECT_TRUE(notifier.AddContentProtectionElement(kRepresentationId, element)); } -TEST_P(SimpleMpdNotifierTest, UpdateEncryption) { +TEST_F(SimpleMpdNotifierTest, UpdateEncryption) { const char kProtectedContent[] = "video_info {\n" " codec: 'avc1'\n" @@ -276,10 +206,9 @@ TEST_P(SimpleMpdNotifierTest, UpdateEncryption) { " default_key_id: '_default_key_id_'\n" "}\n" "container_type: 1\n"; - SimpleMpdNotifier notifier(kLiveProfile, empty_mpd_option_, empty_base_urls_, - output_path_); + SimpleMpdNotifier notifier(empty_mpd_option_, empty_base_urls_, output_path_); const uint32_t kRepresentationId = 447834u; - std::unique_ptr mock_mpd_builder(DynamicMpdBuilderMock()); + std::unique_ptr mock_mpd_builder(new MockMpdBuilder()); std::unique_ptr mock_representation( new MockRepresentation(kRepresentationId)); @@ -312,7 +241,7 @@ TEST_P(SimpleMpdNotifierTest, UpdateEncryption) { } // Don't put different audio languages or codecs in the same AdaptationSet. -TEST_P(SimpleMpdNotifierTest, SplitAdaptationSetsByLanguageAndCodec) { +TEST_F(SimpleMpdNotifierTest, SplitAdaptationSetsByLanguageAndCodec) { // MP4, English const char kAudioContent1[] = "audio_info {\n" @@ -365,9 +294,8 @@ TEST_P(SimpleMpdNotifierTest, SplitAdaptationSetsByLanguageAndCodec) { "container_type: CONTAINER_WEBM\n" "media_duration_seconds: 10.5\n"; - SimpleMpdNotifier notifier(kOnDemandProfile, empty_mpd_option_, - empty_base_urls_, output_path_); - std::unique_ptr mock_mpd_builder(StaticMpdBuilderMock()); + SimpleMpdNotifier notifier(empty_mpd_option_, empty_base_urls_, output_path_); + std::unique_ptr mock_mpd_builder(new MockMpdBuilder()); std::unique_ptr adaptation_set1(new MockAdaptationSet(1)); std::unique_ptr adaptation_set2(new MockAdaptationSet(2)); @@ -410,9 +338,4 @@ TEST_P(SimpleMpdNotifierTest, SplitAdaptationSetsByLanguageAndCodec) { ConvertToMediaInfo(kAudioContent4), &unused_container_id)); } -INSTANTIATE_TEST_CASE_P(StaticAndDynamic, - SimpleMpdNotifierTest, - ::testing::Values(MpdBuilder::kStatic, - MpdBuilder::kDynamic)); - } // namespace shaka diff --git a/packager/mpd/util/mpd_writer.cc b/packager/mpd/util/mpd_writer.cc index 51596b4920..a04cb3f7b5 100644 --- a/packager/mpd/util/mpd_writer.cc +++ b/packager/mpd/util/mpd_writer.cc @@ -35,12 +35,11 @@ class DashIopMpdNotifierFactory : public MpdNotifierFactory { DashIopMpdNotifierFactory() {} ~DashIopMpdNotifierFactory() override {} - std::unique_ptr Create(DashProfile dash_profile, - const MpdOptions& mpd_options, + std::unique_ptr Create(const MpdOptions& mpd_options, const std::vector& base_urls, const std::string& output_path) override { - return std::unique_ptr(new DashIopMpdNotifier( - dash_profile, mpd_options, base_urls, output_path)); + return std::unique_ptr( + new DashIopMpdNotifier(mpd_options, base_urls, output_path)); } }; @@ -50,12 +49,11 @@ class SimpleMpdNotifierFactory : public MpdNotifierFactory { SimpleMpdNotifierFactory() {} ~SimpleMpdNotifierFactory() override {} - std::unique_ptr Create(DashProfile dash_profile, - const MpdOptions& mpd_options, + std::unique_ptr Create(const MpdOptions& mpd_options, const std::vector& base_urls, const std::string& output_path) override { - return std::unique_ptr(new SimpleMpdNotifier( - dash_profile, mpd_options, base_urls, output_path)); + return std::unique_ptr( + new SimpleMpdNotifier(mpd_options, base_urls, output_path)); } }; @@ -96,8 +94,8 @@ void MpdWriter::AddBaseUrl(const std::string& base_url) { bool MpdWriter::WriteMpdToFile(const char* file_name) { CHECK(file_name); - std::unique_ptr notifier = notifier_factory_->Create( - kOnDemandProfile, MpdOptions(), base_urls_, file_name); + std::unique_ptr notifier = + notifier_factory_->Create(MpdOptions(), base_urls_, file_name); if (!notifier->Init()) { LOG(ERROR) << "failed to initialize MpdNotifier."; return false; diff --git a/packager/mpd/util/mpd_writer.h b/packager/mpd/util/mpd_writer.h index 6b30b0fea5..6ea9e62c0f 100644 --- a/packager/mpd/util/mpd_writer.h +++ b/packager/mpd/util/mpd_writer.h @@ -35,7 +35,6 @@ class MpdNotifierFactory { virtual ~MpdNotifierFactory() {} virtual std::unique_ptr Create( - DashProfile dash_profile, const MpdOptions& mpd_options, const std::vector& base_urls, const std::string& output_path) = 0; diff --git a/packager/mpd/util/mpd_writer_unittest.cc b/packager/mpd/util/mpd_writer_unittest.cc index c3f5d490bc..a0128a879e 100644 --- a/packager/mpd/util/mpd_writer_unittest.cc +++ b/packager/mpd/util/mpd_writer_unittest.cc @@ -33,14 +33,13 @@ class TestMpdNotifierFactory : public MpdNotifierFactory { // std::unique_ptr. // For now we only need to return MockMpdNotifier() with these set of // expectations for all the tests. - std::unique_ptr Create(DashProfile dash_profile, - const MpdOptions& mpd_options, + std::unique_ptr Create(const MpdOptions& mpd_options, const std::vector& base_urls, const std::string& output_path) override { EXPECT_EQ(expected_base_urls_, base_urls); std::unique_ptr mock_notifier( - new MockMpdNotifier(kOnDemandProfile)); + new MockMpdNotifier(mpd_options)); EXPECT_CALL(*mock_notifier, Init()).WillOnce(Return(true)); EXPECT_CALL(*mock_notifier, NotifyNewContainer(_, _))