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:
parent
634ee65663
commit
222737d5b5
|
@ -9,36 +9,41 @@ DASH options
|
||||||
that if segment_template is not specified, shaka-packager always generates
|
that if segment_template is not specified, shaka-packager always generates
|
||||||
static mpd regardless of the value of this flag.
|
static mpd regardless of the value of this flag.
|
||||||
|
|
||||||
--mpd_output <file_path>
|
--mpd_output {file_path}
|
||||||
|
|
||||||
MPD output file name.
|
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>
|
Comma separated BaseURLs for the MPD. The values will be added as <BaseURL>
|
||||||
element(s) immediately under the <MPD> element.
|
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
|
Specifies, in seconds, a common duration used in the definition of the MPD
|
||||||
Representation data rate.
|
Representation data rate.
|
||||||
|
|
||||||
--minimum_update_period <seconds>
|
--minimum_update_period {seconds}
|
||||||
|
|
||||||
Indicates to the player how often to refresh the media presentation
|
Indicates to the player how often to refresh the media presentation
|
||||||
description in seconds. This value is used for dynamic MPD only.
|
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.
|
Specifies a delay, in seconds, to be added to the media presentation time.
|
||||||
This value is used for dynamic MPD only.
|
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
|
Guaranteed duration of the time shifting buffer for dynamic media
|
||||||
presentations, in seconds.
|
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
|
Any audio/text tracks tagged with this language will have
|
||||||
<Role ... value=\"main\" /> in the manifest. This allows the player to
|
<Role ... value=\"main\" /> in the manifest. This allows the player to
|
||||||
|
|
|
@ -43,6 +43,10 @@ DEFINE_double(suggested_presentation_delay,
|
||||||
0.0,
|
0.0,
|
||||||
"Specifies a delay, in seconds, to be added to the media "
|
"Specifies a delay, in seconds, to be added to the media "
|
||||||
"presentation time. This value is used for dynamic MPD only.");
|
"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,
|
DEFINE_bool(generate_dash_if_iop_compliant_mpd,
|
||||||
true,
|
true,
|
||||||
"Try to generate DASH-IF IOP compliant MPD. This is best effort "
|
"Try to generate DASH-IF IOP compliant MPD. This is best effort "
|
||||||
|
|
|
@ -18,6 +18,7 @@ DECLARE_string(base_urls);
|
||||||
DECLARE_double(minimum_update_period);
|
DECLARE_double(minimum_update_period);
|
||||||
DECLARE_double(min_buffer_time);
|
DECLARE_double(min_buffer_time);
|
||||||
DECLARE_double(suggested_presentation_delay);
|
DECLARE_double(suggested_presentation_delay);
|
||||||
|
DECLARE_string(utc_timings);
|
||||||
DECLARE_bool(generate_dash_if_iop_compliant_mpd);
|
DECLARE_bool(generate_dash_if_iop_compliant_mpd);
|
||||||
|
|
||||||
#endif // APP_MPD_FLAGS_H_
|
#endif // APP_MPD_FLAGS_H_
|
||||||
|
|
|
@ -396,17 +396,30 @@ base::Optional<PackagingParams> GetPackagingParams() {
|
||||||
packaging_params.output_media_info = FLAGS_output_media_info;
|
packaging_params.output_media_info = FLAGS_output_media_info;
|
||||||
|
|
||||||
MpdParams& mpd_params = packaging_params.mpd_params;
|
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.mpd_output = FLAGS_mpd_output;
|
||||||
mpd_params.base_urls = base::SplitString(
|
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 =
|
mpd_params.generate_dash_if_iop_compliant_mpd =
|
||||||
FLAGS_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;
|
HlsParams& hls_params = packaging_params.hls_params;
|
||||||
if (!GetHlsPlaylistType(FLAGS_hls_playlist_type, &hls_params.playlist_type)) {
|
if (!GetHlsPlaylistType(FLAGS_hls_playlist_type, &hls_params.playlist_type)) {
|
||||||
|
|
|
@ -273,6 +273,7 @@ class PackagerAppTest(unittest.TestCase):
|
||||||
output_hls=False,
|
output_hls=False,
|
||||||
hls_playlist_type=None,
|
hls_playlist_type=None,
|
||||||
time_shift_buffer_depth=0.0,
|
time_shift_buffer_depth=0.0,
|
||||||
|
utc_timings=None,
|
||||||
generate_static_mpd=False,
|
generate_static_mpd=False,
|
||||||
ad_cues=None,
|
ad_cues=None,
|
||||||
use_fake_clock=True):
|
use_fake_clock=True):
|
||||||
|
@ -341,6 +342,9 @@ class PackagerAppTest(unittest.TestCase):
|
||||||
else:
|
else:
|
||||||
flags += ['--mpd_output', self.mpd_output]
|
flags += ['--mpd_output', self.mpd_output]
|
||||||
|
|
||||||
|
if utc_timings:
|
||||||
|
flags += ['--utc_timings', utc_timings]
|
||||||
|
|
||||||
if generate_static_mpd:
|
if generate_static_mpd:
|
||||||
flags += ['--generate_static_mpd']
|
flags += ['--generate_static_mpd']
|
||||||
|
|
||||||
|
@ -384,7 +388,7 @@ class PackagerAppTest(unittest.TestCase):
|
||||||
else:
|
else:
|
||||||
match = filecmp.cmp(test_output, golden_file)
|
match = filecmp.cmp(test_output, golden_file)
|
||||||
if not match:
|
if not match:
|
||||||
output, error = self._GitDiff(test_output, golden_file)
|
output, error = self._GitDiff(golden_file, test_output)
|
||||||
command_line = self.packager.GetCommandLine()
|
command_line = self.packager.GetCommandLine()
|
||||||
failure_message = '\n'.join([
|
failure_message = '\n'.join([
|
||||||
output,
|
output,
|
||||||
|
@ -471,7 +475,7 @@ class PackagerAppTest(unittest.TestCase):
|
||||||
actual_file = os.path.join(out_dir, diff_file)
|
actual_file = os.path.join(out_dir, diff_file)
|
||||||
expected_file = os.path.join(gold_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:
|
if output:
|
||||||
failure_messages += [output]
|
failure_messages += [output]
|
||||||
|
@ -1126,7 +1130,13 @@ class PackagerFunctionalTest(PackagerAppTest):
|
||||||
|
|
||||||
def testPackageLiveProfile(self):
|
def testPackageLiveProfile(self):
|
||||||
self.assertPackageSuccess(
|
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')
|
self._CheckTestResults('live-profile')
|
||||||
|
|
||||||
def testPackageLiveStaticProfile(self):
|
def testPackageLiveStaticProfile(self):
|
||||||
|
|
|
@ -25,4 +25,6 @@
|
||||||
</Representation>
|
</Representation>
|
||||||
</AdaptationSet>
|
</AdaptationSet>
|
||||||
</Period>
|
</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>
|
</MPD>
|
||||||
|
|
|
@ -215,6 +215,8 @@ xmlDocPtr MpdBuilder::GenerateMpd() {
|
||||||
break;
|
break;
|
||||||
case MpdType::kDynamic:
|
case MpdType::kDynamic:
|
||||||
AddDynamicMpdInfo(&mpd);
|
AddDynamicMpdInfo(&mpd);
|
||||||
|
// Must be after Period element.
|
||||||
|
AddUtcTiming(&mpd);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
NOTREACHED() << "Unknown MPD type: "
|
NOTREACHED() << "Unknown MPD type: "
|
||||||
|
@ -302,6 +304,19 @@ void MpdBuilder::AddDynamicMpdInfo(XmlNode* mpd_node) {
|
||||||
mpd_options_.mpd_params.suggested_presentation_delay, 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() {
|
float MpdBuilder::GetStaticMpdDuration() {
|
||||||
DCHECK_EQ(MpdType::kStatic, mpd_options_.mpd_type);
|
DCHECK_EQ(MpdType::kStatic, mpd_options_.mpd_type);
|
||||||
|
|
||||||
|
|
|
@ -100,6 +100,9 @@ class MpdBuilder {
|
||||||
// Same as AddStaticMpdInfo() but for 'dynamic' MPDs.
|
// Same as AddStaticMpdInfo() but for 'dynamic' MPDs.
|
||||||
void AddDynamicMpdInfo(xml::XmlNode* mpd_node);
|
void AddDynamicMpdInfo(xml::XmlNode* mpd_node);
|
||||||
|
|
||||||
|
// Add UTCTiming element if utc timing is provided.
|
||||||
|
void AddUtcTiming(xml::XmlNode* mpd_node);
|
||||||
|
|
||||||
float GetStaticMpdDuration();
|
float GetStaticMpdDuration();
|
||||||
|
|
||||||
// Set MPD attributes for dynamic profile MPD. Uses non-zero |mpd_options_| as
|
// Set MPD attributes for dynamic profile MPD. Uses non-zero |mpd_options_| as
|
||||||
|
|
|
@ -272,11 +272,21 @@ TEST_F(LiveMpdBuilderTest, DynamicCheckMpdAttributes) {
|
||||||
" type=\"dynamic\""
|
" type=\"dynamic\""
|
||||||
" publishTime=\"2016-01-11T15:10:24Z\""
|
" publishTime=\"2016-01-11T15:10:24Z\""
|
||||||
" availabilityStartTime=\"2011-12-25T12:30:00\""
|
" 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;
|
std::string mpd_doc;
|
||||||
mutable_mpd_options()->mpd_type = MpdType::kDynamic;
|
mutable_mpd_options()->mpd_type = MpdType::kDynamic;
|
||||||
mutable_mpd_options()->mpd_params.minimum_update_period = 2;
|
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_TRUE(mpd_.ToString(&mpd_doc));
|
||||||
ASSERT_EQ(kExpectedOutput, mpd_doc);
|
ASSERT_EQ(kExpectedOutput, mpd_doc);
|
||||||
}
|
}
|
||||||
|
@ -298,6 +308,15 @@ TEST_F(LiveMpdBuilderTest, StaticCheckMpdAttributes) {
|
||||||
|
|
||||||
std::string mpd_doc;
|
std::string mpd_doc;
|
||||||
mutable_mpd_options()->mpd_type = MpdType::kStatic;
|
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_TRUE(mpd_.ToString(&mpd_doc));
|
||||||
ASSERT_EQ(kExpectedOutput, mpd_doc);
|
ASSERT_EQ(kExpectedOutput, mpd_doc);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,25 +25,31 @@ struct MpdParams {
|
||||||
/// providing playout begins at min_buffer_time after the first bit is
|
/// providing playout begins at min_buffer_time after the first bit is
|
||||||
/// received.
|
/// received.
|
||||||
double min_buffer_time = 2.0;
|
double min_buffer_time = 2.0;
|
||||||
/// Generate static MPD for live profile. Note that this flag has no effect
|
/// Set MPD@minimumUpdatePeriod attribute, which indicates to the player how
|
||||||
/// for on-demand profile, in which case static MPD is always used.
|
/// often to refresh the MPD in seconds. For dynamic MPD only.
|
||||||
bool generate_static_live_mpd = false;
|
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;
|
|
||||||
/// Set MPD@suggestedPresentationDelay attribute. For 'dynamic' media
|
/// Set MPD@suggestedPresentationDelay attribute. For 'dynamic' media
|
||||||
/// presentations, it specifies a delay, in seconds, to be added to the 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
|
/// presentation time. The attribute is not set if the value is 0; the client
|
||||||
/// is expected to choose a suitable value in this case.
|
/// is expected to choose a suitable value in this case.
|
||||||
static constexpr double kSuggestedPresentationDelayNotSet = 0;
|
static constexpr double kSuggestedPresentationDelayNotSet = 0;
|
||||||
double suggested_presentation_delay = kSuggestedPresentationDelayNotSet;
|
double suggested_presentation_delay = kSuggestedPresentationDelayNotSet;
|
||||||
/// Set MPD@minimumUpdatePeriod attribute, which indicates to the player how
|
/// Set MPD@timeShiftBufferDepth attribute, which is the guaranteed duration
|
||||||
/// often to refresh the MPD in seconds. For dynamic MPD only.
|
/// of the time shifting buffer for 'dynamic' media presentations, in seconds.
|
||||||
double minimum_update_period = 0;
|
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\" />
|
/// The tracks tagged with this language will have <Role ... value=\"main\" />
|
||||||
/// in the manifest. This allows the player to choose the correct default
|
/// in the manifest. This allows the player to choose the correct default
|
||||||
/// language for the content.
|
/// language for the content.
|
||||||
std::string default_language;
|
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.
|
/// Try to generate DASH-IF IOP compliant MPD.
|
||||||
bool generate_dash_if_iop_compliant_mpd = true;
|
bool generate_dash_if_iop_compliant_mpd = true;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue