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:
parent
5aaae303e8
commit
80c54a533a
25
README.md
25
README.md
|
@ -145,7 +145,7 @@ host operating systems.
|
|||
# VOD: mp4 --> dash
|
||||
packager input=/media/example.mp4,stream=audio,output=audio.mp4 \
|
||||
input=/media/example.mp4,stream=video,output=video.mp4 \
|
||||
--profile on-demand --mpd_output example.mpd
|
||||
--mpd_output example.mpd
|
||||
|
||||
# Leave the container.
|
||||
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
|
||||
```
|
||||
|
||||
Demux streams from the input and generates a mpd with on-demand profile along
|
||||
with fragmented mp4:
|
||||
Demux streams from the input and generates a mpd with fragmented mp4:
|
||||
```Shell
|
||||
packager \
|
||||
input=sintel.mp4,stream=audio,output=sintel_audio.mp4 \
|
||||
input=sintel.mp4,stream=video,output=sintel_video.mp4 \
|
||||
--profile on-demand \
|
||||
--mpd_output sintel_vod.mpd
|
||||
```
|
||||
|
||||
|
@ -247,19 +245,17 @@ packager \
|
|||
input=sintel.mp4,stream=audio,output=sintel_audio.mp4 \
|
||||
input=sintel.mp4,stream=video,output=sintel_video.mp4 \
|
||||
input=sintel_english_input.vtt,stream=text,output=sintel_english.vtt \
|
||||
--profile on-demand \
|
||||
--mpd_output sintel_vod.mpd
|
||||
```
|
||||
|
||||
|
||||
You may also generate mpd with live profile. Here is an example with IPTV input
|
||||
streams:
|
||||
You may also generate mpd with live profile by specifying segment_template in
|
||||
stream descriptors. Here is an example with IPTV input streams:
|
||||
```Shell
|
||||
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=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' \
|
||||
--profile live \
|
||||
--mpd_output live.mpd
|
||||
```
|
||||
|
||||
|
@ -274,6 +270,16 @@ Three options are supported right now:
|
|||
- timeout=microseconds
|
||||
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
|
||||
Widevine encryption with RSA signing key file *widevine_test_private.der*:
|
||||
```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=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' \
|
||||
--profile live \
|
||||
--mpd_output live.mpd \
|
||||
--enable_widevine_encryption \
|
||||
--key_server_url "https://license.uat.widevine.com/cenc/getcontentkey/widevine_test" \
|
||||
|
@ -343,7 +348,6 @@ generated TS segments.
|
|||
```Shell
|
||||
packager \
|
||||
'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_base_url="http://localhost:10000/"
|
||||
```
|
||||
|
@ -354,7 +358,6 @@ specified.
|
|||
packager \
|
||||
'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' \
|
||||
--single_segment=false \
|
||||
--hls_master_playlist_output="master_playlist.m3u8" \
|
||||
--hls_base_url="http://localhost:10000/"
|
||||
```
|
||||
|
|
|
@ -8,6 +8,14 @@
|
|||
|
||||
#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
|
||||
// --output_media_info can work together with --mpd_output.
|
||||
DEFINE_bool(output_media_info,
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
DECLARE_bool(generate_static_mpd);
|
||||
DECLARE_bool(output_media_info);
|
||||
DECLARE_string(mpd_output);
|
||||
DECLARE_string(base_urls);
|
||||
|
|
|
@ -8,18 +8,9 @@
|
|||
|
||||
#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,
|
||||
10.0f,
|
||||
"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,
|
||||
10.0f,
|
||||
"Segment duration in seconds. If single_segment is specified, "
|
||||
|
|
|
@ -11,9 +11,7 @@
|
|||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
DECLARE_string(profile);
|
||||
DECLARE_double(clear_lead);
|
||||
DECLARE_bool(single_segment);
|
||||
DECLARE_double(segment_duration);
|
||||
DECLARE_bool(segment_sap_aligned);
|
||||
DECLARE_double(fragment_duration);
|
||||
|
|
|
@ -265,11 +265,6 @@ bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors,
|
|||
return false;
|
||||
}
|
||||
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;
|
||||
|
||||
|
@ -419,20 +414,11 @@ bool RunPackager(const StreamDescriptorList& stream_descriptors) {
|
|||
if (protection_scheme == FOURCC_NULL)
|
||||
return false;
|
||||
|
||||
if (!AssignFlagsFromProfile())
|
||||
return false;
|
||||
|
||||
if (FLAGS_output_media_info && !FLAGS_mpd_output.empty()) {
|
||||
NOTIMPLEMENTED() << "ERROR: --output_media_info and --mpd_output do not "
|
||||
"work together.";
|
||||
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,
|
||||
// disallow specifying both MPD and HLS flags.
|
||||
|
@ -446,8 +432,28 @@ bool RunPackager(const StreamDescriptorList& stream_descriptors) {
|
|||
if (!GetMuxerOptions(&muxer_options))
|
||||
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;
|
||||
if (!GetMpdOptions(&mpd_options))
|
||||
if (!GetMpdOptions(on_demand_dash_profile, &mpd_options))
|
||||
return false;
|
||||
|
||||
// Create encryption key source if needed.
|
||||
|
|
|
@ -125,30 +125,9 @@ std::unique_ptr<KeySource> CreateDecryptionKeySource() {
|
|||
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) {
|
||||
DCHECK(muxer_options);
|
||||
|
||||
muxer_options->single_segment = FLAGS_single_segment;
|
||||
muxer_options->segment_duration = FLAGS_segment_duration;
|
||||
muxer_options->fragment_duration = FLAGS_fragment_duration;
|
||||
muxer_options->segment_sap_aligned = FLAGS_segment_sap_aligned;
|
||||
|
@ -168,15 +147,15 @@ bool GetMuxerOptions(MuxerOptions* muxer_options) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GetMpdOptions(MpdOptions* mpd_options) {
|
||||
bool GetMpdOptions(bool on_demand_profile, MpdOptions* mpd_options) {
|
||||
DCHECK(mpd_options);
|
||||
|
||||
mpd_options->dash_profile =
|
||||
FLAGS_single_segment ? 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.
|
||||
on_demand_profile ? DashProfile::kOnDemand : DashProfile::kLive;
|
||||
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->minimum_update_period = FLAGS_minimum_update_period;
|
||||
mpd_options->min_buffer_time = FLAGS_min_buffer_time;
|
||||
|
|
|
@ -42,14 +42,11 @@ std::unique_ptr<KeySource> CreateEncryptionKeySource();
|
|||
/// decryption is not required.
|
||||
std::unique_ptr<KeySource> CreateDecryptionKeySource();
|
||||
|
||||
/// Set flags according to profile.
|
||||
bool AssignFlagsFromProfile();
|
||||
|
||||
/// Fill MuxerOptions members using provided command line options.
|
||||
bool GetMuxerOptions(MuxerOptions* muxer_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.
|
||||
/// @param streams contains the set of MediaStreams from which to select.
|
||||
|
|
|
@ -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);
|
|
@ -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);
|
|
@ -110,7 +110,7 @@ class PackagerAppTest(unittest.TestCase):
|
|||
output_format='ts',
|
||||
live=True,
|
||||
test_files=['bear-640x360.ts']),
|
||||
self._GetFlags(live=True, output_hls=True))
|
||||
self._GetFlags(output_hls=True))
|
||||
self._DiffLiveGold(self.output[0],
|
||||
'bear-640x360-a-golden',
|
||||
output_format='ts')
|
||||
|
@ -220,8 +220,7 @@ class PackagerAppTest(unittest.TestCase):
|
|||
output_format='ts',
|
||||
live=True,
|
||||
test_files=['bear-640x360.ts']),
|
||||
self._GetFlags(encryption=True,
|
||||
live=True, output_hls=True))
|
||||
self._GetFlags(encryption=True, output_hls=True))
|
||||
self._DiffLiveGold(self.output[0],
|
||||
'bear-640x360-a-enc-golden',
|
||||
output_format='ts')
|
||||
|
@ -329,8 +328,7 @@ class PackagerAppTest(unittest.TestCase):
|
|||
|
||||
def testPackageWithLiveProfile(self):
|
||||
self.packager.Package(
|
||||
self._GetStreams(['audio', 'video'], live=True),
|
||||
self._GetFlags(live=True))
|
||||
self._GetStreams(['audio', 'video'], live=True), self._GetFlags())
|
||||
self._DiffLiveGold(self.output[0], 'bear-640x360-a-live-golden')
|
||||
self._DiffLiveGold(self.output[1], 'bear-640x360-v-live-golden')
|
||||
self._DiffLiveMpdGold(self.mpd_output, 'bear-640x360-av-live-golden.mpd')
|
||||
|
@ -338,7 +336,7 @@ class PackagerAppTest(unittest.TestCase):
|
|||
def testPackageWithLiveProfileAndEncryption(self):
|
||||
self.packager.Package(
|
||||
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[1], 'bear-640x360-v-live-cenc-golden')
|
||||
self._DiffLiveMpdGold(self.mpd_output,
|
||||
|
@ -347,8 +345,7 @@ class PackagerAppTest(unittest.TestCase):
|
|||
def testPackageWithLiveProfileAndEncryptionAndDashIfIop(self):
|
||||
self.packager.Package(
|
||||
self._GetStreams(['audio', 'video'], live=True),
|
||||
self._GetFlags(encryption=True,
|
||||
live=True, dash_if_iop=True))
|
||||
self._GetFlags(encryption=True, dash_if_iop=True))
|
||||
self._DiffLiveGold(self.output[0], 'bear-640x360-a-live-cenc-golden')
|
||||
self._DiffLiveGold(self.output[1], 'bear-640x360-v-live-cenc-golden')
|
||||
self._DiffLiveMpdGold(self.mpd_output,
|
||||
|
@ -360,8 +357,7 @@ class PackagerAppTest(unittest.TestCase):
|
|||
live=True,
|
||||
test_files=['bear-1280x720.mp4', 'bear-640x360.mp4',
|
||||
'bear-320x180.mp4']),
|
||||
self._GetFlags(encryption=True,
|
||||
live=True, dash_if_iop=True))
|
||||
self._GetFlags(encryption=True, dash_if_iop=True))
|
||||
self._DiffLiveGold(self.output[2], 'bear-640x360-a-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
|
||||
|
@ -371,9 +367,7 @@ class PackagerAppTest(unittest.TestCase):
|
|||
def testPackageWithLiveProfileAndKeyRotation(self):
|
||||
self.packager.Package(
|
||||
self._GetStreams(['audio', 'video'], live=True),
|
||||
self._GetFlags(encryption=True,
|
||||
key_rotation=True,
|
||||
live=True))
|
||||
self._GetFlags(encryption=True, key_rotation=True))
|
||||
self._DiffLiveGold(self.output[0],
|
||||
'bear-640x360-a-live-cenc-rotation-golden')
|
||||
self._DiffLiveGold(self.output[1],
|
||||
|
@ -386,7 +380,6 @@ class PackagerAppTest(unittest.TestCase):
|
|||
self._GetStreams(['audio', 'video'], live=True),
|
||||
self._GetFlags(encryption=True,
|
||||
key_rotation=True,
|
||||
live=True,
|
||||
dash_if_iop=True))
|
||||
self._DiffLiveGold(self.output[0],
|
||||
'bear-640x360-a-live-cenc-rotation-golden')
|
||||
|
@ -496,7 +489,6 @@ class PackagerAppTest(unittest.TestCase):
|
|||
random_iv=False,
|
||||
widevine_encryption=False,
|
||||
key_rotation=False,
|
||||
live=False,
|
||||
dash_if_iop=False,
|
||||
output_media_info=False,
|
||||
output_hls=False,
|
||||
|
@ -526,8 +518,6 @@ class PackagerAppTest(unittest.TestCase):
|
|||
if key_rotation:
|
||||
flags.append('--crypto_period_duration=1')
|
||||
|
||||
if live:
|
||||
flags.append('--profile=live')
|
||||
if dash_if_iop:
|
||||
flags.append('--generate_dash_if_iop_compliant_mpd')
|
||||
if output_media_info:
|
||||
|
|
|
@ -9,17 +9,8 @@
|
|||
namespace shaka {
|
||||
namespace media {
|
||||
|
||||
MuxerOptions::MuxerOptions()
|
||||
: single_segment(false),
|
||||
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() {}
|
||||
MuxerOptions::MuxerOptions() = default;
|
||||
MuxerOptions::~MuxerOptions() = default;
|
||||
|
||||
} // namespace media
|
||||
} // namespace shaka
|
||||
|
|
|
@ -19,42 +19,38 @@ struct 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
|
||||
/// parameter sets the duration of a subsegment; otherwise, this parameter
|
||||
/// sets the duration of a segment. A segment can contain one or many
|
||||
/// fragments.
|
||||
double segment_duration;
|
||||
double segment_duration = 0;
|
||||
|
||||
/// Fragment duration in seconds. Should not be larger than the segment
|
||||
/// duration.
|
||||
double fragment_duration;
|
||||
double fragment_duration = 0;
|
||||
|
||||
/// Force segments to begin with stream access points. Segment duration may
|
||||
/// 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
|
||||
/// may not be exactly what specified by segment_duration. Setting to true
|
||||
/// implies that segment_sap_aligned is true as well.
|
||||
bool fragment_sap_aligned;
|
||||
bool fragment_sap_aligned = false;
|
||||
|
||||
/// For ISO BMFF only.
|
||||
/// 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
|
||||
/// will pack N subsegments in the root SIDX of the segment, with
|
||||
/// segment_duration/N/fragment_duration fragments per subsegment.
|
||||
int num_subsegments_per_sidx;
|
||||
int num_subsegments_per_sidx = 0;
|
||||
|
||||
/// For ISO BMFF only.
|
||||
/// Set the flag use_decoding_timestamp_in_timeline, which if set to true, use
|
||||
/// decoding timestamp instead of presentation timestamp in media timeline,
|
||||
/// which is needed to workaround a Chromium bug that decoding timestamp is
|
||||
/// 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
|
||||
/// 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
|
||||
/// attempt to estimate.
|
||||
uint32_t bandwidth;
|
||||
uint32_t bandwidth = 0;
|
||||
|
||||
// Enable/disable subsample encryption for WebM containers.
|
||||
bool webm_subsample_encryption;
|
||||
bool webm_subsample_encryption = true;
|
||||
};
|
||||
|
||||
} // namespace media
|
||||
|
|
|
@ -44,7 +44,6 @@ MediaInfo ConvertToMediaInfo(const std::string& media_info_string) {
|
|||
}
|
||||
|
||||
void SetDefaultLiveMuxerOptionsValues(media::MuxerOptions* muxer_options) {
|
||||
muxer_options->single_segment = false;
|
||||
muxer_options->segment_duration = 10.0;
|
||||
muxer_options->fragment_duration = 10.0;
|
||||
muxer_options->segment_sap_aligned = true;
|
||||
|
|
|
@ -149,9 +149,8 @@ void SetMediaInfoStreamInfo(const StreamInfo& stream_info,
|
|||
void SetMediaInfoMuxerOptions(const MuxerOptions& muxer_options,
|
||||
MediaInfo* 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);
|
||||
DCHECK(muxer_options.segment_template.empty());
|
||||
} else {
|
||||
media_info->set_init_segment_name(muxer_options.output_file_name);
|
||||
media_info->set_segment_template(muxer_options.segment_template);
|
||||
|
|
|
@ -73,7 +73,6 @@ OnMediaEndParameters GetDefaultOnMediaEndParams() {
|
|||
}
|
||||
|
||||
void SetDefaultMuxerOptionsValues(MuxerOptions* muxer_options) {
|
||||
muxer_options->single_segment = true;
|
||||
muxer_options->segment_duration = 10.0;
|
||||
muxer_options->fragment_duration = 10.0;
|
||||
muxer_options->segment_sap_aligned = true;
|
||||
|
|
|
@ -45,7 +45,7 @@ void VodMediaInfoDumpMuxerListener::OnMediaStart(
|
|||
const StreamInfo& stream_info,
|
||||
uint32_t time_scale,
|
||||
ContainerType container_type) {
|
||||
DCHECK(muxer_options.single_segment);
|
||||
DCHECK(muxer_options.segment_template.empty());
|
||||
media_info_.reset(new MediaInfo());
|
||||
if (!internal::GenerateMediaInfo(muxer_options,
|
||||
stream_info,
|
||||
|
|
|
@ -134,7 +134,7 @@ Status MP4Muxer::Initialize() {
|
|||
}
|
||||
}
|
||||
|
||||
if (options().single_segment) {
|
||||
if (options().segment_template.empty()) {
|
||||
segmenter_.reset(new SingleSegmentSegmenter(options(), std::move(ftyp),
|
||||
std::move(moov)));
|
||||
} else {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <gtest/gtest.h>
|
||||
#include <memory>
|
||||
#include "packager/media/base/muxer_util.h"
|
||||
#include "packager/media/formats/webm/segmenter_test_base.h"
|
||||
|
||||
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,
|
||||
// ID: Info, Payload Size: 88
|
||||
0x15, 0x49, 0xa9, 0x66, 0xd8,
|
||||
// ID: Info, Payload Size: 81
|
||||
0x15, 0x49, 0xa9, 0x66, 0xd1,
|
||||
// TimecodeScale: 1000000
|
||||
0x2a, 0xd7, 0xb1, 0x83, 0x0f, 0x42, 0x40,
|
||||
// Duration: float(0)
|
||||
0x44, 0x89, 0x84, 0x3f, 0x80, 0x00, 0x00,
|
||||
// MuxingApp: 'libwebm-0.2.1.0'
|
||||
0x4d, 0x80, 0x8f, 0x6c, 0x69, 0x62, 0x77, 0x65, 0x62, 0x6d, 0x2d, 0x30,
|
||||
0x2e, 0x32, 0x2e, 0x31, 0x2e, 0x30,
|
||||
|
@ -93,7 +92,10 @@ const uint8_t kBasicSupportDataSegment[] = {
|
|||
|
||||
class MultiSegmentSegmenterTest : public SegmentTestBase {
|
||||
public:
|
||||
MultiSegmentSegmenterTest() : info_(CreateVideoStreamInfo()) {}
|
||||
MultiSegmentSegmenterTest()
|
||||
: info_(CreateVideoStreamInfo()),
|
||||
segment_template_(std::string(kMemoryFilePrefix) +
|
||||
"output-template-$Number$.webm") {}
|
||||
|
||||
protected:
|
||||
void InitializeSegmenter(const MuxerOptions& options) {
|
||||
|
@ -102,12 +104,18 @@ class MultiSegmentSegmenterTest : public SegmentTestBase {
|
|||
options, info_.get(), NULL, &segmenter_));
|
||||
}
|
||||
|
||||
std::string TemplateFileName(int number) const {
|
||||
return GetSegmentName(segment_template_, 0, number, 0);
|
||||
}
|
||||
|
||||
scoped_refptr<StreamInfo> info_;
|
||||
std::string segment_template_;
|
||||
std::unique_ptr<webm::Segmenter> segmenter_;
|
||||
};
|
||||
|
||||
TEST_F(MultiSegmentSegmenterTest, BasicSupport) {
|
||||
MuxerOptions options = CreateMuxerOptions();
|
||||
options.segment_template = segment_template_;
|
||||
ASSERT_NO_FATAL_FAILURE(InitializeSegmenter(options));
|
||||
|
||||
// Write the samples to the Segmenter.
|
||||
|
@ -128,6 +136,7 @@ TEST_F(MultiSegmentSegmenterTest, BasicSupport) {
|
|||
|
||||
TEST_F(MultiSegmentSegmenterTest, SplitsFilesOnSegmentDuration) {
|
||||
MuxerOptions options = CreateMuxerOptions();
|
||||
options.segment_template = segment_template_;
|
||||
options.segment_duration = 5; // seconds
|
||||
ASSERT_NO_FATAL_FAILURE(InitializeSegmenter(options));
|
||||
|
||||
|
@ -154,6 +163,7 @@ TEST_F(MultiSegmentSegmenterTest, SplitsFilesOnSegmentDuration) {
|
|||
|
||||
TEST_F(MultiSegmentSegmenterTest, RespectsSegmentSAPAlign) {
|
||||
MuxerOptions options = CreateMuxerOptions();
|
||||
options.segment_template = segment_template_;
|
||||
options.segment_duration = 3; // seconds
|
||||
options.segment_sap_aligned = true;
|
||||
ASSERT_NO_FATAL_FAILURE(InitializeSegmenter(options));
|
||||
|
@ -182,6 +192,7 @@ TEST_F(MultiSegmentSegmenterTest, RespectsSegmentSAPAlign) {
|
|||
|
||||
TEST_F(MultiSegmentSegmenterTest, SplitsClustersOnFragmentDuration) {
|
||||
MuxerOptions options = CreateMuxerOptions();
|
||||
options.segment_template = segment_template_;
|
||||
options.fragment_duration = 5; // seconds
|
||||
ASSERT_NO_FATAL_FAILURE(InitializeSegmenter(options));
|
||||
|
||||
|
@ -205,6 +216,7 @@ TEST_F(MultiSegmentSegmenterTest, SplitsClustersOnFragmentDuration) {
|
|||
|
||||
TEST_F(MultiSegmentSegmenterTest, RespectsFragmentSAPAlign) {
|
||||
MuxerOptions options = CreateMuxerOptions();
|
||||
options.segment_template = segment_template_;
|
||||
options.fragment_duration = 3; // seconds
|
||||
options.fragment_sap_aligned = true;
|
||||
ASSERT_NO_FATAL_FAILURE(InitializeSegmenter(options));
|
||||
|
@ -230,4 +242,3 @@ TEST_F(MultiSegmentSegmenterTest, RespectsFragmentSAPAlign) {
|
|||
|
||||
} // namespace media
|
||||
} // namespace shaka
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ Status Segmenter::Initialize(std::unique_ptr<MkvWriter> writer,
|
|||
(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
|
||||
// overwritten at the end. This works because this is a float and floats
|
||||
// are always the same size.
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#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/formats/webm/webm_constants.h"
|
||||
#include "packager/version/version.h"
|
||||
|
@ -43,10 +42,7 @@ void SegmentTestBase::SetUp() {
|
|||
SetPackagerVersionForTesting("test");
|
||||
|
||||
output_file_name_ = std::string(kMemoryFilePrefix) + "output-file.webm";
|
||||
segment_template_ =
|
||||
std::string(kMemoryFilePrefix) + "output-template-$Number$.webm";
|
||||
cur_time_timescale_ = 0;
|
||||
single_segment_ = true;
|
||||
}
|
||||
|
||||
void SegmentTestBase::TearDown() {
|
||||
|
@ -78,9 +74,7 @@ scoped_refptr<MediaSample> SegmentTestBase::CreateSample(
|
|||
|
||||
MuxerOptions SegmentTestBase::CreateMuxerOptions() const {
|
||||
MuxerOptions ret;
|
||||
ret.single_segment = single_segment_;
|
||||
ret.output_file_name = output_file_name_;
|
||||
ret.segment_template = segment_template_;
|
||||
ret.segment_duration = 30; // seconds
|
||||
ret.fragment_duration = 30; // seconds
|
||||
ret.segment_sap_aligned = false;
|
||||
|
@ -102,10 +96,6 @@ std::string SegmentTestBase::OutputFileName() const {
|
|||
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() {}
|
||||
|
|
|
@ -43,7 +43,7 @@ Status WebMMuxer::Initialize() {
|
|||
if (!status.ok())
|
||||
return status;
|
||||
|
||||
if (!options().single_segment) {
|
||||
if (!options().segment_template.empty()) {
|
||||
segmenter_.reset(new MultiSegmentSegmenter(options()));
|
||||
} else {
|
||||
segmenter_.reset(new TwoPassSingleSegmentSegmenter(options()));
|
||||
|
|
|
@ -140,8 +140,6 @@ bool PackagerTestBasic::ContentsEqual(const std::string& file1,
|
|||
MuxerOptions PackagerTestBasic::SetupOptions(const std::string& output,
|
||||
bool single_segment) {
|
||||
MuxerOptions options;
|
||||
options.single_segment = single_segment;
|
||||
|
||||
options.segment_duration = kSegmentDurationInSeconds;
|
||||
options.fragment_duration = kFragmentDurationInSecodns;
|
||||
options.segment_sap_aligned = kSegmentSapAligned;
|
||||
|
@ -149,6 +147,7 @@ MuxerOptions PackagerTestBasic::SetupOptions(const std::string& output,
|
|||
options.num_subsegments_per_sidx = kNumSubsegmentsPerSidx;
|
||||
|
||||
options.output_file_name = GetFullPath(output);
|
||||
if (!single_segment)
|
||||
options.segment_template = GetFullPath(kSegmentTemplate);
|
||||
options.temp_dir = test_directory_.AsUTF8Unsafe();
|
||||
return options;
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
'app/packager_main.cc',
|
||||
'app/packager_util.cc',
|
||||
'app/packager_util.h',
|
||||
'app/retired_flags.cc',
|
||||
'app/retired_flags.h',
|
||||
'app/stream_descriptor.cc',
|
||||
'app/stream_descriptor.h',
|
||||
'app/validate_flag.cc',
|
||||
|
|
Loading…
Reference in New Issue