Prefer Period@duration for static MPD with >1 periods
It is easier to insert Ad Periods with Period@duration compared to Period@start. Change-Id: Ib52e81612562bf60b0e0513a09d9a31c42b09604
This commit is contained in:
parent
48cb55c8d4
commit
221ac81772
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>-->
|
||||
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT2.76317S">
|
||||
<Period id="0">
|
||||
<Period id="0" duration="PT2.06873S">
|
||||
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9">
|
||||
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
|
||||
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b">
|
||||
|
@ -28,7 +28,7 @@
|
|||
</Representation>
|
||||
</AdaptationSet>
|
||||
</Period>
|
||||
<Period id="1" start="PT2.06873S">
|
||||
<Period id="1" duration="PT0.694441S">
|
||||
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9">
|
||||
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
|
||||
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>-->
|
||||
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" profiles="urn:mpeg:dash:profile:isoff-live:2011" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT2.76317S">
|
||||
<Period id="0">
|
||||
<Period id="0" duration="PT2.06873S">
|
||||
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9">
|
||||
<Representation id="0" bandwidth="875099" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
|
||||
<SegmentTemplate timescale="30000" initialization="output_video-init.mp4" media="output_video-$Number$.m4s" startNumber="1">
|
||||
|
@ -22,7 +22,7 @@
|
|||
</Representation>
|
||||
</AdaptationSet>
|
||||
</Period>
|
||||
<Period id="1" start="PT2.06873S">
|
||||
<Period id="1" duration="PT0.694444S">
|
||||
<AdaptationSet id="1" contentType="audio" segmentAlignment="true">
|
||||
<Representation id="1" bandwidth="108486" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include "packager/mpd/base/mpd_builder.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "packager/base/files/file_path.h"
|
||||
#include "packager/base/logging.h"
|
||||
#include "packager/base/strings/string_number_conversions.h"
|
||||
|
@ -170,6 +172,21 @@ xmlDocPtr MpdBuilder::GenerateMpd() {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// Prefer Period@duration to Period@start for static MPD with more than one
|
||||
// periods.
|
||||
if (mpd_options_.mpd_type == MpdType::kStatic && periods_.size() > 1) {
|
||||
// The duration of every period is determined by its start_time and next
|
||||
// period start_time. The code below traverses |periods_| backwards.
|
||||
double next_period_start_time = GetStaticMpdDuration();
|
||||
std::for_each(
|
||||
periods_.rbegin(), periods_.rend(),
|
||||
[&next_period_start_time](const std::unique_ptr<Period>& period) {
|
||||
period->set_duration_seconds(next_period_start_time -
|
||||
period->start_time_in_seconds());
|
||||
next_period_start_time = period->start_time_in_seconds();
|
||||
});
|
||||
}
|
||||
|
||||
for (const auto& period : periods_) {
|
||||
xml::scoped_xml_ptr<xmlNode> period_node(period->GetXml());
|
||||
if (!period_node || !mpd.AddChild(std::move(period_node)))
|
||||
|
@ -242,9 +259,8 @@ void MpdBuilder::AddStaticMpdInfo(XmlNode* mpd_node) {
|
|||
|
||||
static const char kStaticMpdType[] = "static";
|
||||
mpd_node->SetStringAttribute("type", kStaticMpdType);
|
||||
mpd_node->SetStringAttribute(
|
||||
"mediaPresentationDuration",
|
||||
SecondsToXmlDuration(GetStaticMpdDuration(mpd_node)));
|
||||
mpd_node->SetStringAttribute("mediaPresentationDuration",
|
||||
SecondsToXmlDuration(GetStaticMpdDuration()));
|
||||
}
|
||||
|
||||
void MpdBuilder::AddDynamicMpdInfo(XmlNode* mpd_node) {
|
||||
|
@ -290,8 +306,7 @@ void MpdBuilder::AddDynamicMpdInfo(XmlNode* mpd_node) {
|
|||
mpd_options_.mpd_params.suggested_presentation_delay, mpd_node);
|
||||
}
|
||||
|
||||
float MpdBuilder::GetStaticMpdDuration(XmlNode* mpd_node) {
|
||||
DCHECK(mpd_node);
|
||||
float MpdBuilder::GetStaticMpdDuration() {
|
||||
DCHECK_EQ(MpdType::kStatic, mpd_options_.mpd_type);
|
||||
|
||||
if (periods_.empty()) {
|
||||
|
|
|
@ -100,7 +100,7 @@ class MpdBuilder {
|
|||
// Same as AddStaticMpdInfo() but for 'dynamic' MPDs.
|
||||
void AddDynamicMpdInfo(xml::XmlNode* mpd_node);
|
||||
|
||||
float GetStaticMpdDuration(xml::XmlNode* mpd_node);
|
||||
float GetStaticMpdDuration();
|
||||
|
||||
// Set MPD attributes for dynamic profile MPD. Uses non-zero |mpd_options_| as
|
||||
// well as various calculations to set attributes for the MPD.
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "packager/mpd/base/adaptation_set.h"
|
||||
|
@ -12,6 +13,8 @@
|
|||
#include "packager/mpd/test/mpd_builder_test_helper.h"
|
||||
#include "packager/version/version.h"
|
||||
|
||||
using ::testing::HasSubstr;
|
||||
|
||||
namespace shaka {
|
||||
|
||||
namespace {
|
||||
|
@ -150,7 +153,7 @@ TEST_F(OnDemandMpdBuilderTest, MediaInfoMissingBandwidth) {
|
|||
ASSERT_FALSE(mpd_.ToString(&mpd_doc));
|
||||
}
|
||||
|
||||
TEST_F(LiveMpdBuilderTest, MultiplePeriodTest) {
|
||||
TEST_F(OnDemandMpdBuilderTest, MultiplePeriodTest) {
|
||||
const double kPeriodStartTimeSeconds = 1.0;
|
||||
Period* period = mpd_.GetOrCreatePeriod(kPeriodStartTimeSeconds);
|
||||
ASSERT_TRUE(period);
|
||||
|
@ -170,6 +173,40 @@ TEST_F(LiveMpdBuilderTest, MultiplePeriodTest) {
|
|||
ASSERT_EQ(kPeriodStartTimeSeconds3, period3->start_time_in_seconds());
|
||||
}
|
||||
|
||||
TEST_F(OnDemandMpdBuilderTest, MultiplePeriodCheckXmlTest) {
|
||||
const double kPeriodStartTimeSeconds = 0.0;
|
||||
const double kPeriodStartTimeSeconds2 = 3.1;
|
||||
const double kPeriodStartTimeSeconds3 = 8.0;
|
||||
mpd_.GetOrCreatePeriod(kPeriodStartTimeSeconds);
|
||||
mpd_.GetOrCreatePeriod(kPeriodStartTimeSeconds2);
|
||||
mpd_.GetOrCreatePeriod(kPeriodStartTimeSeconds3);
|
||||
|
||||
std::string mpd_doc;
|
||||
ASSERT_TRUE(mpd_.ToString(&mpd_doc));
|
||||
EXPECT_THAT(mpd_doc,
|
||||
HasSubstr(" <Period id=\"0\" duration=\"PT3.1S\"/>\n"
|
||||
" <Period id=\"1\" duration=\"PT4.9S\"/>\n"
|
||||
// There are no Representations so MPD duration is 0,
|
||||
// which results in a negative duration for the last
|
||||
// period. This would not happen in practice.
|
||||
" <Period id=\"2\" duration=\"PT-8S\"/>\n"));
|
||||
}
|
||||
|
||||
TEST_F(LiveMpdBuilderTest, MultiplePeriodCheckXmlTest) {
|
||||
const double kPeriodStartTimeSeconds = 0.0;
|
||||
const double kPeriodStartTimeSeconds2 = 3.1;
|
||||
const double kPeriodStartTimeSeconds3 = 8.0;
|
||||
mpd_.GetOrCreatePeriod(kPeriodStartTimeSeconds);
|
||||
mpd_.GetOrCreatePeriod(kPeriodStartTimeSeconds2);
|
||||
mpd_.GetOrCreatePeriod(kPeriodStartTimeSeconds3);
|
||||
|
||||
std::string mpd_doc;
|
||||
ASSERT_TRUE(mpd_.ToString(&mpd_doc));
|
||||
EXPECT_THAT(mpd_doc, HasSubstr(" <Period id=\"0\" start=\"PT0S\"/>\n"
|
||||
" <Period id=\"1\" start=\"PT3.1S\"/>\n"
|
||||
" <Period id=\"2\" start=\"PT8S\"/>\n"));
|
||||
}
|
||||
|
||||
// Check whether the attributes are set correctly for dynamic <MPD> element.
|
||||
// This test must use ASSERT_EQ for comparison because XmlEqual() cannot
|
||||
// handle namespaces correctly yet.
|
||||
|
|
|
@ -94,7 +94,7 @@ AdaptationSet* Period::GetOrCreateAdaptationSet(
|
|||
return adaptation_set_ptr;
|
||||
}
|
||||
|
||||
xml::scoped_xml_ptr<xmlNode> Period::GetXml() {
|
||||
xml::scoped_xml_ptr<xmlNode> Period::GetXml() const {
|
||||
xml::XmlNode period("Period");
|
||||
|
||||
// Required for 'dynamic' MPDs.
|
||||
|
@ -106,7 +106,10 @@ xml::scoped_xml_ptr<xmlNode> Period::GetXml() {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (mpd_options_.mpd_type == MpdType::kDynamic ||
|
||||
if (duration_seconds_ != 0) {
|
||||
period.SetStringAttribute("duration",
|
||||
SecondsToXmlDuration(duration_seconds_));
|
||||
} else if (mpd_options_.mpd_type == MpdType::kDynamic ||
|
||||
start_time_in_seconds_ != 0) {
|
||||
period.SetStringAttribute("start",
|
||||
SecondsToXmlDuration(start_time_in_seconds_));
|
||||
|
|
|
@ -48,7 +48,7 @@ class Period {
|
|||
/// Generates <Period> xml element with its child AdaptationSet elements.
|
||||
/// @return On success returns a non-NULL scoped_xml_ptr. Otherwise returns a
|
||||
/// NULL scoped_xml_ptr.
|
||||
xml::scoped_xml_ptr<xmlNode> GetXml();
|
||||
xml::scoped_xml_ptr<xmlNode> GetXml() const;
|
||||
|
||||
/// @return The list of AdaptationSets in this Period.
|
||||
const std::list<AdaptationSet*> GetAdaptationSets() const;
|
||||
|
@ -56,6 +56,11 @@ class Period {
|
|||
/// @return The start time of this Period.
|
||||
double start_time_in_seconds() const { return start_time_in_seconds_; }
|
||||
|
||||
/// Set period duration.
|
||||
void set_duration_seconds(double duration_seconds) {
|
||||
duration_seconds_ = duration_seconds;
|
||||
}
|
||||
|
||||
protected:
|
||||
/// @param period_id is an ID number for this Period.
|
||||
/// @param start_time_in_seconds is the start time for this Period.
|
||||
|
@ -102,6 +107,7 @@ class Period {
|
|||
|
||||
const uint32_t id_;
|
||||
const double start_time_in_seconds_;
|
||||
double duration_seconds_ = 0;
|
||||
const MpdOptions& mpd_options_;
|
||||
base::AtomicSequenceNumber* const adaptation_set_counter_;
|
||||
base::AtomicSequenceNumber* const representation_counter_;
|
||||
|
|
|
@ -177,6 +177,38 @@ TEST_P(PeriodTest, DynamicMpdGetXml) {
|
|||
EXPECT_THAT(testable_period_.GetXml().get(), XmlNodeEqual(kExpectedXml));
|
||||
}
|
||||
|
||||
TEST_P(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 id=\"0\" contentType=\"\"/>"
|
||||
"</Period>";
|
||||
EXPECT_THAT(testable_period_.GetXml().get(), XmlNodeEqual(kExpectedXml));
|
||||
}
|
||||
|
||||
// Verify ForceSetSegmentAlignment is called.
|
||||
TEST_P(PeriodTest, Text) {
|
||||
const char kTextMediaInfo[] =
|
||||
|
|
|
@ -315,7 +315,7 @@ float Representation::GetDurationSeconds() const {
|
|||
return media_info_.media_duration_seconds();
|
||||
}
|
||||
|
||||
bool Representation::HasRequiredMediaInfoFields() {
|
||||
bool Representation::HasRequiredMediaInfoFields() const {
|
||||
if (HasVODOnlyFields(media_info_) && HasLiveOnlyFields(media_info_)) {
|
||||
LOG(ERROR) << "MediaInfo cannot have both VOD and Live fields.";
|
||||
return false;
|
||||
|
|
|
@ -172,7 +172,7 @@ class Representation {
|
|||
|
||||
// Returns true if |media_info_| has required fields to generate a valid
|
||||
// Representation. Otherwise returns false.
|
||||
bool HasRequiredMediaInfoFields();
|
||||
bool HasRequiredMediaInfoFields() const;
|
||||
|
||||
// Return false if the segment should be considered a new segment. True if the
|
||||
// segment is contiguous.
|
||||
|
|
Loading…
Reference in New Issue