Update cmd flags to support static mpd with live profile

- Deprecated command line flags --profile and --single_segment.
  'segment_template' in stream descriptors implies live profile
  and non-single segment.
- Added flag --generate_static_mpd_for_live_profile to generate
  static mpd for live profile; if not set, dynamic mpd will be
  generated.

Close #142

Change-Id: I78879297ed118f0f246c4753a16ad125bd6b5e4f
This commit is contained in:
Kongqun Yang 2017-01-06 18:40:37 -08:00 committed by KongQun Yang
parent 5aaae303e8
commit 80c54a533a
24 changed files with 137 additions and 134 deletions

View File

@ -145,7 +145,7 @@ host operating systems.
# VOD: mp4 --> dash # VOD: mp4 --> dash
packager input=/media/example.mp4,stream=audio,output=audio.mp4 \ packager input=/media/example.mp4,stream=audio,output=audio.mp4 \
input=/media/example.mp4,stream=video,output=video.mp4 \ input=/media/example.mp4,stream=video,output=video.mp4 \
--profile on-demand --mpd_output example.mpd --mpd_output example.mpd
# Leave the container. # Leave the container.
exit exit
@ -231,13 +231,11 @@ Demux audio from the input and generate a fragmented mp4:
packager input=sintel.mp4,stream=audio,output=fragmented_sintel.mp4 packager input=sintel.mp4,stream=audio,output=fragmented_sintel.mp4
``` ```
Demux streams from the input and generates a mpd with on-demand profile along Demux streams from the input and generates a mpd with fragmented mp4:
with fragmented mp4:
```Shell ```Shell
packager \ packager \
input=sintel.mp4,stream=audio,output=sintel_audio.mp4 \ input=sintel.mp4,stream=audio,output=sintel_audio.mp4 \
input=sintel.mp4,stream=video,output=sintel_video.mp4 \ input=sintel.mp4,stream=video,output=sintel_video.mp4 \
--profile on-demand \
--mpd_output sintel_vod.mpd --mpd_output sintel_vod.mpd
``` ```
@ -247,19 +245,17 @@ packager \
input=sintel.mp4,stream=audio,output=sintel_audio.mp4 \ input=sintel.mp4,stream=audio,output=sintel_audio.mp4 \
input=sintel.mp4,stream=video,output=sintel_video.mp4 \ input=sintel.mp4,stream=video,output=sintel_video.mp4 \
input=sintel_english_input.vtt,stream=text,output=sintel_english.vtt \ input=sintel_english_input.vtt,stream=text,output=sintel_english.vtt \
--profile on-demand \
--mpd_output sintel_vod.mpd --mpd_output sintel_vod.mpd
``` ```
You may also generate mpd with live profile. Here is an example with IPTV input You may also generate mpd with live profile by specifying segment_template in
streams: stream descriptors. Here is an example with IPTV input streams:
```Shell ```Shell
packager \ packager \
'input=udp://224.1.1.5:5003,stream=audio,init_segment=live-audio.mp4,segment_template=live-audio-$Number$.mp4,bandwidth=130000' \ 'input=udp://224.1.1.5:5003,stream=audio,init_segment=live-audio.mp4,segment_template=live-audio-$Number$.mp4,bandwidth=130000' \
'input=udp://224.1.1.5:5003,stream=video,init_segment=live-video-sd.mp4,segment_template=live-video-sd-$Number$.mp4,bandwidth=2000000' \ 'input=udp://224.1.1.5:5003,stream=video,init_segment=live-video-sd.mp4,segment_template=live-video-sd-$Number$.mp4,bandwidth=2000000' \
'input=udp://224.1.1.5:5002,stream=video,init_segment=live-video-hd.mp4,segment_template=live-video-hd-$Number$.mp4,bandwidth=5000000' \ 'input=udp://224.1.1.5:5002,stream=video,init_segment=live-video-hd.mp4,segment_template=live-video-hd-$Number$.mp4,bandwidth=5000000' \
--profile live \
--mpd_output live.mpd --mpd_output live.mpd
``` ```
@ -274,6 +270,16 @@ Three options are supported right now:
- timeout=microseconds - timeout=microseconds
Timeout in microseconds. Default to unlimited. Timeout in microseconds. Default to unlimited.
To generate static mpd with live profile. An additional flag needs to be
specified:
```Shell
packager \
'input=sintel.mp4,stream=audio,init_segment=audio.mp4,segment_template=audio-$Number$.mp4' \
'input=sintel.mp4,stream=video,init_segment=video.mp4,segment_template=video-$Number$.mp4' \
--mpd_output live_static.mpd \
--generate_static_mpd
```
Demux video from the input and generate an encrypted fragmented mp4 using Demux video from the input and generate an encrypted fragmented mp4 using
Widevine encryption with RSA signing key file *widevine_test_private.der*: Widevine encryption with RSA signing key file *widevine_test_private.der*:
```Shell ```Shell
@ -292,7 +298,6 @@ packager \
'input=udp://224.1.1.5:5003,stream=audio,init_segment=live-audio.mp4,segment_template=live-audio-$Number$.mp4,bandwidth=130000' \ 'input=udp://224.1.1.5:5003,stream=audio,init_segment=live-audio.mp4,segment_template=live-audio-$Number$.mp4,bandwidth=130000' \
'input=udp://224.1.1.5:5003,stream=video,init_segment=live-video-sd.mp4,segment_template=live-video-sd-$Number$.mp4,bandwidth=2000000' \ 'input=udp://224.1.1.5:5003,stream=video,init_segment=live-video-sd.mp4,segment_template=live-video-sd-$Number$.mp4,bandwidth=2000000' \
'input=udp://224.1.1.5:5002,stream=video,init_segment=live-video-hd.mp4,segment_template=live-video-hd-$Number$.mp4,bandwidth=5000000' \ 'input=udp://224.1.1.5:5002,stream=video,init_segment=live-video-hd.mp4,segment_template=live-video-hd-$Number$.mp4,bandwidth=5000000' \
--profile live \
--mpd_output live.mpd \ --mpd_output live.mpd \
--enable_widevine_encryption \ --enable_widevine_encryption \
--key_server_url "https://license.uat.widevine.com/cenc/getcontentkey/widevine_test" \ --key_server_url "https://license.uat.widevine.com/cenc/getcontentkey/widevine_test" \
@ -343,7 +348,6 @@ generated TS segments.
```Shell ```Shell
packager \ packager \
'input=bear-1280x720.mp4,stream=video,segment_template=bear$Number$.ts,playlist_name=playlist.m3u8' \ 'input=bear-1280x720.mp4,stream=video,segment_template=bear$Number$.ts,playlist_name=playlist.m3u8' \
--single_segment=false \
--hls_master_playlist_output="master.m3u8" \ --hls_master_playlist_output="master.m3u8" \
--hls_base_url="http://localhost:10000/" --hls_base_url="http://localhost:10000/"
``` ```
@ -354,7 +358,6 @@ specified.
packager \ packager \
'input=input.mp4,stream=video,segment_template=output$Number$.ts,playlist_name=video_playlist.m3u8' \ 'input=input.mp4,stream=video,segment_template=output$Number$.ts,playlist_name=video_playlist.m3u8' \
'input=input.mp4,stream=audio,segment_template=output_audio$Number$.ts,playlist_name=audio_playlist.m3u8,hls_group_id=audio,hls_name=ENGLISH' \ 'input=input.mp4,stream=audio,segment_template=output_audio$Number$.ts,playlist_name=audio_playlist.m3u8,hls_group_id=audio,hls_name=ENGLISH' \
--single_segment=false \
--hls_master_playlist_output="master_playlist.m3u8" \ --hls_master_playlist_output="master_playlist.m3u8" \
--hls_base_url="http://localhost:10000/" --hls_base_url="http://localhost:10000/"
``` ```

