Support UTCTiming

UTCTiming schemeIdUri and value pairs can be provided to packager using
--utc_timings flag. It should be comma separated list of
schemeIdUri=value pairs.

Note that urn:mpeg:dash:utc:direct:2014 scheme is not supported as it
requires the MPD to be dynamically generated on the fly when MPD is
served to client.

Fixes #311.

Change-Id: Ibc07af8a6d8b2b6261ba3ecd2c02f23809f96614
This commit is contained in:
KongQun Yang 2018-03-16 18:37:53 -07:00
parent 634ee65663
commit 222737d5b5
10 changed files with 105 additions and 27 deletions

View File

@ -9,36 +9,41 @@ DASH options
that if segment_template is not specified, shaka-packager always generates
static mpd regardless of the value of this flag.
--mpd_output <file_path>
--mpd_output {file_path}
MPD output file name.
--base_urls <comma separated url>
--base_urls {url}[,{url}]...
Comma separated BaseURLs for the MPD. The values will be added as <BaseURL>
element(s) immediately under the <MPD> element.
--min_buffer_time <seconds>
--min_buffer_time {seconds}
Specifies, in seconds, a common duration used in the definition of the MPD
Representation data rate.
--minimum_update_period <seconds>
--minimum_update_period {seconds}
Indicates to the player how often to refresh the media presentation
description in seconds. This value is used for dynamic MPD only.
--suggested_presentation_delay <seconds>
--suggested_presentation_delay {seconds}
Specifies a delay, in seconds, to be added to the media presentation time.
This value is used for dynamic MPD only.
--time_shift_buffer_depth <seconds>
--time_shift_buffer_depth {seconds}
Guaranteed duration of the time shifting buffer for dynamic media
presentations, in seconds.
--default_language <language>
--utc_timing {scheme_id_uri}={value}[,{scheme_id_uri}={value}]...
Comma separated UTCTiming schemeIdUri and value pairs for the MPD.
This value is used for dynamic MPD only.
--default_language {language}
Any audio/text tracks tagged with this language will have
<Role ... value=\"main\" /> in the manifest. This allows the player to

View File

