Add segment_list support for DASH on-demand profile
Configurable under flag --dash_force_segment_list, default to false. Note that DASH live profile is not supported right now.
This commit is contained in:
parent
fe9e26d5d9
commit
4686454a73
1
AUTHORS
1
AUTHORS
|
@ -16,6 +16,7 @@
|
||||||
3Q GmbH <*@3qsdn.com>
|
3Q GmbH <*@3qsdn.com>
|
||||||
Alen Vrecko <alen.vrecko@gmail.com>
|
Alen Vrecko <alen.vrecko@gmail.com>
|
||||||
Anders Hasselqvist <anders.hasselqvist@gmail.com>
|
Anders Hasselqvist <anders.hasselqvist@gmail.com>
|
||||||
|
Audible <*@audible.com>
|
||||||
Chun-da Chen <capitalm.c@gmail.com>
|
Chun-da Chen <capitalm.c@gmail.com>
|
||||||
Daniel Cantarín <canta@canta.com.ar>
|
Daniel Cantarín <canta@canta.com.ar>
|
||||||
Dolby Laboratories <*@dolby.com>
|
Dolby Laboratories <*@dolby.com>
|
||||||
|
|
|
@ -43,6 +43,7 @@ Piotr Srebrny <srebrny.piotr@gmail.com>
|
||||||
Qingquan Wang <wangqq1103@gmail.com>
|
Qingquan Wang <wangqq1103@gmail.com>
|
||||||
Richard Eklycke <richard@eklycke.se>
|
Richard Eklycke <richard@eklycke.se>
|
||||||
Rintaro Kuroiwa <rkuroiwa@google.com>
|
Rintaro Kuroiwa <rkuroiwa@google.com>
|
||||||
|
Samidh Talsania <talsania@audible.com>
|
||||||
Sanil Raut <sr1990003@gmail.com>
|
Sanil Raut <sr1990003@gmail.com>
|
||||||
Sergio Ammirata <sergio@ammirata.net>
|
Sergio Ammirata <sergio@ammirata.net>
|
||||||
Thomas Inskip <tinskip@google.com>
|
Thomas Inskip <tinskip@google.com>
|
||||||
|
|
|
@ -69,3 +69,9 @@ DEFINE_bool(include_mspr_pro_for_playready,
|
||||||
"If enabled, PlayReady Object <mspr:pro> will be inserted into "
|
"If enabled, PlayReady Object <mspr:pro> will be inserted into "
|
||||||
"<ContentProtection ...> element alongside with <cenc:pssh> "
|
"<ContentProtection ...> element alongside with <cenc:pssh> "
|
||||||
"when using PlayReady protection system.");
|
"when using PlayReady protection system.");
|
||||||
|
DEFINE_bool(dash_force_segment_list,
|
||||||
|
false,
|
||||||
|
"Uses SegmentList instead of SegmentBase. Use this if the "
|
||||||
|
"content is huge and the total number of (sub)segment references "
|
||||||
|
"is greater than what the sidx atom allows (65535). Currently "
|
||||||
|
"this flag is only supported in DASH ondemand profile.");
|
||||||
|
|
|
@ -23,5 +23,6 @@ DECLARE_bool(generate_dash_if_iop_compliant_mpd);
|
||||||
DECLARE_bool(allow_approximate_segment_timeline);
|
DECLARE_bool(allow_approximate_segment_timeline);
|
||||||
DECLARE_bool(allow_codec_switching);
|
DECLARE_bool(allow_codec_switching);
|
||||||
DECLARE_bool(include_mspr_pro_for_playready);
|
DECLARE_bool(include_mspr_pro_for_playready);
|
||||||
|
DECLARE_bool(dash_force_segment_list);
|
||||||
|
|
||||||
#endif // APP_MPD_FLAGS_H_
|
#endif // APP_MPD_FLAGS_H_
|
||||||
|
|
|
@ -451,6 +451,7 @@ base::Optional<PackagingParams> GetPackagingParams() {
|
||||||
mpd_params.time_shift_buffer_depth = FLAGS_time_shift_buffer_depth;
|
mpd_params.time_shift_buffer_depth = FLAGS_time_shift_buffer_depth;
|
||||||
mpd_params.preserved_segments_outside_live_window =
|
mpd_params.preserved_segments_outside_live_window =
|
||||||
FLAGS_preserved_segments_outside_live_window;
|
FLAGS_preserved_segments_outside_live_window;
|
||||||
|
mpd_params.use_segment_list = FLAGS_dash_force_segment_list;
|
||||||
|
|
||||||
if (!FLAGS_utc_timings.empty()) {
|
if (!FLAGS_utc_timings.empty()) {
|
||||||
base::StringPairs pairs;
|
base::StringPairs pairs;
|
||||||
|
|
|
@ -478,7 +478,8 @@ class PackagerAppTest(unittest.TestCase):
|
||||||
default_language=None,
|
default_language=None,
|
||||||
segment_duration=1.0,
|
segment_duration=1.0,
|
||||||
use_fake_clock=True,
|
use_fake_clock=True,
|
||||||
allow_codec_switching=False):
|
allow_codec_switching=False,
|
||||||
|
dash_force_segment_list=False):
|
||||||
flags = ['--single_threaded']
|
flags = ['--single_threaded']
|
||||||
|
|
||||||
if not strip_parameter_set_nalus:
|
if not strip_parameter_set_nalus:
|
||||||
|
@ -568,6 +569,10 @@ class PackagerAppTest(unittest.TestCase):
|
||||||
if default_language:
|
if default_language:
|
||||||
flags += ['--default_language', default_language]
|
flags += ['--default_language', default_language]
|
||||||
|
|
||||||
|
if dash_force_segment_list:
|
||||||
|
flags += ['--dash_force_segment_list']
|
||||||
|
flags += ['--generate_sidx_in_media_segments=false']
|
||||||
|
|
||||||
flags.append('--segment_duration={0}'.format(segment_duration))
|
flags.append('--segment_duration={0}'.format(segment_duration))
|
||||||
|
|
||||||
# Use fake clock, so output can be compared.
|
# Use fake clock, so output can be compared.
|
||||||
|
@ -1518,7 +1523,7 @@ class PackagerFunctionalTest(PackagerAppTest):
|
||||||
|
|
||||||
def testEncryptionAndOutputMediaInfoAndMpdFromMediaInfo(self):
|
def testEncryptionAndOutputMediaInfoAndMpdFromMediaInfo(self):
|
||||||
self.assertPackageSuccess(
|
self.assertPackageSuccess(
|
||||||
# The order is not determinstic if there are more than one
|
# The order is not deterministic if there are more than one
|
||||||
# AdaptationSets, so only one is included here.
|
# AdaptationSets, so only one is included here.
|
||||||
self._GetStreams(['video']),
|
self._GetStreams(['video']),
|
||||||
self._GetFlags(encryption=True, output_media_info=True))
|
self._GetFlags(encryption=True, output_media_info=True))
|
||||||
|
@ -1526,6 +1531,15 @@ class PackagerFunctionalTest(PackagerAppTest):
|
||||||
self._CheckTestResults(
|
self._CheckTestResults(
|
||||||
'encryption-and-output-media-info-and-mpd-from-media-info')
|
'encryption-and-output-media-info-and-mpd-from-media-info')
|
||||||
|
|
||||||
|
def testEncryptionAndOutputMediaInfoAndMpdFromMediaInfoSegmentList(self):
|
||||||
|
self.assertPackageSuccess(
|
||||||
|
# The order is not deterministic if there are more than one
|
||||||
|
# AdaptationSets, so only one is included here.
|
||||||
|
self._GetStreams(['audio']),
|
||||||
|
self._GetFlags(encryption=True, output_media_info=True, dash_force_segment_list=True, output_dash=True))
|
||||||
|
self._CheckTestResults(
|
||||||
|
'encryption-and-output-media-info-and-mpd-from-media-info-segmentlist')
|
||||||
|
|
||||||
def testHlsSingleSegmentMp4Encrypted(self):
|
def testHlsSingleSegmentMp4Encrypted(self):
|
||||||
self.assertPackageSuccess(
|
self.assertPackageSuccess(
|
||||||
self._GetStreams(['audio', 'video'], hls=True),
|
self._GetStreams(['audio', 'video'], hls=True),
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,40 @@
|
||||||
|
bandwidth: 133663
|
||||||
|
audio_info {
|
||||||
|
codec: "mp4a.40.2"
|
||||||
|
sampling_frequency: 44100
|
||||||
|
time_scale: 44100
|
||||||
|
num_channels: 2
|
||||||
|
decoder_config: "\022\020V\345\000"
|
||||||
|
}
|
||||||
|
init_range {
|
||||||
|
begin: 0
|
||||||
|
end: 1006
|
||||||
|
}
|
||||||
|
index_range {
|
||||||
|
begin: 1007
|
||||||
|
end: 1006
|
||||||
|
}
|
||||||
|
media_file_name: "bear-640x360-audio.mp4"
|
||||||
|
media_duration_seconds: 2.76317453
|
||||||
|
reference_time_scale: 44100
|
||||||
|
container_type: CONTAINER_MP4
|
||||||
|
protected_content {
|
||||||
|
default_key_id: "1234567890123456"
|
||||||
|
content_protection_entry {
|
||||||
|
uuid: "1077efec-c0b2-4d02-ace3-3c1e52e2fb4b"
|
||||||
|
pssh: "\000\000\0004pssh\001\000\000\000\020w\357\354\300\262M\002\254\343<\036R\342\373K\000\000\000\0011234567890123456\000\000\000\000"
|
||||||
|
}
|
||||||
|
protection_scheme: "cenc"
|
||||||
|
}
|
||||||
|
subsegment_ranges {
|
||||||
|
begin: 1007
|
||||||
|
end: 18034
|
||||||
|
}
|
||||||
|
subsegment_ranges {
|
||||||
|
begin: 18035
|
||||||
|
end: 34716
|
||||||
|
}
|
||||||
|
subsegment_ranges {
|
||||||
|
begin: 34717
|
||||||
|
end: 44575
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?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" 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.739954710006714S">
|
||||||
|
<Period id="0">
|
||||||
|
<AdaptationSet id="0" contentType="audio" subsegmentAlignment="true">
|
||||||
|
<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">
|
||||||
|
<cenc:pssh>AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==</cenc:pssh>
|
||||||
|
</ContentProtection>
|
||||||
|
<Representation id="0" bandwidth="133663" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||||
|
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
|
||||||
|
<BaseURL>bear-640x360-audio.mp4</BaseURL>
|
||||||
|
<SegmentList timescale="44100" duration="44100">
|
||||||
|
<Initialization range="0-1006"/>
|
||||||
|
<SegmentURL mediaRange="1007-18034"/>
|
||||||
|
<SegmentURL mediaRange="18035-34716"/>
|
||||||
|
<SegmentURL mediaRange="34717-44575"/>
|
||||||
|
</SegmentList>
|
||||||
|
</Representation>
|
||||||
|
</AdaptationSet>
|
||||||
|
</Period>
|
||||||
|
</MPD>
|
|
@ -142,6 +142,7 @@ void MpdNotifyMuxerListener::OnMediaEnd(const MediaRanges& media_ranges,
|
||||||
|
|
||||||
DCHECK(media_info_);
|
DCHECK(media_info_);
|
||||||
if (!internal::SetVodInformation(media_ranges, duration_seconds,
|
if (!internal::SetVodInformation(media_ranges, duration_seconds,
|
||||||
|
mpd_notifier_->use_segment_list(),
|
||||||
media_info_.get())) {
|
media_info_.get())) {
|
||||||
LOG(ERROR) << "Failed to generate VOD information from input.";
|
LOG(ERROR) << "Failed to generate VOD information from input.";
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -68,6 +68,19 @@ class MpdNotifyMuxerListenerTest : public ::testing::TestWithParam<MpdType> {
|
||||||
mpd_options.dash_profile = DashProfile::kOnDemand;
|
mpd_options.dash_profile = DashProfile::kOnDemand;
|
||||||
// On-demand profile should be static.
|
// On-demand profile should be static.
|
||||||
mpd_options.mpd_type = MpdType::kStatic;
|
mpd_options.mpd_type = MpdType::kStatic;
|
||||||
|
mpd_options.mpd_params.use_segment_list = false;
|
||||||
|
notifier_.reset(new MockMpdNotifier(mpd_options));
|
||||||
|
listener_.reset(
|
||||||
|
new MpdNotifyMuxerListener(notifier_.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SetupForVodSegmentList() {
|
||||||
|
MpdOptions mpd_options;
|
||||||
|
mpd_options.dash_profile = DashProfile::kOnDemand;
|
||||||
|
// On-demand profile should be static.
|
||||||
|
mpd_options.mpd_type = MpdType::kStatic;
|
||||||
|
mpd_options.mpd_params.use_segment_list = true;
|
||||||
notifier_.reset(new MockMpdNotifier(mpd_options));
|
notifier_.reset(new MockMpdNotifier(mpd_options));
|
||||||
listener_.reset(
|
listener_.reset(
|
||||||
new MpdNotifyMuxerListener(notifier_.get()));
|
new MpdNotifyMuxerListener(notifier_.get()));
|
||||||
|
@ -78,6 +91,7 @@ class MpdNotifyMuxerListenerTest : public ::testing::TestWithParam<MpdType> {
|
||||||
mpd_options.dash_profile = DashProfile::kLive;
|
mpd_options.dash_profile = DashProfile::kLive;
|
||||||
// Live profile can be static or dynamic.
|
// Live profile can be static or dynamic.
|
||||||
mpd_options.mpd_type = GetParam();
|
mpd_options.mpd_type = GetParam();
|
||||||
|
mpd_options.mpd_params.use_segment_list = false;
|
||||||
notifier_.reset(new MockMpdNotifier(mpd_options));
|
notifier_.reset(new MockMpdNotifier(mpd_options));
|
||||||
listener_.reset(new MpdNotifyMuxerListener(notifier_.get()));
|
listener_.reset(new MpdNotifyMuxerListener(notifier_.get()));
|
||||||
}
|
}
|
||||||
|
@ -123,6 +137,28 @@ TEST_F(MpdNotifyMuxerListenerTest, VodClearContent) {
|
||||||
FireOnMediaEndWithParams(GetDefaultOnMediaEndParams());
|
FireOnMediaEndWithParams(GetDefaultOnMediaEndParams());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(MpdNotifyMuxerListenerTest, VodClearContentSegmentList) {
|
||||||
|
SetupForVodSegmentList();
|
||||||
|
MuxerOptions muxer_options;
|
||||||
|
SetDefaultMuxerOptions(&muxer_options);
|
||||||
|
VideoStreamInfoParameters video_params = GetDefaultVideoStreamInfoParams();
|
||||||
|
std::shared_ptr<StreamInfo> video_stream_info =
|
||||||
|
CreateVideoStreamInfo(video_params);
|
||||||
|
|
||||||
|
EXPECT_CALL(*notifier_, NotifyNewContainer(_, _)).Times(0);
|
||||||
|
listener_->OnMediaStart(muxer_options, *video_stream_info,
|
||||||
|
kDefaultReferenceTimeScale,
|
||||||
|
MuxerListener::kContainerMp4);
|
||||||
|
::testing::Mock::VerifyAndClearExpectations(notifier_.get());
|
||||||
|
|
||||||
|
EXPECT_CALL(*notifier_, NotifyNewContainer(
|
||||||
|
ExpectMediaInfoEq(kExpectedDefaultMediaInfoSubsegmentRange), _))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
EXPECT_CALL(*notifier_, Flush());
|
||||||
|
FireOnMediaEndWithParams(GetDefaultOnMediaEndParams());
|
||||||
|
}
|
||||||
|
|
||||||
// default_key_id and pssh are converted to string because when std::equal
|
// default_key_id and pssh are converted to string because when std::equal
|
||||||
// compares a negative char and uint8_t > 127, it considers them not equal.
|
// compares a negative char and uint8_t > 127, it considers them not equal.
|
||||||
MATCHER_P4(ProtectedContentEq, uuid, name, default_key_id, pssh, "") {
|
MATCHER_P4(ProtectedContentEq, uuid, name, default_key_id, pssh, "") {
|
||||||
|
@ -190,6 +226,49 @@ TEST_F(MpdNotifyMuxerListenerTest, VodEncryptedContent) {
|
||||||
FireOnMediaEndWithParams(GetDefaultOnMediaEndParams());
|
FireOnMediaEndWithParams(GetDefaultOnMediaEndParams());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(MpdNotifyMuxerListenerTest, VodEncryptedContentSegmentList) {
|
||||||
|
SetupForVodSegmentList();
|
||||||
|
MuxerOptions muxer_options;
|
||||||
|
SetDefaultMuxerOptions(&muxer_options);
|
||||||
|
VideoStreamInfoParameters video_params = GetDefaultVideoStreamInfoParams();
|
||||||
|
std::shared_ptr<StreamInfo> video_stream_info =
|
||||||
|
CreateVideoStreamInfo(video_params);
|
||||||
|
|
||||||
|
const std::vector<uint8_t> default_key_id(
|
||||||
|
kDefaultKeyId, kDefaultKeyId + arraysize(kDefaultKeyId) - 1);
|
||||||
|
|
||||||
|
const std::string kExpectedMediaInfo =
|
||||||
|
std::string(kExpectedDefaultMediaInfoSubsegmentRange) +
|
||||||
|
"protected_content {\n"
|
||||||
|
" protection_scheme: 'cenc'\n"
|
||||||
|
" content_protection_entry {\n"
|
||||||
|
" uuid: '00010203-0405-0607-0809-0a0b0c0d0e0f'\n"
|
||||||
|
" pssh: '" + std::string(kExpectedDefaultPsshBox) + "'\n"
|
||||||
|
" }\n"
|
||||||
|
" default_key_id: 'defaultkeyid'\n"
|
||||||
|
" include_mspr_pro: 1\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
EXPECT_CALL(*notifier_, NotifyNewContainer(_, _)).Times(0);
|
||||||
|
|
||||||
|
std::vector<uint8_t> iv(kBogusIv, kBogusIv + arraysize(kBogusIv));
|
||||||
|
listener_->OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cenc,
|
||||||
|
default_key_id, iv,
|
||||||
|
GetDefaultKeySystemInfo());
|
||||||
|
|
||||||
|
listener_->OnMediaStart(muxer_options, *video_stream_info,
|
||||||
|
kDefaultReferenceTimeScale,
|
||||||
|
MuxerListener::kContainerMp4);
|
||||||
|
::testing::Mock::VerifyAndClearExpectations(notifier_.get());
|
||||||
|
|
||||||
|
EXPECT_CALL(*notifier_,
|
||||||
|
NotifyNewContainer(ExpectMediaInfoEq(kExpectedMediaInfo), _))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
EXPECT_CALL(*notifier_, Flush());
|
||||||
|
FireOnMediaEndWithParams(GetDefaultOnMediaEndParams());
|
||||||
|
}
|
||||||
|
|
||||||
// Verify that calling OnSampleDurationReady() sets the frame duration in the
|
// Verify that calling OnSampleDurationReady() sets the frame duration in the
|
||||||
// media info, and the media info gets passed to NotifyNewContainer() with
|
// media info, and the media info gets passed to NotifyNewContainer() with
|
||||||
// frame_duration == sample_duration.
|
// frame_duration == sample_duration.
|
||||||
|
@ -223,6 +302,57 @@ TEST_F(MpdNotifyMuxerListenerTest, VodOnSampleDurationReady) {
|
||||||
"container_type: 1\n"
|
"container_type: 1\n"
|
||||||
"media_file_name: 'test_output_file_name.mp4'\n"
|
"media_file_name: 'test_output_file_name.mp4'\n"
|
||||||
"media_duration_seconds: 10.5\n";
|
"media_duration_seconds: 10.5\n";
|
||||||
|
|
||||||
|
const uint32_t kReferenceTimeScale = 1111u; // Should match the protobuf.
|
||||||
|
|
||||||
|
EXPECT_CALL(*notifier_, NotifyNewContainer(_, _)).Times(0);
|
||||||
|
listener_->OnMediaStart(muxer_options, *video_stream_info,
|
||||||
|
kReferenceTimeScale, MuxerListener::kContainerMp4);
|
||||||
|
listener_->OnSampleDurationReady(kSampleDuration);
|
||||||
|
::testing::Mock::VerifyAndClearExpectations(notifier_.get());
|
||||||
|
|
||||||
|
EXPECT_CALL(*notifier_,
|
||||||
|
NotifyNewContainer(ExpectMediaInfoEq(kExpectedMediaInfo), _))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
EXPECT_CALL(*notifier_, Flush());
|
||||||
|
FireOnMediaEndWithParams(GetDefaultOnMediaEndParams());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(MpdNotifyMuxerListenerTest, VodOnSampleDurationReadySegmentList) {
|
||||||
|
SetupForVodSegmentList();
|
||||||
|
MuxerOptions muxer_options;
|
||||||
|
SetDefaultMuxerOptions(&muxer_options);
|
||||||
|
VideoStreamInfoParameters video_params = GetDefaultVideoStreamInfoParams();
|
||||||
|
std::shared_ptr<StreamInfo> video_stream_info =
|
||||||
|
CreateVideoStreamInfo(video_params);
|
||||||
|
const uint32_t kSampleDuration = 1234u;
|
||||||
|
const char kExpectedMediaInfo[] =
|
||||||
|
"video_info {\n"
|
||||||
|
" frame_duration: 1234\n" // Should match the constant above.
|
||||||
|
" codec: 'avc1.010101'\n"
|
||||||
|
" width: 720\n"
|
||||||
|
" height: 480\n"
|
||||||
|
" time_scale: 10\n"
|
||||||
|
" pixel_width: 1\n"
|
||||||
|
" pixel_height: 1\n"
|
||||||
|
"}\n"
|
||||||
|
"init_range {\n"
|
||||||
|
" begin: 0\n"
|
||||||
|
" end: 120\n"
|
||||||
|
"}\n"
|
||||||
|
"index_range {\n"
|
||||||
|
" begin: 121\n"
|
||||||
|
" end: 221\n"
|
||||||
|
"}\n"
|
||||||
|
"reference_time_scale: 1111\n"
|
||||||
|
"container_type: 1\n"
|
||||||
|
"media_file_name: 'test_output_file_name.mp4'\n"
|
||||||
|
"media_duration_seconds: 10.5\n"
|
||||||
|
"subsegment_ranges {\n"
|
||||||
|
" begin: 222\n"
|
||||||
|
" end: 9999\n"
|
||||||
|
"}\n";
|
||||||
const uint32_t kReferenceTimeScale = 1111u; // Should match the protobuf.
|
const uint32_t kReferenceTimeScale = 1111u; // Should match the protobuf.
|
||||||
|
|
||||||
EXPECT_CALL(*notifier_, NotifyNewContainer(_, _)).Times(0);
|
EXPECT_CALL(*notifier_, NotifyNewContainer(_, _)).Times(0);
|
||||||
|
@ -278,6 +408,44 @@ TEST_F(MpdNotifyMuxerListenerTest, VodOnNewSegment) {
|
||||||
FireOnMediaEndWithParams(GetDefaultOnMediaEndParams());
|
FireOnMediaEndWithParams(GetDefaultOnMediaEndParams());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(MpdNotifyMuxerListenerTest, VodOnNewSegmentSegmentList) {
|
||||||
|
SetupForVodSegmentList();
|
||||||
|
MuxerOptions muxer_options;
|
||||||
|
SetDefaultMuxerOptions(&muxer_options);
|
||||||
|
VideoStreamInfoParameters video_params = GetDefaultVideoStreamInfoParams();
|
||||||
|
std::shared_ptr<StreamInfo> video_stream_info =
|
||||||
|
CreateVideoStreamInfo(video_params);
|
||||||
|
|
||||||
|
const uint64_t kStartTime1 = 0u;
|
||||||
|
const uint64_t kDuration1 = 1000u;
|
||||||
|
const uint64_t kSegmentFileSize1 = 29812u;
|
||||||
|
const uint64_t kStartTime2 = 1001u;
|
||||||
|
const uint64_t kDuration2 = 3787u;
|
||||||
|
const uint64_t kSegmentFileSize2 = 83743u;
|
||||||
|
|
||||||
|
EXPECT_CALL(*notifier_, NotifyNewContainer(_, _)).Times(0);
|
||||||
|
EXPECT_CALL(*notifier_, NotifyNewSegment(_, _, _, _)).Times(0);
|
||||||
|
listener_->OnMediaStart(muxer_options, *video_stream_info,
|
||||||
|
kDefaultReferenceTimeScale,
|
||||||
|
MuxerListener::kContainerMp4);
|
||||||
|
listener_->OnNewSegment("", kStartTime1, kDuration1, kSegmentFileSize1);
|
||||||
|
listener_->OnCueEvent(kStartTime2, "dummy cue data");
|
||||||
|
listener_->OnNewSegment("", kStartTime2, kDuration2, kSegmentFileSize2);
|
||||||
|
::testing::Mock::VerifyAndClearExpectations(notifier_.get());
|
||||||
|
|
||||||
|
InSequence s;
|
||||||
|
EXPECT_CALL(*notifier_, NotifyNewContainer(
|
||||||
|
ExpectMediaInfoEq(kExpectedDefaultMediaInfoSubsegmentRange), _))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
EXPECT_CALL(*notifier_,
|
||||||
|
NotifyNewSegment(_, kStartTime1, kDuration1, kSegmentFileSize1));
|
||||||
|
EXPECT_CALL(*notifier_, NotifyCueEvent(_, kStartTime2));
|
||||||
|
EXPECT_CALL(*notifier_,
|
||||||
|
NotifyNewSegment(_, kStartTime2, kDuration2, kSegmentFileSize2));
|
||||||
|
EXPECT_CALL(*notifier_, Flush());
|
||||||
|
FireOnMediaEndWithParams(GetDefaultOnMediaEndParams());
|
||||||
|
}
|
||||||
|
|
||||||
// Verify the event handling with multiple files, i.e. multiple OnMediaStart and
|
// Verify the event handling with multiple files, i.e. multiple OnMediaStart and
|
||||||
// OnMediaEnd calls.
|
// OnMediaEnd calls.
|
||||||
TEST_F(MpdNotifyMuxerListenerTest, VodMultipleFiles) {
|
TEST_F(MpdNotifyMuxerListenerTest, VodMultipleFiles) {
|
||||||
|
@ -341,6 +509,68 @@ TEST_F(MpdNotifyMuxerListenerTest, VodMultipleFiles) {
|
||||||
FireOnMediaEndWithParams(GetDefaultOnMediaEndParams());
|
FireOnMediaEndWithParams(GetDefaultOnMediaEndParams());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(MpdNotifyMuxerListenerTest, VodMultipleFilesSegmentList) {
|
||||||
|
SetupForVodSegmentList();
|
||||||
|
MuxerOptions muxer_options1;
|
||||||
|
SetDefaultMuxerOptions(&muxer_options1);
|
||||||
|
muxer_options1.output_file_name = "test_output1.mp4";
|
||||||
|
MuxerOptions muxer_options2 = muxer_options1;
|
||||||
|
muxer_options2.output_file_name = "test_output2.mp4";
|
||||||
|
|
||||||
|
MediaInfo expected_media_info1 =
|
||||||
|
ConvertToMediaInfo(kExpectedDefaultMediaInfoSubsegmentRange);
|
||||||
|
expected_media_info1.set_media_file_name("test_output1.mp4");
|
||||||
|
MediaInfo expected_media_info2 = expected_media_info1;
|
||||||
|
expected_media_info2.set_media_file_name("test_output2.mp4");
|
||||||
|
|
||||||
|
VideoStreamInfoParameters video_params = GetDefaultVideoStreamInfoParams();
|
||||||
|
std::shared_ptr<StreamInfo> video_stream_info =
|
||||||
|
CreateVideoStreamInfo(video_params);
|
||||||
|
|
||||||
|
const uint64_t kStartTime1 = 0u;
|
||||||
|
const uint64_t kDuration1 = 1000u;
|
||||||
|
const uint64_t kSegmentFileSize1 = 29812u;
|
||||||
|
const uint64_t kStartTime2 = 1001u;
|
||||||
|
const uint64_t kDuration2 = 3787u;
|
||||||
|
const uint64_t kSegmentFileSize2 = 83743u;
|
||||||
|
|
||||||
|
// Expectation for first file before OnMediaEnd.
|
||||||
|
EXPECT_CALL(*notifier_, NotifyNewContainer(_, _)).Times(0);
|
||||||
|
EXPECT_CALL(*notifier_, NotifyNewSegment(_, _, _, _)).Times(0);
|
||||||
|
listener_->OnMediaStart(muxer_options1, *video_stream_info,
|
||||||
|
kDefaultReferenceTimeScale,
|
||||||
|
MuxerListener::kContainerMp4);
|
||||||
|
listener_->OnNewSegment("", kStartTime1, kDuration1, kSegmentFileSize1);
|
||||||
|
listener_->OnCueEvent(kStartTime2, "dummy cue data");
|
||||||
|
::testing::Mock::VerifyAndClearExpectations(notifier_.get());
|
||||||
|
|
||||||
|
// Expectation for first file OnMediaEnd.
|
||||||
|
InSequence s;
|
||||||
|
EXPECT_CALL(*notifier_,
|
||||||
|
NotifyNewContainer(EqualsProto(expected_media_info1), _))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
EXPECT_CALL(*notifier_,
|
||||||
|
NotifyNewSegment(_, kStartTime1, kDuration1, kSegmentFileSize1));
|
||||||
|
EXPECT_CALL(*notifier_, NotifyCueEvent(_, kStartTime2));
|
||||||
|
EXPECT_CALL(*notifier_, Flush());
|
||||||
|
FireOnMediaEndWithParams(GetDefaultOnMediaEndParams());
|
||||||
|
|
||||||
|
// Expectation for second file before OnMediaEnd.
|
||||||
|
listener_->OnMediaStart(muxer_options2, *video_stream_info,
|
||||||
|
kDefaultReferenceTimeScale,
|
||||||
|
MuxerListener::kContainerMp4);
|
||||||
|
listener_->OnNewSegment("", kStartTime2, kDuration2, kSegmentFileSize2);
|
||||||
|
|
||||||
|
// Expectation for second file OnMediaEnd.
|
||||||
|
EXPECT_CALL(*notifier_,
|
||||||
|
NotifyMediaInfoUpdate(_, EqualsProto(expected_media_info2)));
|
||||||
|
EXPECT_CALL(*notifier_,
|
||||||
|
NotifyNewSegment(_, kStartTime2, kDuration2, kSegmentFileSize2));
|
||||||
|
EXPECT_CALL(*notifier_, Flush());
|
||||||
|
FireOnMediaEndWithParams(GetDefaultOnMediaEndParams());
|
||||||
|
}
|
||||||
|
|
||||||
// Live without key rotation. Note that OnEncryptionInfoReady() is called before
|
// Live without key rotation. Note that OnEncryptionInfoReady() is called before
|
||||||
// OnMediaStart() but no more calls.
|
// OnMediaStart() but no more calls.
|
||||||
TEST_P(MpdNotifyMuxerListenerTest, LiveNoKeyRotation) {
|
TEST_P(MpdNotifyMuxerListenerTest, LiveNoKeyRotation) {
|
||||||
|
|
|
@ -25,11 +25,12 @@ namespace {
|
||||||
const char kMediaInfoSuffix[] = ".media_info";
|
const char kMediaInfoSuffix[] = ".media_info";
|
||||||
|
|
||||||
std::unique_ptr<MuxerListener> CreateMediaInfoDumpListenerInternal(
|
std::unique_ptr<MuxerListener> CreateMediaInfoDumpListenerInternal(
|
||||||
const std::string& output) {
|
const std::string& output,
|
||||||
|
bool use_segment_list) {
|
||||||
DCHECK(!output.empty());
|
DCHECK(!output.empty());
|
||||||
|
|
||||||
std::unique_ptr<MuxerListener> listener(
|
std::unique_ptr<MuxerListener> listener(
|
||||||
new VodMediaInfoDumpMuxerListener(output + kMediaInfoSuffix));
|
new VodMediaInfoDumpMuxerListener(output + kMediaInfoSuffix, use_segment_list));
|
||||||
return listener;
|
return listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,11 +81,13 @@ std::list<std::unique_ptr<MuxerListener>> CreateHlsListenersInternal(
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
MuxerListenerFactory::MuxerListenerFactory(bool output_media_info,
|
MuxerListenerFactory::MuxerListenerFactory(bool output_media_info,
|
||||||
|
bool use_segment_list,
|
||||||
MpdNotifier* mpd_notifier,
|
MpdNotifier* mpd_notifier,
|
||||||
hls::HlsNotifier* hls_notifier)
|
hls::HlsNotifier* hls_notifier)
|
||||||
: output_media_info_(output_media_info),
|
: output_media_info_(output_media_info),
|
||||||
mpd_notifier_(mpd_notifier),
|
mpd_notifier_(mpd_notifier),
|
||||||
hls_notifier_(hls_notifier) {}
|
hls_notifier_(hls_notifier),
|
||||||
|
use_segment_list_(use_segment_list) {}
|
||||||
|
|
||||||
std::unique_ptr<MuxerListener> MuxerListenerFactory::CreateListener(
|
std::unique_ptr<MuxerListener> MuxerListenerFactory::CreateListener(
|
||||||
const StreamData& stream) {
|
const StreamData& stream) {
|
||||||
|
@ -103,7 +106,8 @@ std::unique_ptr<MuxerListener> MuxerListenerFactory::CreateListener(
|
||||||
new CombinedMuxerListener);
|
new CombinedMuxerListener);
|
||||||
if (output_media_info_) {
|
if (output_media_info_) {
|
||||||
combined_listener->AddListener(
|
combined_listener->AddListener(
|
||||||
CreateMediaInfoDumpListenerInternal(stream.media_info_output));
|
CreateMediaInfoDumpListenerInternal(stream.media_info_output,
|
||||||
|
use_segment_list_));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mpd_notifier_ && !stream.hls_only) {
|
if (mpd_notifier_ && !stream.hls_only) {
|
||||||
|
|
|
@ -58,11 +58,15 @@ class MuxerListenerFactory {
|
||||||
/// Create a new muxer listener.
|
/// Create a new muxer listener.
|
||||||
/// @param output_media_info must be true for the combined listener to include
|
/// @param output_media_info must be true for the combined listener to include
|
||||||
/// a media info dump listener.
|
/// a media info dump listener.
|
||||||
|
/// @param use_segment_list is set when mpd_notifier_ is null and
|
||||||
|
/// --output_media_info is set. If mpd_notifer is non-null, this value
|
||||||
|
/// is the same as mpd_notifier->use_segment_list().
|
||||||
/// @param mpd_notifer must be non-null for the combined listener to include a
|
/// @param mpd_notifer must be non-null for the combined listener to include a
|
||||||
/// mpd listener.
|
/// mpd listener.
|
||||||
/// @param hls_notifier must be non-null for the combined listener to include
|
/// @param hls_notifier must be non-null for the combined listener to include
|
||||||
/// an HLS listener.
|
/// an HLS listener.
|
||||||
MuxerListenerFactory(bool output_media_info,
|
MuxerListenerFactory(bool output_media_info,
|
||||||
|
bool use_segment_list,
|
||||||
MpdNotifier* mpd_notifier,
|
MpdNotifier* mpd_notifier,
|
||||||
hls::HlsNotifier* hls_notifier);
|
hls::HlsNotifier* hls_notifier);
|
||||||
|
|
||||||
|
@ -81,6 +85,9 @@ class MuxerListenerFactory {
|
||||||
MpdNotifier* mpd_notifier_;
|
MpdNotifier* mpd_notifier_;
|
||||||
hls::HlsNotifier* hls_notifier_;
|
hls::HlsNotifier* hls_notifier_;
|
||||||
|
|
||||||
|
/// This is set when mpd_notifier_ is NULL and --output_media_info is set.
|
||||||
|
bool use_segment_list_;
|
||||||
|
|
||||||
// A counter to track which stream we are on.
|
// A counter to track which stream we are on.
|
||||||
int stream_index_ = 0;
|
int stream_index_ = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -251,6 +251,7 @@ bool IsMediaInfoCompatible(const MediaInfo& media_info1,
|
||||||
|
|
||||||
bool SetVodInformation(const MuxerListener::MediaRanges& media_ranges,
|
bool SetVodInformation(const MuxerListener::MediaRanges& media_ranges,
|
||||||
float duration_seconds,
|
float duration_seconds,
|
||||||
|
bool use_segment_lists,
|
||||||
MediaInfo* media_info) {
|
MediaInfo* media_info) {
|
||||||
DCHECK(media_info);
|
DCHECK(media_info);
|
||||||
|
|
||||||
|
@ -270,6 +271,12 @@ bool SetVodInformation(const MuxerListener::MediaRanges& media_ranges,
|
||||||
media_info->mutable_index_range());
|
media_info->mutable_index_range());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (use_segment_lists) {
|
||||||
|
for (const auto& range : media_ranges.subsegment_ranges) {
|
||||||
|
SetRange(range.start, range.end, media_info->add_subsegment_ranges());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
media_info->set_media_duration_seconds(duration_seconds);
|
media_info->set_media_duration_seconds(duration_seconds);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -42,6 +42,7 @@ bool IsMediaInfoCompatible(const MediaInfo& media_info1,
|
||||||
/// @return true on success, false otherwise.
|
/// @return true on success, false otherwise.
|
||||||
bool SetVodInformation(const MuxerListener::MediaRanges& media_ranges,
|
bool SetVodInformation(const MuxerListener::MediaRanges& media_ranges,
|
||||||
float duration_seconds,
|
float duration_seconds,
|
||||||
|
bool use_segment_list,
|
||||||
MediaInfo* media_info);
|
MediaInfo* media_info);
|
||||||
|
|
||||||
/// @param protection_scheme specifies the protection scheme: 'cenc', 'cens',
|
/// @param protection_scheme specifies the protection scheme: 'cenc', 'cens',
|
||||||
|
|
|
@ -43,6 +43,33 @@ const char kExpectedDefaultMediaInfo[] =
|
||||||
"container_type: 1\n"
|
"container_type: 1\n"
|
||||||
"media_file_name: 'test_output_file_name.mp4'\n"
|
"media_file_name: 'test_output_file_name.mp4'\n"
|
||||||
"media_duration_seconds: 10.5\n";
|
"media_duration_seconds: 10.5\n";
|
||||||
|
|
||||||
|
const char kExpectedDefaultMediaInfoSubsegmentRange[] =
|
||||||
|
"video_info {\n"
|
||||||
|
" codec: 'avc1.010101'\n"
|
||||||
|
" width: 720\n"
|
||||||
|
" height: 480\n"
|
||||||
|
" time_scale: 10\n"
|
||||||
|
" pixel_width: 1\n"
|
||||||
|
" pixel_height: 1\n"
|
||||||
|
"}\n"
|
||||||
|
"init_range {\n"
|
||||||
|
" begin: 0\n"
|
||||||
|
" end: 120\n"
|
||||||
|
"}\n"
|
||||||
|
"index_range {\n"
|
||||||
|
" begin: 121\n"
|
||||||
|
" end: 221\n"
|
||||||
|
"}\n"
|
||||||
|
"reference_time_scale: 1000\n"
|
||||||
|
"container_type: 1\n"
|
||||||
|
"media_file_name: 'test_output_file_name.mp4'\n"
|
||||||
|
"media_duration_seconds: 10.5\n"
|
||||||
|
"subsegment_ranges {\n"
|
||||||
|
" begin: 222\n"
|
||||||
|
" end: 9999\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
const uint32_t kDefaultReferenceTimeScale = 1000u;
|
const uint32_t kDefaultReferenceTimeScale = 1000u;
|
||||||
|
|
||||||
// Struct that gets passed for to CreateVideoStreamInfo() to create a
|
// Struct that gets passed for to CreateVideoStreamInfo() to create a
|
||||||
|
|
|
@ -22,8 +22,9 @@ namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
VodMediaInfoDumpMuxerListener::VodMediaInfoDumpMuxerListener(
|
VodMediaInfoDumpMuxerListener::VodMediaInfoDumpMuxerListener(
|
||||||
const std::string& output_file_path)
|
const std::string& output_file_path, bool use_segment_list)
|
||||||
: output_file_name_(output_file_path) {}
|
: output_file_name_(output_file_path),
|
||||||
|
use_segment_list_(use_segment_list) {}
|
||||||
|
|
||||||
VodMediaInfoDumpMuxerListener::~VodMediaInfoDumpMuxerListener() {}
|
VodMediaInfoDumpMuxerListener::~VodMediaInfoDumpMuxerListener() {}
|
||||||
|
|
||||||
|
@ -78,7 +79,7 @@ void VodMediaInfoDumpMuxerListener::OnMediaEnd(const MediaRanges& media_ranges,
|
||||||
float duration_seconds) {
|
float duration_seconds) {
|
||||||
DCHECK(media_info_);
|
DCHECK(media_info_);
|
||||||
if (!internal::SetVodInformation(media_ranges, duration_seconds,
|
if (!internal::SetVodInformation(media_ranges, duration_seconds,
|
||||||
media_info_.get())) {
|
use_segment_list_, media_info_.get())) {
|
||||||
LOG(ERROR) << "Failed to generate VOD information from input.";
|
LOG(ERROR) << "Failed to generate VOD information from input.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ namespace media {
|
||||||
|
|
||||||
class VodMediaInfoDumpMuxerListener : public MuxerListener {
|
class VodMediaInfoDumpMuxerListener : public MuxerListener {
|
||||||
public:
|
public:
|
||||||
VodMediaInfoDumpMuxerListener(const std::string& output_file_name);
|
VodMediaInfoDumpMuxerListener(const std::string& output_file_name, bool use_segment_list);
|
||||||
~VodMediaInfoDumpMuxerListener() override;
|
~VodMediaInfoDumpMuxerListener() override;
|
||||||
|
|
||||||
/// @name MuxerListener implementation overrides.
|
/// @name MuxerListener implementation overrides.
|
||||||
|
@ -63,6 +63,8 @@ class VodMediaInfoDumpMuxerListener : public MuxerListener {
|
||||||
static bool WriteMediaInfoToFile(const MediaInfo& media_info,
|
static bool WriteMediaInfoToFile(const MediaInfo& media_info,
|
||||||
const std::string& output_file_path);
|
const std::string& output_file_path);
|
||||||
|
|
||||||
|
void set_use_segment_list(bool value) {use_segment_list_ = value;}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string output_file_name_;
|
std::string output_file_name_;
|
||||||
std::unique_ptr<MediaInfo> media_info_;
|
std::unique_ptr<MediaInfo> media_info_;
|
||||||
|
@ -74,6 +76,8 @@ class VodMediaInfoDumpMuxerListener : public MuxerListener {
|
||||||
std::vector<uint8_t> default_key_id_;
|
std::vector<uint8_t> default_key_id_;
|
||||||
std::vector<ProtectionSystemSpecificInfo> key_system_info_;
|
std::vector<ProtectionSystemSpecificInfo> key_system_info_;
|
||||||
|
|
||||||
|
bool use_segment_list_ = false;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(VodMediaInfoDumpMuxerListener);
|
DISALLOW_COPY_AND_ASSIGN(VodMediaInfoDumpMuxerListener);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,12 @@ class VodMediaInfoDumpMuxerListenerTest : public ::testing::Test {
|
||||||
DLOG(INFO) << "Created temp file: " << temp_file_path_.value();
|
DLOG(INFO) << "Created temp file: " << temp_file_path_.value();
|
||||||
|
|
||||||
listener_.reset(new VodMediaInfoDumpMuxerListener(temp_file_path_
|
listener_.reset(new VodMediaInfoDumpMuxerListener(temp_file_path_
|
||||||
.AsUTF8Unsafe()));
|
.AsUTF8Unsafe(),false));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSegmentListFlag() {
|
||||||
|
listener_->set_use_segment_list(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TearDown() override {
|
void TearDown() override {
|
||||||
|
@ -228,6 +233,7 @@ TEST_F(VodMediaInfoDumpMuxerListenerTest, CheckPixelWidthAndHeightSet) {
|
||||||
"container_type: 1\n"
|
"container_type: 1\n"
|
||||||
"media_file_name: 'test_output_file_name.mp4'\n"
|
"media_file_name: 'test_output_file_name.mp4'\n"
|
||||||
"media_duration_seconds: 10.5\n";
|
"media_duration_seconds: 10.5\n";
|
||||||
|
|
||||||
EXPECT_THAT(temp_file_path_.AsUTF8Unsafe(),
|
EXPECT_THAT(temp_file_path_.AsUTF8Unsafe(),
|
||||||
FileContentEqualsProto(kExpectedProtobufOutput));
|
FileContentEqualsProto(kExpectedProtobufOutput));
|
||||||
}
|
}
|
||||||
|
@ -274,5 +280,47 @@ TEST_F(VodMediaInfoDumpMuxerListenerTest, CheckBandwidth) {
|
||||||
FileContentEqualsProto(kExpectedProtobufOutput));
|
FileContentEqualsProto(kExpectedProtobufOutput));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Equivalent tests with segment list flag on which writes subsegment ranges
|
||||||
|
// to media info files
|
||||||
|
|
||||||
|
TEST_F(VodMediaInfoDumpMuxerListenerTest, UnencryptedStream_Normal_SegmentList) {
|
||||||
|
SetSegmentListFlag();
|
||||||
|
std::shared_ptr<StreamInfo> stream_info =
|
||||||
|
CreateVideoStreamInfo(GetDefaultVideoStreamInfoParams());
|
||||||
|
|
||||||
|
FireOnMediaStartWithDefaultMuxerOptions(*stream_info, !kEnableEncryption);
|
||||||
|
OnMediaEndParameters media_end_param = GetDefaultOnMediaEndParams();
|
||||||
|
FireOnMediaEndWithParams(media_end_param);
|
||||||
|
|
||||||
|
const char kExpectedProtobufOutput[] =
|
||||||
|
"bandwidth: 0\n"
|
||||||
|
"video_info {\n"
|
||||||
|
" codec: 'avc1.010101'\n"
|
||||||
|
" width: 720\n"
|
||||||
|
" height: 480\n"
|
||||||
|
" time_scale: 10\n"
|
||||||
|
" pixel_width: 1\n"
|
||||||
|
" pixel_height: 1\n"
|
||||||
|
"}\n"
|
||||||
|
"init_range {\n"
|
||||||
|
" begin: 0\n"
|
||||||
|
" end: 120\n"
|
||||||
|
"}\n"
|
||||||
|
"index_range {\n"
|
||||||
|
" begin: 121\n"
|
||||||
|
" end: 221\n"
|
||||||
|
"}\n"
|
||||||
|
"reference_time_scale: 1000\n"
|
||||||
|
"container_type: 1\n"
|
||||||
|
"media_file_name: 'test_output_file_name.mp4'\n"
|
||||||
|
"media_duration_seconds: 10.5\n"
|
||||||
|
"subsegment_ranges {\n"
|
||||||
|
" begin: 222\n"
|
||||||
|
" end: 9999\n"
|
||||||
|
"}\n";
|
||||||
|
EXPECT_THAT(temp_file_path_.AsUTF8Unsafe(),
|
||||||
|
FileContentEqualsProto(kExpectedProtobufOutput));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
|
@ -172,6 +172,7 @@ message MediaInfo {
|
||||||
optional Range init_range = 6;
|
optional Range init_range = 6;
|
||||||
optional Range index_range = 7;
|
optional Range index_range = 7;
|
||||||
optional string media_file_name = 8;
|
optional string media_file_name = 8;
|
||||||
|
repeated Range subsegment_ranges = 23;
|
||||||
// END VOD only.
|
// END VOD only.
|
||||||
|
|
||||||
// VOD and static LIVE.
|
// VOD and static LIVE.
|
||||||
|
|
|
@ -113,6 +113,11 @@ class MpdNotifier {
|
||||||
/// @return The mpd type for this object.
|
/// @return The mpd type for this object.
|
||||||
MpdType mpd_type() const { return mpd_options_.mpd_type; }
|
MpdType mpd_type() const { return mpd_options_.mpd_type; }
|
||||||
|
|
||||||
|
/// @return The value of dash_force_segment_list flag
|
||||||
|
bool use_segment_list() const {
|
||||||
|
return mpd_options_.mpd_params.use_segment_list;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const MpdOptions mpd_options_;
|
const MpdOptions mpd_options_;
|
||||||
|
|
||||||
|
|
|
@ -265,7 +265,9 @@ base::Optional<xml::XmlNode> Representation::GetXml() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HasVODOnlyFields(media_info_) &&
|
if (HasVODOnlyFields(media_info_) &&
|
||||||
!representation.AddVODOnlyInfo(media_info_)) {
|
!representation.AddVODOnlyInfo(
|
||||||
|
media_info_, mpd_options_.mpd_params.use_segment_list,
|
||||||
|
mpd_options_.mpd_params.target_segment_duration)) {
|
||||||
LOG(ERROR) << "Failed to add VOD info.";
|
LOG(ERROR) << "Failed to add VOD info.";
|
||||||
return base::nullopt;
|
return base::nullopt;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <gflags/gflags.h>
|
#include <gflags/gflags.h>
|
||||||
#include <libxml/tree.h>
|
#include <libxml/tree.h>
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
|
@ -376,57 +377,83 @@ bool RepresentationXmlNode::AddAudioInfo(const AudioInfo& audio_info) {
|
||||||
AddAudioSamplingRateInfo(audio_info);
|
AddAudioSamplingRateInfo(audio_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RepresentationXmlNode::AddVODOnlyInfo(const MediaInfo& media_info) {
|
bool RepresentationXmlNode::AddVODOnlyInfo(const MediaInfo& media_info,
|
||||||
const bool use_segment_list_text =
|
bool use_segment_list,
|
||||||
|
double target_segment_duration) {
|
||||||
|
const bool use_single_segment_url_with_media =
|
||||||
media_info.has_text_info() && media_info.has_presentation_time_offset();
|
media_info.has_text_info() && media_info.has_presentation_time_offset();
|
||||||
|
|
||||||
if (media_info.has_media_file_url() && !use_segment_list_text) {
|
if (media_info.has_media_file_url() && !use_single_segment_url_with_media) {
|
||||||
XmlNode base_url("BaseURL");
|
XmlNode base_url("BaseURL");
|
||||||
base_url.SetContent(media_info.media_file_url());
|
base_url.SetContent(media_info.media_file_url());
|
||||||
|
|
||||||
RCHECK(AddChild(std::move(base_url)));
|
RCHECK(AddChild(std::move(base_url)));
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool need_segment_base =
|
const bool need_segment_base_or_list =
|
||||||
media_info.has_index_range() || media_info.has_init_range() ||
|
use_segment_list || media_info.has_index_range() ||
|
||||||
(media_info.has_reference_time_scale() && !media_info.has_text_info());
|
media_info.has_init_range() ||
|
||||||
DCHECK(!need_segment_base || !use_segment_list_text);
|
(media_info.has_reference_time_scale() && !media_info.has_text_info()) ||
|
||||||
|
use_single_segment_url_with_media;
|
||||||
|
|
||||||
if (need_segment_base || use_segment_list_text) {
|
if (!need_segment_base_or_list) {
|
||||||
XmlNode child(need_segment_base ? "SegmentBase" : "SegmentList");
|
return true;
|
||||||
if (media_info.has_index_range()) {
|
|
||||||
RCHECK(child.SetStringAttribute("indexRange",
|
|
||||||
RangeToString(media_info.index_range())));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (media_info.has_reference_time_scale()) {
|
|
||||||
RCHECK(child.SetIntegerAttribute("timescale",
|
|
||||||
media_info.reference_time_scale()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (media_info.has_presentation_time_offset()) {
|
|
||||||
RCHECK(child.SetIntegerAttribute("presentationTimeOffset",
|
|
||||||
media_info.presentation_time_offset()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (media_info.has_init_range()) {
|
|
||||||
XmlNode initialization("Initialization");
|
|
||||||
RCHECK(initialization.SetStringAttribute(
|
|
||||||
"range", RangeToString(media_info.init_range())));
|
|
||||||
|
|
||||||
RCHECK(child.AddChild(std::move(initialization)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (use_segment_list_text) {
|
|
||||||
XmlNode media_url("SegmentURL");
|
|
||||||
RCHECK(
|
|
||||||
media_url.SetStringAttribute("media", media_info.media_file_url()));
|
|
||||||
RCHECK(child.AddChild(std::move(media_url)));
|
|
||||||
}
|
|
||||||
|
|
||||||
RCHECK(AddChild(std::move(child)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
XmlNode child(use_segment_list || use_single_segment_url_with_media
|
||||||
|
? "SegmentList"
|
||||||
|
: "SegmentBase");
|
||||||
|
|
||||||
|
// Forcing SegmentList for longer audio causes sidx atom to not be
|
||||||
|
// generated, therefore indexRange is not added to MPD if flag is set.
|
||||||
|
if (media_info.has_index_range() && !use_segment_list) {
|
||||||
|
RCHECK(child.SetStringAttribute("indexRange",
|
||||||
|
RangeToString(media_info.index_range())));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (media_info.has_reference_time_scale()) {
|
||||||
|
RCHECK(child.SetIntegerAttribute("timescale",
|
||||||
|
media_info.reference_time_scale()));
|
||||||
|
|
||||||
|
if (use_segment_list && !use_single_segment_url_with_media) {
|
||||||
|
const uint64_t duration_seconds = static_cast<uint64_t>(
|
||||||
|
floor(target_segment_duration * media_info.reference_time_scale()));
|
||||||
|
RCHECK(child.SetIntegerAttribute("duration", duration_seconds));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (media_info.has_presentation_time_offset()) {
|
||||||
|
RCHECK(child.SetIntegerAttribute("presentationTimeOffset",
|
||||||
|
media_info.presentation_time_offset()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (media_info.has_init_range()) {
|
||||||
|
XmlNode initialization("Initialization");
|
||||||
|
RCHECK(initialization.SetStringAttribute(
|
||||||
|
"range", RangeToString(media_info.init_range())));
|
||||||
|
|
||||||
|
RCHECK(child.AddChild(std::move(initialization)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (use_single_segment_url_with_media) {
|
||||||
|
XmlNode media_url("SegmentURL");
|
||||||
|
RCHECK(media_url.SetStringAttribute("media", media_info.media_file_url()));
|
||||||
|
RCHECK(child.AddChild(std::move(media_url)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since the SegmentURLs here do not have a @media element,
|
||||||
|
// BaseURL element is mapped to the @media attribute.
|
||||||
|
if (use_segment_list) {
|
||||||
|
for (const Range& subsegment_range : media_info.subsegment_ranges()) {
|
||||||
|
XmlNode subsegment("SegmentURL");
|
||||||
|
RCHECK(subsegment.SetStringAttribute("mediaRange",
|
||||||
|
RangeToString(subsegment_range)));
|
||||||
|
|
||||||
|
RCHECK(child.AddChild(std::move(subsegment)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RCHECK(AddChild(std::move(child)));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -205,8 +205,15 @@ class RepresentationXmlNode : public RepresentationBaseXmlNode {
|
||||||
/// Adds fields that are specific to VOD. This ignores @a media_info fields
|
/// Adds fields that are specific to VOD. This ignores @a media_info fields
|
||||||
/// for Live.
|
/// for Live.
|
||||||
/// @param media_info is a MediaInfo with VOD information.
|
/// @param media_info is a MediaInfo with VOD information.
|
||||||
|
/// @param use_segment_list is a param that instructs the xml writer to
|
||||||
|
/// use SegmentList instead of SegmentBase.
|
||||||
|
/// @param target_segment_duration is a param that specifies the target
|
||||||
|
// duration of media segments. This is only used when use_segment_list
|
||||||
|
// is true.
|
||||||
/// @return true on success, false otherwise.
|
/// @return true on success, false otherwise.
|
||||||
bool AddVODOnlyInfo(const MediaInfo& media_info) WARN_UNUSED_RESULT;
|
bool AddVODOnlyInfo(const MediaInfo& media_info,
|
||||||
|
bool use_segment_list,
|
||||||
|
double target_segment_duration) WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
/// @param segment_infos is a set of SegmentInfos. This method assumes that
|
/// @param segment_infos is a set of SegmentInfos. This method assumes that
|
||||||
/// SegmentInfos are sorted by its start time.
|
/// SegmentInfos are sorted by its start time.
|
||||||
|
|
|
@ -15,11 +15,13 @@
|
||||||
#include "packager/base/strings/string_util.h"
|
#include "packager/base/strings/string_util.h"
|
||||||
#include "packager/mpd/base/segment_info.h"
|
#include "packager/mpd/base/segment_info.h"
|
||||||
#include "packager/mpd/base/xml/xml_node.h"
|
#include "packager/mpd/base/xml/xml_node.h"
|
||||||
|
#include "packager/mpd/test/mpd_builder_test_helper.h"
|
||||||
#include "packager/mpd/test/xml_compare.h"
|
#include "packager/mpd/test/xml_compare.h"
|
||||||
|
|
||||||
DECLARE_bool(segment_template_constant_duration);
|
DECLARE_bool(segment_template_constant_duration);
|
||||||
DECLARE_bool(dash_add_last_segment_number_when_needed);
|
DECLARE_bool(dash_add_last_segment_number_when_needed);
|
||||||
|
|
||||||
|
|
||||||
using ::testing::ElementsAre;
|
using ::testing::ElementsAre;
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
|
@ -543,5 +545,175 @@ TEST_F(LiveSegmentTimelineTest, LastSegmentNumberSupplementalProperty) {
|
||||||
FLAGS_dash_add_last_segment_number_when_needed = false;
|
FLAGS_dash_add_last_segment_number_when_needed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Creating a separate Test Suite for RepresentationXmlNode::AddVODOnlyInfo
|
||||||
|
class OnDemandVODSegmentTest : public ::testing::Test {
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(OnDemandVODSegmentTest, SegmentBase) {
|
||||||
|
const char kTestMediaInfo[] =
|
||||||
|
"audio_info {\n"
|
||||||
|
" codec: 'mp4a.40.2'\n"
|
||||||
|
" sampling_frequency: 44100\n"
|
||||||
|
" time_scale: 44100\n"
|
||||||
|
" num_channels: 2\n"
|
||||||
|
"}\n"
|
||||||
|
"init_range {\n"
|
||||||
|
" begin: 0\n"
|
||||||
|
" end: 863\n"
|
||||||
|
"}\n"
|
||||||
|
"index_range {\n"
|
||||||
|
" begin: 864\n"
|
||||||
|
" end: 931\n"
|
||||||
|
"}\n"
|
||||||
|
"media_file_url: 'encrypted_audio.mp4'\n"
|
||||||
|
"media_duration_seconds: 24.009434\n"
|
||||||
|
"reference_time_scale: 44100\n"
|
||||||
|
"presentation_time_offset: 100\n";
|
||||||
|
|
||||||
|
const MediaInfo media_info = ConvertToMediaInfo(kTestMediaInfo);
|
||||||
|
|
||||||
|
RepresentationXmlNode representation;
|
||||||
|
ASSERT_TRUE(representation.AddVODOnlyInfo(media_info, false, 100));
|
||||||
|
EXPECT_THAT(representation,
|
||||||
|
XmlNodeEqual("<Representation>"
|
||||||
|
"<BaseURL>encrypted_audio.mp4</BaseURL>"
|
||||||
|
"<SegmentBase indexRange=\"864-931\" "
|
||||||
|
"timescale=\"44100\" presentationTimeOffset=\"100\">"
|
||||||
|
"<Initialization range=\"0-863\"/>"
|
||||||
|
"</SegmentBase>"
|
||||||
|
"</Representation>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(OnDemandVODSegmentTest, TextInfoBaseUrl) {
|
||||||
|
const char kTextMediaInfo[] =
|
||||||
|
"text_info {\n"
|
||||||
|
" codec: 'ttml'\n"
|
||||||
|
" language: 'en'\n"
|
||||||
|
" type: SUBTITLE\n"
|
||||||
|
"}\n"
|
||||||
|
"media_duration_seconds: 35\n"
|
||||||
|
"bandwidth: 1000\n"
|
||||||
|
"media_file_url: 'subtitle.xml'\n"
|
||||||
|
"container_type: CONTAINER_TEXT\n";
|
||||||
|
|
||||||
|
const MediaInfo media_info = ConvertToMediaInfo(kTextMediaInfo);
|
||||||
|
|
||||||
|
RepresentationXmlNode representation;
|
||||||
|
ASSERT_TRUE(representation.AddVODOnlyInfo(media_info, false, 100));
|
||||||
|
EXPECT_THAT(representation, XmlNodeEqual("<Representation>"
|
||||||
|
"<BaseURL>subtitle.xml</BaseURL>"
|
||||||
|
"</Representation>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(OnDemandVODSegmentTest, TextInfoWithPresentationOffset) {
|
||||||
|
const char kTextMediaInfo[] =
|
||||||
|
"text_info {\n"
|
||||||
|
" codec: 'ttml'\n"
|
||||||
|
" language: 'en'\n"
|
||||||
|
" type: SUBTITLE\n"
|
||||||
|
"}\n"
|
||||||
|
"media_duration_seconds: 35\n"
|
||||||
|
"bandwidth: 1000\n"
|
||||||
|
"media_file_url: 'subtitle.xml'\n"
|
||||||
|
"container_type: CONTAINER_TEXT\n"
|
||||||
|
"presentation_time_offset: 100\n";
|
||||||
|
|
||||||
|
const MediaInfo media_info = ConvertToMediaInfo(kTextMediaInfo);
|
||||||
|
|
||||||
|
RepresentationXmlNode representation;
|
||||||
|
ASSERT_TRUE(representation.AddVODOnlyInfo(media_info, false, 100));
|
||||||
|
|
||||||
|
EXPECT_THAT(representation,
|
||||||
|
XmlNodeEqual("<Representation>"
|
||||||
|
"<SegmentList presentationTimeOffset=\"100\">"
|
||||||
|
"<SegmentURL media=\"subtitle.xml\"/>"
|
||||||
|
"</SegmentList>"
|
||||||
|
"</Representation>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(OnDemandVODSegmentTest, SegmentListWithoutUrls) {
|
||||||
|
const char kTestMediaInfo[] =
|
||||||
|
"audio_info {\n"
|
||||||
|
" codec: 'mp4a.40.2'\n"
|
||||||
|
" sampling_frequency: 44100\n"
|
||||||
|
" time_scale: 44100\n"
|
||||||
|
" num_channels: 2\n"
|
||||||
|
"}\n"
|
||||||
|
"init_range {\n"
|
||||||
|
" begin: 0\n"
|
||||||
|
" end: 863\n"
|
||||||
|
"}\n"
|
||||||
|
"index_range {\n"
|
||||||
|
" begin: 864\n"
|
||||||
|
" end: 931\n"
|
||||||
|
"}\n"
|
||||||
|
"media_file_url: 'encrypted_audio.mp4'\n"
|
||||||
|
"media_duration_seconds: 24.009434\n"
|
||||||
|
"reference_time_scale: 44100\n"
|
||||||
|
"presentation_time_offset: 100\n";
|
||||||
|
|
||||||
|
const MediaInfo media_info = ConvertToMediaInfo(kTestMediaInfo);
|
||||||
|
|
||||||
|
RepresentationXmlNode representation;
|
||||||
|
ASSERT_TRUE(representation.AddVODOnlyInfo(media_info, true, 100));
|
||||||
|
|
||||||
|
EXPECT_THAT(
|
||||||
|
representation,
|
||||||
|
XmlNodeEqual("<Representation>"
|
||||||
|
"<BaseURL>encrypted_audio.mp4</BaseURL>"
|
||||||
|
"<SegmentList timescale=\"44100\" duration=\"4410000\" "
|
||||||
|
"presentationTimeOffset=\"100\">"
|
||||||
|
"<Initialization range=\"0-863\"/>"
|
||||||
|
"</SegmentList>"
|
||||||
|
"</Representation>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(OnDemandVODSegmentTest, SegmentUrlWithMediaRanges) {
|
||||||
|
const char kTextMediaInfo[] =
|
||||||
|
"audio_info {\n"
|
||||||
|
" codec: 'mp4a.40.2'\n"
|
||||||
|
" sampling_frequency: 44100\n"
|
||||||
|
" time_scale: 44100\n"
|
||||||
|
" num_channels: 2\n"
|
||||||
|
"}\n"
|
||||||
|
"init_range {\n"
|
||||||
|
" begin: 0\n"
|
||||||
|
" end: 863\n"
|
||||||
|
"}\n"
|
||||||
|
"index_range {\n"
|
||||||
|
" begin: 864\n"
|
||||||
|
" end: 931\n"
|
||||||
|
"}\n"
|
||||||
|
"media_file_url: 'encrypted_audio.mp4'\n"
|
||||||
|
"media_duration_seconds: 24.009434\n"
|
||||||
|
"reference_time_scale: 44100\n"
|
||||||
|
"presentation_time_offset: 100\n"
|
||||||
|
"subsegment_ranges {\n"
|
||||||
|
" begin: 932\n"
|
||||||
|
" end: 9999\n"
|
||||||
|
"}\n"
|
||||||
|
"subsegment_ranges {\n"
|
||||||
|
" begin: 10000\n"
|
||||||
|
" end: 11000\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
const MediaInfo media_info = ConvertToMediaInfo(kTextMediaInfo);
|
||||||
|
|
||||||
|
RepresentationXmlNode representation;
|
||||||
|
ASSERT_TRUE(representation.AddVODOnlyInfo(media_info, true, 100));
|
||||||
|
|
||||||
|
EXPECT_THAT(
|
||||||
|
representation,
|
||||||
|
XmlNodeEqual("<Representation>"
|
||||||
|
"<BaseURL>encrypted_audio.mp4</BaseURL>"
|
||||||
|
"<SegmentList timescale=\"44100\" duration=\"4410000\" "
|
||||||
|
"presentationTimeOffset=\"100\">"
|
||||||
|
"<Initialization range=\"0-863\"/>"
|
||||||
|
"<SegmentURL mediaRange=\"932-9999\"/>"
|
||||||
|
"<SegmentURL mediaRange=\"10000-11000\"/>"
|
||||||
|
"</SegmentList>"
|
||||||
|
"</Representation>"));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace xml
|
} // namespace xml
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
|
@ -87,6 +87,10 @@ struct MpdParams {
|
||||||
/// <ContentProtection ...> element alongside with <cenc:pssh>
|
/// <ContentProtection ...> element alongside with <cenc:pssh>
|
||||||
/// when using PlayReady protection system.
|
/// when using PlayReady protection system.
|
||||||
bool include_mspr_pro = true;
|
bool include_mspr_pro = true;
|
||||||
|
/// Uses SegmentList instead of SegmentBase. Use this if the
|
||||||
|
/// content is huge and the total number of (sub)segment references
|
||||||
|
/// is greater than what the sidx atom allows (65535).
|
||||||
|
bool use_segment_list = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
|
@ -367,10 +367,11 @@ Status ValidateParams(const PackagingParams& packaging_params,
|
||||||
|
|
||||||
if (on_demand_dash_profile &&
|
if (on_demand_dash_profile &&
|
||||||
!packaging_params.mpd_params.mpd_output.empty() &&
|
!packaging_params.mpd_params.mpd_output.empty() &&
|
||||||
!packaging_params.mp4_output_params.generate_sidx_in_media_segments) {
|
!packaging_params.mp4_output_params.generate_sidx_in_media_segments &&
|
||||||
|
!packaging_params.mpd_params.use_segment_list) {
|
||||||
return Status(error::UNIMPLEMENTED,
|
return Status(error::UNIMPLEMENTED,
|
||||||
"--generate_sidx_in_media_segments is required for DASH "
|
"--generate_sidx_in_media_segments is required for DASH "
|
||||||
"on-demand profile (not using segment_template).");
|
"on-demand profile (not using segment_template or segment list).");
|
||||||
}
|
}
|
||||||
|
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
|
@ -937,8 +938,9 @@ Status Packager::Initialize(
|
||||||
}
|
}
|
||||||
|
|
||||||
media::MuxerListenerFactory muxer_listener_factory(
|
media::MuxerListenerFactory muxer_listener_factory(
|
||||||
packaging_params.output_media_info, internal->mpd_notifier.get(),
|
packaging_params.output_media_info,
|
||||||
internal->hls_notifier.get());
|
packaging_params.mpd_params.use_segment_list,
|
||||||
|
internal->mpd_notifier.get(), internal->hls_notifier.get());
|
||||||
|
|
||||||
RETURN_IF_ERROR(media::CreateAllJobs(
|
RETURN_IF_ERROR(media::CreateAllJobs(
|
||||||
streams_for_jobs, packaging_params, internal->mpd_notifier.get(),
|
streams_for_jobs, packaging_params, internal->mpd_notifier.get(),
|
||||||
|
|
Loading…
Reference in New Issue