Support dash_only and hls_only parameters (#721)

This allows conditional stream descriptors which apply to DASH or HLS only.

Closes #651.
This commit is contained in:
sr90 2020-03-06 10:19:47 -08:00 committed by GitHub
parent 055c67888b
commit a1dd82d478
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 127 additions and 2 deletions

View File

@ -23,5 +23,6 @@ More Screens Ltd. <*@morescreens.net>
Philo Inc. <*@philo.com>
Piotr Srebrny <srebrny.piotr@gmail.com>
Richard Eklycke <richard@eklycke.se>
Sanil Raut <sr1990003@gmail.com>
Sergio Ammirata <sergio@ammirata.net>
The Chromium Authors <*@chromium.org>

View File

@ -37,5 +37,6 @@ Leo Law <leoltlaw.gh@gmail.com>
Piotr Srebrny <srebrny.piotr@gmail.com>
Richard Eklycke <richard@eklycke.se>
Rintaro Kuroiwa <rkuroiwa@google.com>
Sanil Raut <sr1990003@gmail.com>
Sergio Ammirata <sergio@ammirata.net>
Thomas Inskip <tinskip@google.com>

View File

@ -85,3 +85,8 @@ DASH options
Ignored if $Time$ is used in segment template, since $Time$ requires
accurate Segment Timeline.
--dash_only=0|1
Optional. Defaults to 0 if not specified. If it is set to 1, indicates the
stream is DASH only.

View File

@ -75,3 +75,8 @@ HLS options
The EXT-X-MEDIA-SEQUENCE documentation can be read here:
https://tools.ietf.org/html/rfc8216#section-4.3.3.2.
--hls_only=0|1
Optional. Defaults to 0 if not specified. If it is set to 1, indicates the
stream is HLS only.

View File

@ -11,3 +11,21 @@
The above packaging command creates five single file MP4 streams, and HLS
playlists as well as DASH manifests.
* Output DASH + HLS with dash_only and hls_only options
$ packager \
'in=h264_baseline_360p_600.mp4,stream=audio,init_segment=audio/init.mp4,segment_template=audio/$Number$.m4s' \
'in=input_text.vtt,stream=text,init_segment=text/init.mp4,segment_template=text/$Number$.m4s,dash_only=true' \
'in=input_text.vtt,stream=text,segment_template=text/$Number$.vtt,hls_only=true' \
'in=h264_baseline_360p_600.mp4,stream=video,init_segment=h264_360p/init.mp4,segment_template=h264_360p/$Number$.m4s' \
'in=h264_main_480p_1000.mp4,stream=video,init_segment=h264_480p/init.mp4,segment_template=h264_480p/$Number$.m4s' \
'in=h264_main_720p_3000.mp4,stream=video,init_segment=h264_720p/init.mp4,segment_template=h264_720p/$Number$.m4s' \
'in=h264_high_1080p_6000.mp4,stream=video,init_segment=h264_1080p/init.mp4,segment_template=h264_1080p/$Number$.m4s' \
--generate_static_live_mpd --mpd_output h264.mpd \
--hls_master_playlist_output h264_master.m3u8
The above packaging command creates HLS playlists and DASH manifest while using
dash_only for creating segmented WebVTT in mp4 format and hls_only option for
creating WebVTT in text format.

View File

@ -33,6 +33,8 @@ enum FieldType {
kHlsCharacteristicsField,
kDashAccessiblitiesField,
kDashRolesField,
kDashOnlyField,
kHlsOnlyField,
};
struct FieldNameToTypeMapping {
@ -77,6 +79,8 @@ const FieldNameToTypeMapping kFieldNameTypeMappings[] = {
{"dash_role", kDashRolesField},
{"roles", kDashRolesField},
{"role", kDashRolesField},
{"dash_only", kDashOnlyField},
{"hls_only", kHlsOnlyField},
};
FieldType GetFieldType(const std::string& field_name) {
@ -206,6 +210,32 @@ base::Optional<StreamDescriptor> ParseStreamDescriptor(
base::SplitString(iter->second, ";", base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
break;
case kDashOnlyField:
unsigned dash_only_value;
if (!base::StringToUint(iter->second, &dash_only_value)) {
LOG(ERROR) << "Non-numeric option for dash_only field "
"specified (" << iter->second << ").";
return base::nullopt;
}
if (dash_only_value > 1) {
LOG(ERROR) << "dash_only should be either 0 or 1.";
return base::nullopt;
}
descriptor.dash_only = dash_only_value > 0;
break;
case kHlsOnlyField:
unsigned hls_only_value;
if (!base::StringToUint(iter->second, &hls_only_value)) {
LOG(ERROR) << "Non-numeric option for hls_only field "
"specified (" << iter->second << ").";
return base::nullopt;
}
if (hls_only_value > 1) {
LOG(ERROR) << "hls_only should be either 0 or 1.";
return base::nullopt;
}
descriptor.hls_only = hls_only_value > 0;
break;
default:
LOG(ERROR) << "Unknown field in stream descriptor (\"" << iter->first
<< "\").";

View File

@ -275,8 +275,10 @@ class PackagerAppTest(unittest.TestCase):
using_time_specifier=False,
hls=False,
hls_characteristics=None,
hls_only=None,
dash_accessibilities=None,
dash_roles=None,
dash_only=None,
trick_play_factor=None,
drm_label=None,
skip_encryption=None,
@ -303,8 +305,10 @@ class PackagerAppTest(unittest.TestCase):
$Number$. This flag is only relevant if segmented is True.
hls: Should the output be for an HLS manifest.
hls_characteristics: CHARACTERISTICS attribute for the HLS stream.
hls_only: If set to true, will indicate that the stream is for HLS only.
dash_accessibilities: Accessibility element for the DASH stream.
dash_roles: Role element for the DASH stream.
dash_only: If set to true, will indicate that the stream is for DASH only.
trick_play_factor: Signals the stream is to be used for a trick play
stream and which key frames to use. A trick play factor of 0 is the
same as not specifying a trick play factor.
@ -360,11 +364,17 @@ class PackagerAppTest(unittest.TestCase):
if hls_characteristics:
stream.Append('hls_characteristics', hls_characteristics)
if hls_only:
stream.Append('hls_only', 1)
if dash_accessibilities:
stream.Append('dash_accessibilities', dash_accessibilities)
if dash_roles:
stream.Append('dash_roles', dash_roles)
if dash_only:
stream.Append('dash_only', 1)
requires_init_segment = segmented and base_ext not in [
'aac', 'ac3', 'ec3', 'ts', 'vtt'
]
@ -700,6 +710,14 @@ class PackagerFunctionalTest(PackagerAppTest):
# order of trick play factors gets the same mpd.
self._CheckTestResults('audio-video-with-two-trick-play')
def testDashOnlyAndHlsOnly(self):
streams = [
self._GetStream('video', hls_only=True),
self._GetStream('audio', dash_only=True),
]
self.assertPackageSuccess(streams, self._GetFlags(output_dash=True,output_hls=True))
self._CheckTestResults('hls-only-dash-only')
def testAudioVideoWithLanguageOverride(self):
self.assertPackageSuccess(
self._GetStreams(['audio', 'video'], language='por', hls=True),

View File

@ -0,0 +1,5 @@
#EXTM3U
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-STREAM-INF:BANDWIDTH=973483,AVERAGE-BANDWIDTH=879459,CODECS="avc1.64001e",RESOLUTION=640x360,FRAME-RATE=29.970
stream_1.m3u8

View File

@ -0,0 +1,15 @@
<?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" 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">
<Representation id="0" bandwidth="133334" 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>
<SegmentBase indexRange="793-860" timescale="44100">
<Initialization range="0-792"/>
</SegmentBase>
</Representation>
</AdaptationSet>
</Period>
</MPD>

View File

@ -0,0 +1,16 @@
#EXTM3U
#EXT-X-VERSION:6
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-TARGETDURATION:2
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-MAP:URI="bear-640x360-video.mp4",BYTERANGE="859@0"
#EXTINF:1.001,
#EXT-X-BYTERANGE:99313@927
bear-640x360-video.mp4
#EXTINF:1.001,
#EXT-X-BYTERANGE:121807
bear-640x360-video.mp4
#EXTINF:0.734,
#EXT-X-BYTERANGE:79662
bear-640x360-video.mp4
#EXT-X-ENDLIST

View File

@ -105,11 +105,13 @@ std::unique_ptr<MuxerListener> MuxerListenerFactory::CreateListener(
combined_listener->AddListener(
CreateMediaInfoDumpListenerInternal(stream.media_info_output));
}
if (mpd_notifier_) {
if (mpd_notifier_ && !stream.hls_only) {
combined_listener->AddListener(
CreateMpdListenerInternal(stream, mpd_notifier_));
}
if (hls_notifier_) {
if (hls_notifier_ && !stream.dash_only) {
for (auto& listener :
CreateHlsListenersInternal(stream, stream_index, hls_notifier_)) {
combined_listener->AddListener(std::move(listener));

View File

@ -46,11 +46,13 @@ class MuxerListenerFactory {
std::string hls_playlist_name;
std::string hls_iframe_playlist_name;
std::vector<std::string> hls_characteristics;
bool hls_only = false;
// DASH specific values needed to write DASH mpd. Will only be used if an
// MpdNotifier is given to the factory.
std::vector<std::string> dash_accessiblities;
std::vector<std::string> dash_roles;
bool dash_only = false;
};
/// Create a new muxer listener.

View File

@ -93,9 +93,11 @@ MuxerListenerFactory::StreamData ToMuxerListenerData(
data.hls_playlist_name = stream.hls_playlist_name;
data.hls_iframe_playlist_name = stream.hls_iframe_playlist_name;
data.hls_characteristics = stream.hls_characteristics;
data.hls_only = stream.hls_only;
data.dash_accessiblities = stream.dash_accessiblities;
data.dash_roles = stream.dash_roles;
data.dash_only = stream.dash_only;
return data;
};

View File

@ -128,6 +128,11 @@ struct StreamDescriptor {
std::vector<std::string> dash_accessiblities;
/// Optional for DASH output. It defines Role elements of the stream.
std::vector<std::string> dash_roles;
/// Set to true to indicate that the stream is for dash only.
bool dash_only = false;
/// Set to true to indicate that the stream is for hls only.
bool hls_only = false;
};
class SHAKA_EXPORT Packager {