@ -43,6 +43,10 @@ DEFINE_double(suggested_presentation_delay,
0.0,
"Specifies a delay, in seconds, to be added to the media "
"presentation time. This value is used for dynamic MPD only.");
DEFINE_string(utc_timings,
"",
"Comma separated UTCTiming schemeIdUri and value pairs for the "
"MPD. This value is used for dynamic MPD only.");
DEFINE_bool(generate_dash_if_iop_compliant_mpd,
true,
"Try to generate DASH-IF IOP compliant MPD. This is best effort "

View File

@ -18,6 +18,7 @@ DECLARE_string(base_urls);
DECLARE_double(minimum_update_period);
DECLARE_double(min_buffer_time);
DECLARE_double(suggested_presentation_delay);
DECLARE_string(utc_timings);
DECLARE_bool(generate_dash_if_iop_compliant_mpd);
#endif // APP_MPD_FLAGS_H_

View File

@ -396,17 +396,30 @@ base::Optional<PackagingParams> GetPackagingParams() {
packaging_params.output_media_info = FLAGS_output_media_info;
MpdParams& mpd_params = packaging_params.mpd_params;
mpd_params.generate_static_live_mpd = FLAGS_generate_static_mpd;
mpd_params.mpd_output = FLAGS_mpd_output;
mpd_params.base_urls = base::SplitString(
FLAGS_base_urls, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
FLAGS_base_urls, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
mpd_params.min_buffer_time = FLAGS_min_buffer_time;
mpd_params.minimum_update_period = FLAGS_minimum_update_period;
mpd_params.suggested_presentation_delay = FLAGS_suggested_presentation_delay;
mpd_params.time_shift_buffer_depth = FLAGS_time_shift_buffer_depth;
if (!FLAGS_utc_timings.empty()) {
base::StringPairs pairs;
if (!base::SplitStringIntoKeyValuePairs(FLAGS_utc_timings, '=', ',',
&pairs)) {
LOG(ERROR) << "Invalid --utc_timings scheme_id_uri/value pairs.";
return base::nullopt;
}
for (const auto& string_pair : pairs) {
mpd_params.utc_timings.push_back({string_pair.first, string_pair.second});
}
}
mpd_params.default_language = FLAGS_default_language;
mpd_params.generate_static_live_mpd = FLAGS_generate_static_mpd;
mpd_params.generate_dash_if_iop_compliant_mpd =
FLAGS_generate_dash_if_iop_compliant_mpd;
mpd_params.minimum_update_period = FLAGS_minimum_update_period;
mpd_params.min_buffer_time = FLAGS_min_buffer_time;
mpd_params.time_shift_buffer_depth = FLAGS_time_shift_buffer_depth;
mpd_params.suggested_presentation_delay = FLAGS_suggested_presentation_delay;
mpd_params.default_language = FLAGS_default_language;
HlsParams& hls_params = packaging_params.hls_params;
if (!GetHlsPlaylistType(FLAGS_hls_playlist_type, &hls_params.playlist_type)) {

View File

@ -273,6 +273,7 @@ class PackagerAppTest(unittest.TestCase):
output_hls=False,
hls_playlist_type=None,
time_shift_buffer_depth=0.0,
utc_timings=None,
generate_static_mpd=False,
ad_cues=None,
use_fake_clock=True):
@ -341,6 +342,9 @@ class PackagerAppTest(unittest.TestCase):
else:
flags += ['--mpd_output', self.mpd_output]
if utc_timings:
flags += ['--utc_timings', utc_timings]
if generate_static_mpd:
flags += ['--generate_static_mpd']
@ -384,7 +388,7 @@ class PackagerAppTest(unittest.TestCase):
else:
match = filecmp.cmp(test_output, golden_file)
if not match:
output, error = self._GitDiff(test_output, golden_file)
output, error = self._GitDiff(golden_file, test_output)
command_line = self.packager.GetCommandLine()
failure_message = '\n'.join([
output,
@ -471,7 +475,7 @@ class PackagerAppTest(unittest.TestCase):
actual_file = os.path.join(out_dir, diff_file)
expected_file = os.path.join(gold_dir, diff_file)
output, error = self._GitDiff(actual_file, expected_file)
output, error = self._GitDiff(expected_file, actual_file)
if output:
failure_messages += [output]
@ -1126,7 +1130,13 @@ class PackagerFunctionalTest(PackagerAppTest):
def testPackageLiveProfile(self):
self.assertPackageSuccess(
self._GetStreams(['audio', 'video'], segmented=True), self._GetFlags())
self._GetStreams(['audio', 'video'], segmented=True),
self._GetFlags(
utc_timings=
'urn:mpeg:dash:utc:http-xsdate:2014='
'http://foo.bar/my_body_is_the_current_date_and_time,'
'urn:mpeg:dash:utc:http-head:2014='
'http://foo.bar/check_me_for_the_date_header'))
self._CheckTestResults('live-profile')
def testPackageLiveStaticProfile(self):

View File

@ -25,4 +25,6 @@
</Representation>
</AdaptationSet>
</Period>
<UTCTiming schemeIdUri="urn:mpeg:dash:utc:http-xsdate:2014" value="http://foo.bar/my_body_is_the_current_date_and_time"/>
<UTCTiming schemeIdUri="urn:mpeg:dash:utc:http-head:2014" value="http://foo.bar/check_me_for_the_date_header"/>
</MPD>

View File

@ -215,6 +215,8 @@ xmlDocPtr MpdBuilder::GenerateMpd() {
break;
case MpdType::kDynamic:
AddDynamicMpdInfo(&mpd);
// Must be after Period element.
AddUtcTiming(&mpd);
break;
default:
NOTREACHED() << "Unknown MPD type: "
@ -302,6 +304,19 @@ void MpdBuilder::AddDynamicMpdInfo(XmlNode* mpd_node) {
mpd_options_.mpd_params.suggested_presentation_delay, mpd_node);
}
void MpdBuilder::AddUtcTiming(XmlNode* mpd_node) {
DCHECK(mpd_node);
DCHECK_EQ(MpdType::kDynamic, mpd_options_.mpd_type);
for (const MpdParams::UtcTiming& utc_timing :
mpd_options_.mpd_params.utc_timings) {
XmlNode utc_timing_node("UTCTiming");
utc_timing_node.SetStringAttribute("schemeIdUri", utc_timing.scheme_id_uri);
utc_timing_node.SetStringAttribute("value", utc_timing.value);
mpd_node->AddChild(utc_timing_node.PassScopedPtr());
}
}
float MpdBuilder::GetStaticMpdDuration() {
DCHECK_EQ(MpdType::kStatic, mpd_options_.mpd_type);

View File

@ -100,6 +100,9 @@ class MpdBuilder {
// Same as AddStaticMpdInfo() but for 'dynamic' MPDs.
void AddDynamicMpdInfo(xml::XmlNode* mpd_node);
// Add UTCTiming element if utc timing is provided.
void AddUtcTiming(xml::XmlNode* mpd_node);
float GetStaticMpdDuration();
// Set MPD attributes for dynamic profile MPD. Uses non-zero |mpd_options_| as

View File

@ -272,11 +272,21 @@ TEST_F(LiveMpdBuilderTest, DynamicCheckMpdAttributes) {
" type=\"dynamic\""
" publishTime=\"2016-01-11T15:10:24Z\""
" availabilityStartTime=\"2011-12-25T12:30:00\""
" minimumUpdatePeriod=\"PT2S\"/>\n";
" minimumUpdatePeriod=\"PT2S\">\n"
" <UTCTiming schemeIdUri=\"urn:mpeg:dash:utc:http-xsdate:2014\" "
"value=\"http://foo.bar/my_body_is_the_current_date_and_time\"/>\n"
" <UTCTiming schemeIdUri=\"urn:mpeg:dash:utc:http-head:2014\" "
"value=\"http://foo.bar/check_me_for_the_date_header\"/>\n"
"</MPD>\n";
std::string mpd_doc;
mutable_mpd_options()->mpd_type = MpdType::kDynamic;
mutable_mpd_options()->mpd_params.minimum_update_period = 2;
mutable_mpd_options()->mpd_params.utc_timings = {
{"urn:mpeg:dash:utc:http-xsdate:2014",
"http://foo.bar/my_body_is_the_current_date_and_time"},
{"urn:mpeg:dash:utc:http-head:2014",
"http://foo.bar/check_me_for_the_date_header"}};
ASSERT_TRUE(mpd_.ToString(&mpd_doc));
ASSERT_EQ(kExpectedOutput, mpd_doc);
}
@ -298,6 +308,15 @@ TEST_F(LiveMpdBuilderTest, StaticCheckMpdAttributes) {
std::string mpd_doc;
mutable_mpd_options()->mpd_type = MpdType::kStatic;
// Ignored in static MPD.
mutable_mpd_options()->mpd_params.minimum_update_period = 2;
mutable_mpd_options()->mpd_params.utc_timings = {
{"urn:mpeg:dash:utc:http-xsdate:2014",
"http://foo.bar/my_body_is_the_current_date_and_time"},
{"urn:mpeg:dash:utc:http-head:2014",
"http://foo.bar/check_me_for_the_date_header"}};
ASSERT_TRUE(mpd_.ToString(&mpd_doc));
ASSERT_EQ(kExpectedOutput, mpd_doc);
}

View File

@ -25,25 +25,31 @@ struct MpdParams {
/// providing playout begins at min_buffer_time after the first bit is
/// received.
double min_buffer_time = 2.0;
/// Generate static MPD for live profile. Note that this flag has no effect
/// for on-demand profile, in which case static MPD is always used.
bool generate_static_live_mpd = false;
/// Set MPD@timeShiftBufferDepth attribute, which is the guaranteed duration
/// of the time shifting buffer for 'dynamic' media presentations, in seconds.
double time_shift_buffer_depth = 0;
/// Set MPD@minimumUpdatePeriod attribute, which indicates to the player how
/// often to refresh the MPD in seconds. For dynamic MPD only.
double minimum_update_period = 0;
/// Set MPD@suggestedPresentationDelay attribute. For 'dynamic' media
/// presentations, it specifies a delay, in seconds, to be added to the media
/// presentation time. The attribute is not set if the value is 0; the client
/// is expected to choose a suitable value in this case.
static constexpr double kSuggestedPresentationDelayNotSet = 0;
double suggested_presentation_delay = kSuggestedPresentationDelayNotSet;
/// Set MPD@minimumUpdatePeriod attribute, which indicates to the player how
/// often to refresh the MPD in seconds. For dynamic MPD only.
double minimum_update_period = 0;
/// Set MPD@timeShiftBufferDepth attribute, which is the guaranteed duration
/// of the time shifting buffer for 'dynamic' media presentations, in seconds.
double time_shift_buffer_depth = 0;
/// UTCTimings. For dynamic MPD only.
struct UtcTiming {
std::string scheme_id_uri;
std::string value;
};
std::vector<UtcTiming> utc_timings;
/// The tracks tagged with this language will have <Role ... value=\"main\" />
/// in the manifest. This allows the player to choose the correct default
/// language for the content.
std::string default_language;
/// Generate static MPD for live profile. Note that this flag has no effect
/// for on-demand profile, in which case static MPD is always used.
bool generate_static_live_mpd = false;
/// Try to generate DASH-IF IOP compliant MPD.
bool generate_dash_if_iop_compliant_mpd = true;
};