View File

@ -8,6 +8,14 @@
#include "packager/app/mpd_flags.h" #include "packager/app/mpd_flags.h"
DEFINE_bool(generate_static_mpd,
false,
"Set to true to generate static mpd. If segment_template is "
"specified in stream descriptors, shaka-packager generates dynamic "
"mpd by default; if this flag is enabled, shaka-packager generates "
"static mpd instead. Note that if segment_template is not "
"specified, shaka-packager always generates static mpd regardless "
"of the value of this flag.");
// TODO(rkuroiwa, kqyang): Remove the 'Exclusive' statements once // TODO(rkuroiwa, kqyang): Remove the 'Exclusive' statements once
// --output_media_info can work together with --mpd_output. // --output_media_info can work together with --mpd_output.
DEFINE_bool(output_media_info, DEFINE_bool(output_media_info,

View File

@ -11,6 +11,7 @@
#include <gflags/gflags.h> #include <gflags/gflags.h>
DECLARE_bool(generate_static_mpd);
DECLARE_bool(output_media_info); DECLARE_bool(output_media_info);
DECLARE_string(mpd_output); DECLARE_string(mpd_output);
DECLARE_string(base_urls); DECLARE_string(base_urls);

View File

@ -8,18 +8,9 @@
#include "packager/app/muxer_flags.h" #include "packager/app/muxer_flags.h"
DEFINE_string(profile,
"",
"Specify the target DASH profile: on-demand or live. This will "
"set proper option values to ensure conformance to the desired "
"profile.");
DEFINE_double(clear_lead, DEFINE_double(clear_lead,
10.0f, 10.0f,
"Clear lead in seconds if encryption is enabled."); "Clear lead in seconds if encryption is enabled.");
DEFINE_bool(single_segment,
true,
"Generate a single segment for the media presentation. This option "
"should be set for on demand profile.");
DEFINE_double(segment_duration, DEFINE_double(segment_duration,
10.0f, 10.0f,
"Segment duration in seconds. If single_segment is specified, " "Segment duration in seconds. If single_segment is specified, "

View File

@ -11,9 +11,7 @@
#include <gflags/gflags.h> #include <gflags/gflags.h>
DECLARE_string(profile);
DECLARE_double(clear_lead); DECLARE_double(clear_lead);
DECLARE_bool(single_segment);
DECLARE_double(segment_duration); DECLARE_double(segment_duration);
DECLARE_bool(segment_sap_aligned); DECLARE_bool(segment_sap_aligned);
DECLARE_double(fragment_duration); DECLARE_double(fragment_duration);

View File

@ -265,11 +265,6 @@ bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors,
return false; return false;
} }
stream_muxer_options.segment_template = stream_iter->segment_template; stream_muxer_options.segment_template = stream_iter->segment_template;
if (stream_muxer_options.single_segment) {
LOG(WARNING) << "Segment template and single segment are incompatible, "
"setting single segment to false.";
stream_muxer_options.single_segment = false;
}
} }
stream_muxer_options.bandwidth = stream_iter->bandwidth; stream_muxer_options.bandwidth = stream_iter->bandwidth;
@ -419,20 +414,11 @@ bool RunPackager(const StreamDescriptorList& stream_descriptors) {
if (protection_scheme == FOURCC_NULL) if (protection_scheme == FOURCC_NULL)
return false; return false;
if (!AssignFlagsFromProfile())
return false;
if (FLAGS_output_media_info && !FLAGS_mpd_output.empty()) { if (FLAGS_output_media_info && !FLAGS_mpd_output.empty()) {
NOTIMPLEMENTED() << "ERROR: --output_media_info and --mpd_output do not " NOTIMPLEMENTED() << "ERROR: --output_media_info and --mpd_output do not "
"work together."; "work together.";
return false; return false;
} }
if (FLAGS_output_media_info && !FLAGS_single_segment) {
// TODO(rkuroiwa, kqyang): Support partial media info dump for live.
NOTIMPLEMENTED() << "ERROR: --output_media_info is only supported if "
"--single_segment is true.";
return false;
}
// Since there isn't a muxer listener that can output both MPD and HLS, // Since there isn't a muxer listener that can output both MPD and HLS,
// disallow specifying both MPD and HLS flags. // disallow specifying both MPD and HLS flags.
@ -446,8 +432,28 @@ bool RunPackager(const StreamDescriptorList& stream_descriptors) {
if (!GetMuxerOptions(&muxer_options)) if (!GetMuxerOptions(&muxer_options))
return false; return false;
DCHECK(!stream_descriptors.empty());
// On demand profile generates single file segment while live profile
// generates multiple segments specified using segment template.
const bool on_demand_dash_profile =
stream_descriptors.begin()->segment_template.empty();
for (const auto& stream_descriptor : stream_descriptors) {
if (on_demand_dash_profile != stream_descriptor.segment_template.empty()) {
LOG(ERROR) << "Inconsistent stream descriptor specification: "
"segment_template should be specified for none or all "
"stream descriptors.";
return false;
}
}
if (FLAGS_output_media_info && !on_demand_dash_profile) {
// TODO(rkuroiwa, kqyang): Support partial media info dump for live.
NOTIMPLEMENTED() << "ERROR: --output_media_info is only supported for "
"on-demand profile (not using segment_template).";
return false;
}
MpdOptions mpd_options; MpdOptions mpd_options;
if (!GetMpdOptions(&mpd_options)) if (!GetMpdOptions(on_demand_dash_profile, &mpd_options))
return false; return false;
// Create encryption key source if needed. // Create encryption key source if needed.

View File

@ -125,30 +125,9 @@ std::unique_ptr<KeySource> CreateDecryptionKeySource() {
return decryption_key_source; return decryption_key_source;
} }
bool AssignFlagsFromProfile() {
bool single_segment = FLAGS_single_segment;
if (FLAGS_profile == "on-demand") {
single_segment = true;
} else if (FLAGS_profile == "live") {
single_segment = false;
} else if (FLAGS_profile != "") {
fprintf(stderr, "ERROR: --profile '%s' is not supported.\n",
FLAGS_profile.c_str());
return false;
}
if (FLAGS_single_segment != single_segment) {
FLAGS_single_segment = single_segment;
fprintf(stdout, "Profile %s: set --single_segment to %s.\n",
FLAGS_profile.c_str(), single_segment ? "true" : "false");
}
return true;
}
bool GetMuxerOptions(MuxerOptions* muxer_options) { bool GetMuxerOptions(MuxerOptions* muxer_options) {
DCHECK(muxer_options); DCHECK(muxer_options);
muxer_options->single_segment = FLAGS_single_segment;
muxer_options->segment_duration = FLAGS_segment_duration; muxer_options->segment_duration = FLAGS_segment_duration;
muxer_options->fragment_duration = FLAGS_fragment_duration; muxer_options->fragment_duration = FLAGS_fragment_duration;
muxer_options->segment_sap_aligned = FLAGS_segment_sap_aligned; muxer_options->segment_sap_aligned = FLAGS_segment_sap_aligned;
@ -168,15 +147,15 @@ bool GetMuxerOptions(MuxerOptions* muxer_options) {
return true; return true;
} }
bool GetMpdOptions(MpdOptions* mpd_options) { bool GetMpdOptions(bool on_demand_profile, MpdOptions* mpd_options) {
DCHECK(mpd_options); DCHECK(mpd_options);
mpd_options->dash_profile = mpd_options->dash_profile =
FLAGS_single_segment ? DashProfile::kOnDemand : DashProfile::kLive; on_demand_profile ? DashProfile::kOnDemand : DashProfile::kLive;
// Single segment does not always mean static mpd.
// TODO(kqyang): Add a new flag for mpd type and update the code.
mpd_options->mpd_type = mpd_options->mpd_type =
FLAGS_single_segment ? MpdType::kStatic : MpdType::kDynamic; (on_demand_profile || FLAGS_generate_static_mpd)
? MpdType::kStatic
: MpdType::kDynamic;
mpd_options->availability_time_offset = FLAGS_availability_time_offset; mpd_options->availability_time_offset = FLAGS_availability_time_offset;
mpd_options->minimum_update_period = FLAGS_minimum_update_period; mpd_options->minimum_update_period = FLAGS_minimum_update_period;
mpd_options->min_buffer_time = FLAGS_min_buffer_time; mpd_options->min_buffer_time = FLAGS_min_buffer_time;

View File

@ -42,14 +42,11 @@ std::unique_ptr<KeySource> CreateEncryptionKeySource();
/// decryption is not required. /// decryption is not required.
std::unique_ptr<KeySource> CreateDecryptionKeySource(); std::unique_ptr<KeySource> CreateDecryptionKeySource();
/// Set flags according to profile.
bool AssignFlagsFromProfile();
/// Fill MuxerOptions members using provided command line options. /// Fill MuxerOptions members using provided command line options.
bool GetMuxerOptions(MuxerOptions* muxer_options); bool GetMuxerOptions(MuxerOptions* muxer_options);
/// Fill MpdOptions members using provided command line options. /// Fill MpdOptions members using provided command line options.
bool GetMpdOptions(MpdOptions* mpd_options); bool GetMpdOptions(bool on_demand_profile, MpdOptions* mpd_options);
/// Select and add a stream from a provided set to a muxer. /// Select and add a stream from a provided set to a muxer.
/// @param streams contains the set of MediaStreams from which to select. /// @param streams contains the set of MediaStreams from which to select.

View File

@ -0,0 +1,34 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
//
// Defines retired / deprecated flags. These flags will be removed in later
// versions.
#include "packager/app/retired_flags.h"
#include <stdio.h>
DEFINE_string(profile, "", "This flag is deprecated. Do not use.");
DEFINE_bool(single_segment, true, "This flag is deprecated. Do not use.");
// The current gflags library does not provide a way to check whether a flag is
// set in command line. If a flag has a different value to its default value,
// the flag must have been set. It is possible that the flag is set to the same
// value as its default value though.
bool InformRetiredStringFlag(const char* flagname, const std::string& value) {
if (!value.empty())
fprintf(stderr, "WARNING: %s is deprecated and ignored.\n", flagname);
return true;
}
bool InformRetiredDefaultTrueFlag(const char* flagname, bool value) {
if (!value)
fprintf(stderr, "WARNING: %s is deprecated and ignored.\n", flagname);
return true;
}
DEFINE_validator(profile, &InformRetiredStringFlag);
DEFINE_validator(single_segment, &InformRetiredDefaultTrueFlag);

View File

@ -0,0 +1,10 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include <gflags/gflags.h>
DECLARE_string(profile);
DECLARE_bool(single_segment);

View File

@ -110,7 +110,7 @@ class PackagerAppTest(unittest.TestCase):
output_format='ts', output_format='ts',
live=True, live=True,
test_files=['bear-640x360.ts']), test_files=['bear-640x360.ts']),
self._GetFlags(live=True, output_hls=True)) self._GetFlags(output_hls=True))
self._DiffLiveGold(self.output[0], self._DiffLiveGold(self.output[0],
'bear-640x360-a-golden', 'bear-640x360-a-golden',
output_format='ts') output_format='ts')
@ -220,8 +220,7 @@ class PackagerAppTest(unittest.TestCase):
output_format='ts', output_format='ts',
live=True, live=True,
test_files=['bear-640x360.ts']), test_files=['bear-640x360.ts']),
self._GetFlags(encryption=True, self._GetFlags(encryption=True, output_hls=True))
live=True, output_hls=True))
self._DiffLiveGold(self.output[0], self._DiffLiveGold(self.output[0],
'bear-640x360-a-enc-golden', 'bear-640x360-a-enc-golden',
output_format='ts') output_format='ts')
@ -329,8 +328,7 @@ class PackagerAppTest(unittest.TestCase):
def testPackageWithLiveProfile(self): def testPackageWithLiveProfile(self):
self.packager.Package( self.packager.Package(
self._GetStreams(['audio', 'video'], live=True), self._GetStreams(['audio', 'video'], live=True), self._GetFlags())
self._GetFlags(live=True))
self._DiffLiveGold(self.output[0], 'bear-640x360-a-live-golden') self._DiffLiveGold(self.output[0], 'bear-640x360-a-live-golden')
self._DiffLiveGold(self.output[1], 'bear-640x360-v-live-golden') self._DiffLiveGold(self.output[1], 'bear-640x360-v-live-golden')
self._DiffLiveMpdGold(self.mpd_output, 'bear-640x360-av-live-golden.mpd') self._DiffLiveMpdGold(self.mpd_output, 'bear-640x360-av-live-golden.mpd')
@ -338,7 +336,7 @@ class PackagerAppTest(unittest.TestCase):
def testPackageWithLiveProfileAndEncryption(self): def testPackageWithLiveProfileAndEncryption(self):
self.packager.Package( self.packager.Package(
self._GetStreams(['audio', 'video'], live=True), self._GetStreams(['audio', 'video'], live=True),
self._GetFlags(encryption=True, live=True)) self._GetFlags(encryption=True))
self._DiffLiveGold(self.output[0], 'bear-640x360-a-live-cenc-golden') self._DiffLiveGold(self.output[0], 'bear-640x360-a-live-cenc-golden')
self._DiffLiveGold(self.output[1], 'bear-640x360-v-live-cenc-golden') self._DiffLiveGold(self.output[1], 'bear-640x360-v-live-cenc-golden')
self._DiffLiveMpdGold(self.mpd_output, self._DiffLiveMpdGold(self.mpd_output,
@ -347,8 +345,7 @@ class PackagerAppTest(unittest.TestCase):
def testPackageWithLiveProfileAndEncryptionAndDashIfIop(self): def testPackageWithLiveProfileAndEncryptionAndDashIfIop(self):
self.packager.Package( self.packager.Package(
self._GetStreams(['audio', 'video'], live=True), self._GetStreams(['audio', 'video'], live=True),
self._GetFlags(encryption=True, self._GetFlags(encryption=True, dash_if_iop=True))
live=True, dash_if_iop=True))
self._DiffLiveGold(self.output[0], 'bear-640x360-a-live-cenc-golden') self._DiffLiveGold(self.output[0], 'bear-640x360-a-live-cenc-golden')
self._DiffLiveGold(self.output[1], 'bear-640x360-v-live-cenc-golden') self._DiffLiveGold(self.output[1], 'bear-640x360-v-live-cenc-golden')
self._DiffLiveMpdGold(self.mpd_output, self._DiffLiveMpdGold(self.mpd_output,
@ -360,8 +357,7 @@ class PackagerAppTest(unittest.TestCase):
live=True, live=True,
test_files=['bear-1280x720.mp4', 'bear-640x360.mp4', test_files=['bear-1280x720.mp4', 'bear-640x360.mp4',
'bear-320x180.mp4']), 'bear-320x180.mp4']),
self._GetFlags(encryption=True, self._GetFlags(encryption=True, dash_if_iop=True))
live=True, dash_if_iop=True))
self._DiffLiveGold(self.output[2], 'bear-640x360-a-live-cenc-golden') self._DiffLiveGold(self.output[2], 'bear-640x360-a-live-cenc-golden')
self._DiffLiveGold(self.output[3], 'bear-640x360-v-live-cenc-golden') self._DiffLiveGold(self.output[3], 'bear-640x360-v-live-cenc-golden')
# Mpd cannot be validated right now since we don't generate determinstic # Mpd cannot be validated right now since we don't generate determinstic
@ -371,9 +367,7 @@ class PackagerAppTest(unittest.TestCase):
def testPackageWithLiveProfileAndKeyRotation(self): def testPackageWithLiveProfileAndKeyRotation(self):
self.packager.Package( self.packager.Package(
self._GetStreams(['audio', 'video'], live=True), self._GetStreams(['audio', 'video'], live=True),
self._GetFlags(encryption=True, self._GetFlags(encryption=True, key_rotation=True))
key_rotation=True,
live=True))
self._DiffLiveGold(self.output[0], self._DiffLiveGold(self.output[0],
'bear-640x360-a-live-cenc-rotation-golden') 'bear-640x360-a-live-cenc-rotation-golden')
self._DiffLiveGold(self.output[1], self._DiffLiveGold(self.output[1],
@ -386,7 +380,6 @@ class PackagerAppTest(unittest.TestCase):
self._GetStreams(['audio', 'video'], live=True), self._GetStreams(['audio', 'video'], live=True),
self._GetFlags(encryption=True, self._GetFlags(encryption=True,
key_rotation=True, key_rotation=True,
live=True,
dash_if_iop=True)) dash_if_iop=True))
self._DiffLiveGold(self.output[0], self._DiffLiveGold(self.output[0],
'bear-640x360-a-live-cenc-rotation-golden') 'bear-640x360-a-live-cenc-rotation-golden')
@ -496,7 +489,6 @@ class PackagerAppTest(unittest.TestCase):
random_iv=False, random_iv=False,
widevine_encryption=False, widevine_encryption=False,
key_rotation=False, key_rotation=False,
live=False,
dash_if_iop=False, dash_if_iop=False,
output_media_info=False, output_media_info=False,
output_hls=False, output_hls=False,
@ -526,8 +518,6 @@ class PackagerAppTest(unittest.TestCase):
if key_rotation: if key_rotation:
flags.append('--crypto_period_duration=1') flags.append('--crypto_period_duration=1')
if live:
flags.append('--profile=live')
if dash_if_iop: if dash_if_iop:
flags.append('--generate_dash_if_iop_compliant_mpd') flags.append('--generate_dash_if_iop_compliant_mpd')
if output_media_info: if output_media_info:

View File

@ -9,17 +9,8 @@
namespace shaka { namespace shaka {
namespace media { namespace media {
MuxerOptions::MuxerOptions() MuxerOptions::MuxerOptions() = default;
: single_segment(false), MuxerOptions::~MuxerOptions() = default;
segment_duration(0),
fragment_duration(0),
segment_sap_aligned(false),
fragment_sap_aligned(false),
num_subsegments_per_sidx(0),
mp4_use_decoding_timestamp_in_timeline(false),
bandwidth(0),
webm_subsample_encryption(true) {}
MuxerOptions::~MuxerOptions() {}
} // namespace media } // namespace media
} // namespace shaka } // namespace shaka

View File

@ -19,42 +19,38 @@ struct MuxerOptions {
MuxerOptions(); MuxerOptions();
~MuxerOptions(); ~MuxerOptions();
/// Generate a single segment for each media presentation. This option
/// should be set for on demand profile.
bool single_segment;
/// Segment duration in seconds. If single_segment is specified, this /// Segment duration in seconds. If single_segment is specified, this
/// parameter sets the duration of a subsegment; otherwise, this parameter /// parameter sets the duration of a subsegment; otherwise, this parameter
/// sets the duration of a segment. A segment can contain one or many /// sets the duration of a segment. A segment can contain one or many
/// fragments. /// fragments.
double segment_duration; double segment_duration = 0;
/// Fragment duration in seconds. Should not be larger than the segment /// Fragment duration in seconds. Should not be larger than the segment
/// duration. /// duration.
double fragment_duration; double fragment_duration = 0;
/// Force segments to begin with stream access points. Segment duration may /// Force segments to begin with stream access points. Segment duration may
/// not be exactly what specified by segment_duration. /// not be exactly what specified by segment_duration.
bool segment_sap_aligned; bool segment_sap_aligned = false;
/// Force fragments to begin with stream access points. Fragment duration /// Force fragments to begin with stream access points. Fragment duration
/// may not be exactly what specified by segment_duration. Setting to true /// may not be exactly what specified by segment_duration. Setting to true
/// implies that segment_sap_aligned is true as well. /// implies that segment_sap_aligned is true as well.
bool fragment_sap_aligned; bool fragment_sap_aligned = false;
/// For ISO BMFF only. /// For ISO BMFF only.
/// Set the number of subsegments in each SIDX box. If 0, a single SIDX box /// Set the number of subsegments in each SIDX box. If 0, a single SIDX box
/// is used per segment. If -1, no SIDX box is used. Otherwise, the Muxer /// is used per segment. If -1, no SIDX box is used. Otherwise, the Muxer
/// will pack N subsegments in the root SIDX of the segment, with /// will pack N subsegments in the root SIDX of the segment, with
/// segment_duration/N/fragment_duration fragments per subsegment. /// segment_duration/N/fragment_duration fragments per subsegment.
int num_subsegments_per_sidx; int num_subsegments_per_sidx = 0;
/// For ISO BMFF only. /// For ISO BMFF only.
/// Set the flag use_decoding_timestamp_in_timeline, which if set to true, use /// Set the flag use_decoding_timestamp_in_timeline, which if set to true, use
/// decoding timestamp instead of presentation timestamp in media timeline, /// decoding timestamp instead of presentation timestamp in media timeline,
/// which is needed to workaround a Chromium bug that decoding timestamp is /// which is needed to workaround a Chromium bug that decoding timestamp is
/// used in buffered range, https://crbug.com/398130. /// used in buffered range, https://crbug.com/398130.
bool mp4_use_decoding_timestamp_in_timeline; bool mp4_use_decoding_timestamp_in_timeline = false;
/// Output file name. If segment_template is not specified, the Muxer /// Output file name. If segment_template is not specified, the Muxer
/// generates this single output file with all segments concatenated; /// generates this single output file with all segments concatenated;
@ -72,10 +68,10 @@ struct MuxerOptions {
/// User-specified bit rate for the media stream. If zero, the muxer will /// User-specified bit rate for the media stream. If zero, the muxer will
/// attempt to estimate. /// attempt to estimate.
uint32_t bandwidth; uint32_t bandwidth = 0;
// Enable/disable subsample encryption for WebM containers. // Enable/disable subsample encryption for WebM containers.
bool webm_subsample_encryption; bool webm_subsample_encryption = true;
}; };
} // namespace media } // namespace media

View File

@ -44,7 +44,6 @@ MediaInfo ConvertToMediaInfo(const std::string& media_info_string) {
} }
void SetDefaultLiveMuxerOptionsValues(media::MuxerOptions* muxer_options) { void SetDefaultLiveMuxerOptionsValues(media::MuxerOptions* muxer_options) {
muxer_options->single_segment = false;
muxer_options->segment_duration = 10.0; muxer_options->segment_duration = 10.0;
muxer_options->fragment_duration = 10.0; muxer_options->fragment_duration = 10.0;
muxer_options->segment_sap_aligned = true; muxer_options->segment_sap_aligned = true;

View File

@ -149,9 +149,8 @@ void SetMediaInfoStreamInfo(const StreamInfo& stream_info,
void SetMediaInfoMuxerOptions(const MuxerOptions& muxer_options, void SetMediaInfoMuxerOptions(const MuxerOptions& muxer_options,
MediaInfo* media_info) { MediaInfo* media_info) {
DCHECK(media_info); DCHECK(media_info);
if (muxer_options.single_segment) { if (muxer_options.segment_template.empty()) {
media_info->set_media_file_name(muxer_options.output_file_name); media_info->set_media_file_name(muxer_options.output_file_name);
DCHECK(muxer_options.segment_template.empty());
} else { } else {
media_info->set_init_segment_name(muxer_options.output_file_name); media_info->set_init_segment_name(muxer_options.output_file_name);
media_info->set_segment_template(muxer_options.segment_template); media_info->set_segment_template(muxer_options.segment_template);

View File

@ -73,7 +73,6 @@ OnMediaEndParameters GetDefaultOnMediaEndParams() {
} }
void SetDefaultMuxerOptionsValues(MuxerOptions* muxer_options) { void SetDefaultMuxerOptionsValues(MuxerOptions* muxer_options) {
muxer_options->single_segment = true;
muxer_options->segment_duration = 10.0; muxer_options->segment_duration = 10.0;
muxer_options->fragment_duration = 10.0; muxer_options->fragment_duration = 10.0;
muxer_options->segment_sap_aligned = true; muxer_options->segment_sap_aligned = true;

View File

@ -45,7 +45,7 @@ void VodMediaInfoDumpMuxerListener::OnMediaStart(
const StreamInfo& stream_info, const StreamInfo& stream_info,
uint32_t time_scale, uint32_t time_scale,
ContainerType container_type) { ContainerType container_type) {
DCHECK(muxer_options.single_segment); DCHECK(muxer_options.segment_template.empty());
media_info_.reset(new MediaInfo()); media_info_.reset(new MediaInfo());
if (!internal::GenerateMediaInfo(muxer_options, if (!internal::GenerateMediaInfo(muxer_options,
stream_info, stream_info,

View File

@ -134,7 +134,7 @@ Status MP4Muxer::Initialize() {
} }
} }
if (options().single_segment) { if (options().segment_template.empty()) {
segmenter_.reset(new SingleSegmentSegmenter(options(), std::move(ftyp), segmenter_.reset(new SingleSegmentSegmenter(options(), std::move(ftyp),
std::move(moov))); std::move(moov)));
} else { } else {

View File

@ -6,6 +6,7 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <memory> #include <memory>
#include "packager/media/base/muxer_util.h"
#include "packager/media/formats/webm/segmenter_test_base.h" #include "packager/media/formats/webm/segmenter_test_base.h"
namespace shaka { namespace shaka {
@ -27,12 +28,10 @@ const uint8_t kBasicSupportDataInit[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// ID: Info, Payload Size: 88 // ID: Info, Payload Size: 81
0x15, 0x49, 0xa9, 0x66, 0xd8, 0x15, 0x49, 0xa9, 0x66, 0xd1,
// TimecodeScale: 1000000 // TimecodeScale: 1000000
0x2a, 0xd7, 0xb1, 0x83, 0x0f, 0x42, 0x40, 0x2a, 0xd7, 0xb1, 0x83, 0x0f, 0x42, 0x40,
// Duration: float(0)
0x44, 0x89, 0x84, 0x3f, 0x80, 0x00, 0x00,
// MuxingApp: 'libwebm-0.2.1.0' // MuxingApp: 'libwebm-0.2.1.0'
0x4d, 0x80, 0x8f, 0x6c, 0x69, 0x62, 0x77, 0x65, 0x62, 0x6d, 0x2d, 0x30, 0x4d, 0x80, 0x8f, 0x6c, 0x69, 0x62, 0x77, 0x65, 0x62, 0x6d, 0x2d, 0x30,
0x2e, 0x32, 0x2e, 0x31, 0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x31, 0x2e, 0x30,
@ -93,7 +92,10 @@ const uint8_t kBasicSupportDataSegment[] = {
class MultiSegmentSegmenterTest : public SegmentTestBase { class MultiSegmentSegmenterTest : public SegmentTestBase {
public: public:
MultiSegmentSegmenterTest() : info_(CreateVideoStreamInfo()) {} MultiSegmentSegmenterTest()
: info_(CreateVideoStreamInfo()),
segment_template_(std::string(kMemoryFilePrefix) +
"output-template-$Number$.webm") {}
protected: protected:
void InitializeSegmenter(const MuxerOptions& options) { void InitializeSegmenter(const MuxerOptions& options) {
@ -102,12 +104,18 @@ class MultiSegmentSegmenterTest : public SegmentTestBase {
options, info_.get(), NULL, &segmenter_)); options, info_.get(), NULL, &segmenter_));
} }
std::string TemplateFileName(int number) const {
return GetSegmentName(segment_template_, 0, number, 0);
}
scoped_refptr<StreamInfo> info_; scoped_refptr<StreamInfo> info_;
std::string segment_template_;
std::unique_ptr<webm::Segmenter> segmenter_; std::unique_ptr<webm::Segmenter> segmenter_;
}; };
TEST_F(MultiSegmentSegmenterTest, BasicSupport) { TEST_F(MultiSegmentSegmenterTest, BasicSupport) {
MuxerOptions options = CreateMuxerOptions(); MuxerOptions options = CreateMuxerOptions();
options.segment_template = segment_template_;
ASSERT_NO_FATAL_FAILURE(InitializeSegmenter(options)); ASSERT_NO_FATAL_FAILURE(InitializeSegmenter(options));
// Write the samples to the Segmenter. // Write the samples to the Segmenter.
@ -128,6 +136,7 @@ TEST_F(MultiSegmentSegmenterTest, BasicSupport) {
TEST_F(MultiSegmentSegmenterTest, SplitsFilesOnSegmentDuration) { TEST_F(MultiSegmentSegmenterTest, SplitsFilesOnSegmentDuration) {
MuxerOptions options = CreateMuxerOptions(); MuxerOptions options = CreateMuxerOptions();
options.segment_template = segment_template_;
options.segment_duration = 5; // seconds options.segment_duration = 5; // seconds
ASSERT_NO_FATAL_FAILURE(InitializeSegmenter(options)); ASSERT_NO_FATAL_FAILURE(InitializeSegmenter(options));
@ -154,6 +163,7 @@ TEST_F(MultiSegmentSegmenterTest, SplitsFilesOnSegmentDuration) {
TEST_F(MultiSegmentSegmenterTest, RespectsSegmentSAPAlign) { TEST_F(MultiSegmentSegmenterTest, RespectsSegmentSAPAlign) {
MuxerOptions options = CreateMuxerOptions(); MuxerOptions options = CreateMuxerOptions();
options.segment_template = segment_template_;
options.segment_duration = 3; // seconds options.segment_duration = 3; // seconds
options.segment_sap_aligned = true; options.segment_sap_aligned = true;
ASSERT_NO_FATAL_FAILURE(InitializeSegmenter(options)); ASSERT_NO_FATAL_FAILURE(InitializeSegmenter(options));
@ -182,6 +192,7 @@ TEST_F(MultiSegmentSegmenterTest, RespectsSegmentSAPAlign) {
TEST_F(MultiSegmentSegmenterTest, SplitsClustersOnFragmentDuration) { TEST_F(MultiSegmentSegmenterTest, SplitsClustersOnFragmentDuration) {
MuxerOptions options = CreateMuxerOptions(); MuxerOptions options = CreateMuxerOptions();
options.segment_template = segment_template_;
options.fragment_duration = 5; // seconds options.fragment_duration = 5; // seconds
ASSERT_NO_FATAL_FAILURE(InitializeSegmenter(options)); ASSERT_NO_FATAL_FAILURE(InitializeSegmenter(options));
@ -205,6 +216,7 @@ TEST_F(MultiSegmentSegmenterTest, SplitsClustersOnFragmentDuration) {
TEST_F(MultiSegmentSegmenterTest, RespectsFragmentSAPAlign) { TEST_F(MultiSegmentSegmenterTest, RespectsFragmentSAPAlign) {
MuxerOptions options = CreateMuxerOptions(); MuxerOptions options = CreateMuxerOptions();
options.segment_template = segment_template_;
options.fragment_duration = 3; // seconds options.fragment_duration = 3; // seconds
options.fragment_sap_aligned = true; options.fragment_sap_aligned = true;
ASSERT_NO_FATAL_FAILURE(InitializeSegmenter(options)); ASSERT_NO_FATAL_FAILURE(InitializeSegmenter(options));
@ -230,4 +242,3 @@ TEST_F(MultiSegmentSegmenterTest, RespectsFragmentSAPAlign) {
} // namespace media } // namespace media
} // namespace shaka } // namespace shaka

View File

@ -74,7 +74,7 @@ Status Segmenter::Initialize(std::unique_ptr<MkvWriter> writer,
(GetPackagerProjectUrl() + " version " + version).c_str()); (GetPackagerProjectUrl() + " version " + version).c_str());
} }
if (options().single_segment) { if (options().segment_template.empty()) {
// Set an initial duration so the duration element is written; will be // Set an initial duration so the duration element is written; will be
// overwritten at the end. This works because this is a float and floats // overwritten at the end. This works because this is a float and floats
// are always the same size. // are always the same size.

View File

@ -6,7 +6,6 @@
#include "packager/media/formats/webm/segmenter_test_base.h" #include "packager/media/formats/webm/segmenter_test_base.h"
#include "packager/media/base/muxer_util.h"
#include "packager/media/file/memory_file.h" #include "packager/media/file/memory_file.h"
#include "packager/media/formats/webm/webm_constants.h" #include "packager/media/formats/webm/webm_constants.h"
#include "packager/version/version.h" #include "packager/version/version.h"
@ -43,10 +42,7 @@ void SegmentTestBase::SetUp() {
SetPackagerVersionForTesting("test"); SetPackagerVersionForTesting("test");
output_file_name_ = std::string(kMemoryFilePrefix) + "output-file.webm"; output_file_name_ = std::string(kMemoryFilePrefix) + "output-file.webm";
segment_template_ =
std::string(kMemoryFilePrefix) + "output-template-$Number$.webm";
cur_time_timescale_ = 0; cur_time_timescale_ = 0;
single_segment_ = true;
} }
void SegmentTestBase::TearDown() { void SegmentTestBase::TearDown() {
@ -78,9 +74,7 @@ scoped_refptr<MediaSample> SegmentTestBase::CreateSample(
MuxerOptions SegmentTestBase::CreateMuxerOptions() const { MuxerOptions SegmentTestBase::CreateMuxerOptions() const {
MuxerOptions ret; MuxerOptions ret;
ret.single_segment = single_segment_;
ret.output_file_name = output_file_name_; ret.output_file_name = output_file_name_;
ret.segment_template = segment_template_;
ret.segment_duration = 30; // seconds ret.segment_duration = 30; // seconds
ret.fragment_duration = 30; // seconds ret.fragment_duration = 30; // seconds
ret.segment_sap_aligned = false; ret.segment_sap_aligned = false;
@ -102,10 +96,6 @@ std::string SegmentTestBase::OutputFileName() const {
return output_file_name_; return output_file_name_;
} }
std::string SegmentTestBase::TemplateFileName(int number) const {
return GetSegmentName(segment_template_, 0, number, 0);
}
SegmentTestBase::ClusterParser::ClusterParser() : in_cluster_(false) {} SegmentTestBase::ClusterParser::ClusterParser() : in_cluster_(false) {}
SegmentTestBase::ClusterParser::~ClusterParser() {} SegmentTestBase::ClusterParser::~ClusterParser() {}

View File

@ -43,7 +43,7 @@ Status WebMMuxer::Initialize() {
if (!status.ok()) if (!status.ok())
return status; return status;
if (!options().single_segment) { if (!options().segment_template.empty()) {
segmenter_.reset(new MultiSegmentSegmenter(options())); segmenter_.reset(new MultiSegmentSegmenter(options()));
} else { } else {
segmenter_.reset(new TwoPassSingleSegmentSegmenter(options())); segmenter_.reset(new TwoPassSingleSegmentSegmenter(options()));

View File

@ -140,8 +140,6 @@ bool PackagerTestBasic::ContentsEqual(const std::string& file1,
MuxerOptions PackagerTestBasic::SetupOptions(const std::string& output, MuxerOptions PackagerTestBasic::SetupOptions(const std::string& output,
bool single_segment) { bool single_segment) {
MuxerOptions options; MuxerOptions options;
options.single_segment = single_segment;
options.segment_duration = kSegmentDurationInSeconds; options.segment_duration = kSegmentDurationInSeconds;
options.fragment_duration = kFragmentDurationInSecodns; options.fragment_duration = kFragmentDurationInSecodns;
options.segment_sap_aligned = kSegmentSapAligned; options.segment_sap_aligned = kSegmentSapAligned;
@ -149,7 +147,8 @@ MuxerOptions PackagerTestBasic::SetupOptions(const std::string& output,
options.num_subsegments_per_sidx = kNumSubsegmentsPerSidx; options.num_subsegments_per_sidx = kNumSubsegmentsPerSidx;
options.output_file_name = GetFullPath(output); options.output_file_name = GetFullPath(output);
options.segment_template = GetFullPath(kSegmentTemplate); if (!single_segment)
options.segment_template = GetFullPath(kSegmentTemplate);
options.temp_dir = test_directory_.AsUTF8Unsafe(); options.temp_dir = test_directory_.AsUTF8Unsafe();
return options; return options;
} }

View File

@ -26,6 +26,8 @@
'app/packager_main.cc', 'app/packager_main.cc',
'app/packager_util.cc', 'app/packager_util.cc',
'app/packager_util.h', 'app/packager_util.h',
'app/retired_flags.cc',
'app/retired_flags.h',
'app/stream_descriptor.cc', 'app/stream_descriptor.cc',
'app/stream_descriptor.h', 'app/stream_descriptor.h',
'app/validate_flag.cc', 'app/validate_flag.cc',