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:
parent
055c67888b
commit
a1dd82d478
1
AUTHORS
1
AUTHORS
|
@ -23,5 +23,6 @@ More Screens Ltd. <*@morescreens.net>
|
||||||
Philo Inc. <*@philo.com>
|
Philo Inc. <*@philo.com>
|
||||||
Piotr Srebrny <srebrny.piotr@gmail.com>
|
Piotr Srebrny <srebrny.piotr@gmail.com>
|
||||||
Richard Eklycke <richard@eklycke.se>
|
Richard Eklycke <richard@eklycke.se>
|
||||||
|
Sanil Raut <sr1990003@gmail.com>
|
||||||
Sergio Ammirata <sergio@ammirata.net>
|
Sergio Ammirata <sergio@ammirata.net>
|
||||||
The Chromium Authors <*@chromium.org>
|
The Chromium Authors <*@chromium.org>
|
||||||
|
|
|
@ -37,5 +37,6 @@ Leo Law <leoltlaw.gh@gmail.com>
|
||||||
Piotr Srebrny <srebrny.piotr@gmail.com>
|
Piotr Srebrny <srebrny.piotr@gmail.com>
|
||||||
Richard Eklycke <richard@eklycke.se>
|
Richard Eklycke <richard@eklycke.se>
|
||||||
Rintaro Kuroiwa <rkuroiwa@google.com>
|
Rintaro Kuroiwa <rkuroiwa@google.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>
|
||||||
|
|
|
@ -85,3 +85,8 @@ DASH options
|
||||||
|
|
||||||
Ignored if $Time$ is used in segment template, since $Time$ requires
|
Ignored if $Time$ is used in segment template, since $Time$ requires
|
||||||
accurate Segment Timeline.
|
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.
|
||||||
|
|
|
@ -75,3 +75,8 @@ HLS options
|
||||||
|
|
||||||
The EXT-X-MEDIA-SEQUENCE documentation can be read here:
|
The EXT-X-MEDIA-SEQUENCE documentation can be read here:
|
||||||
https://tools.ietf.org/html/rfc8216#section-4.3.3.2.
|
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.
|
||||||
|
|
|
@ -11,3 +11,21 @@
|
||||||
|
|
||||||
The above packaging command creates five single file MP4 streams, and HLS
|
The above packaging command creates five single file MP4 streams, and HLS
|
||||||
playlists as well as DASH manifests.
|
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.
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,8 @@ enum FieldType {
|
||||||
kHlsCharacteristicsField,
|
kHlsCharacteristicsField,
|
||||||
kDashAccessiblitiesField,
|
kDashAccessiblitiesField,
|
||||||
kDashRolesField,
|
kDashRolesField,
|
||||||
|
kDashOnlyField,
|
||||||
|
kHlsOnlyField,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FieldNameToTypeMapping {
|
struct FieldNameToTypeMapping {
|
||||||
|
@ -77,6 +79,8 @@ const FieldNameToTypeMapping kFieldNameTypeMappings[] = {
|
||||||
{"dash_role", kDashRolesField},
|
{"dash_role", kDashRolesField},
|
||||||
{"roles", kDashRolesField},
|
{"roles", kDashRolesField},
|
||||||
{"role", kDashRolesField},
|
{"role", kDashRolesField},
|
||||||
|
{"dash_only", kDashOnlyField},
|
||||||
|
{"hls_only", kHlsOnlyField},
|
||||||
};
|
};
|
||||||
|
|
||||||
FieldType GetFieldType(const std::string& field_name) {
|
FieldType GetFieldType(const std::string& field_name) {
|
||||||
|
@ -206,6 +210,32 @@ base::Optional<StreamDescriptor> ParseStreamDescriptor(
|
||||||
base::SplitString(iter->second, ";", base::TRIM_WHITESPACE,
|
base::SplitString(iter->second, ";", base::TRIM_WHITESPACE,
|
||||||
base::SPLIT_WANT_NONEMPTY);
|
base::SPLIT_WANT_NONEMPTY);
|
||||||
break;
|
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:
|
default:
|
||||||
LOG(ERROR) << "Unknown field in stream descriptor (\"" << iter->first
|
LOG(ERROR) << "Unknown field in stream descriptor (\"" << iter->first
|
||||||
<< "\").";
|
<< "\").";
|
||||||
|
|
|
@ -275,8 +275,10 @@ class PackagerAppTest(unittest.TestCase):
|
||||||
using_time_specifier=False,
|
using_time_specifier=False,
|
||||||
hls=False,
|
hls=False,
|
||||||
hls_characteristics=None,
|
hls_characteristics=None,
|
||||||
|
hls_only=None,
|
||||||
dash_accessibilities=None,
|
dash_accessibilities=None,
|
||||||
dash_roles=None,
|
dash_roles=None,
|
||||||
|
dash_only=None,
|
||||||
trick_play_factor=None,
|
trick_play_factor=None,
|
||||||
drm_label=None,
|
drm_label=None,
|
||||||
skip_encryption=None,
|
skip_encryption=None,
|
||||||
|
@ -303,8 +305,10 @@ class PackagerAppTest(unittest.TestCase):
|
||||||
$Number$. This flag is only relevant if segmented is True.
|
$Number$. This flag is only relevant if segmented is True.
|
||||||
hls: Should the output be for an HLS manifest.
|
hls: Should the output be for an HLS manifest.
|
||||||
hls_characteristics: CHARACTERISTICS attribute for the HLS stream.
|
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_accessibilities: Accessibility element for the DASH stream.
|
||||||
dash_roles: Role 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
|
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
|
stream and which key frames to use. A trick play factor of 0 is the
|
||||||
same as not specifying a trick play factor.
|
same as not specifying a trick play factor.
|
||||||
|
@ -360,11 +364,17 @@ class PackagerAppTest(unittest.TestCase):
|
||||||
if hls_characteristics:
|
if hls_characteristics:
|
||||||
stream.Append('hls_characteristics', hls_characteristics)
|
stream.Append('hls_characteristics', hls_characteristics)
|
||||||
|
|
||||||
|
if hls_only:
|
||||||
|
stream.Append('hls_only', 1)
|
||||||
|
|
||||||
if dash_accessibilities:
|
if dash_accessibilities:
|
||||||
stream.Append('dash_accessibilities', dash_accessibilities)
|
stream.Append('dash_accessibilities', dash_accessibilities)
|
||||||
if dash_roles:
|
if dash_roles:
|
||||||
stream.Append('dash_roles', 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 [
|
requires_init_segment = segmented and base_ext not in [
|
||||||
'aac', 'ac3', 'ec3', 'ts', 'vtt'
|
'aac', 'ac3', 'ec3', 'ts', 'vtt'
|
||||||
]
|
]
|
||||||
|
@ -700,6 +710,14 @@ class PackagerFunctionalTest(PackagerAppTest):
|
||||||
# order of trick play factors gets the same mpd.
|
# order of trick play factors gets the same mpd.
|
||||||
self._CheckTestResults('audio-video-with-two-trick-play')
|
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):
|
def testAudioVideoWithLanguageOverride(self):
|
||||||
self.assertPackageSuccess(
|
self.assertPackageSuccess(
|
||||||
self._GetStreams(['audio', 'video'], language='por', hls=True),
|
self._GetStreams(['audio', 'video'], language='por', hls=True),
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -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
|
|
@ -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>
|
|
@ -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
|
|
@ -105,11 +105,13 @@ std::unique_ptr<MuxerListener> MuxerListenerFactory::CreateListener(
|
||||||
combined_listener->AddListener(
|
combined_listener->AddListener(
|
||||||
CreateMediaInfoDumpListenerInternal(stream.media_info_output));
|
CreateMediaInfoDumpListenerInternal(stream.media_info_output));
|
||||||
}
|
}
|
||||||
if (mpd_notifier_) {
|
|
||||||
|
if (mpd_notifier_ && !stream.hls_only) {
|
||||||
combined_listener->AddListener(
|
combined_listener->AddListener(
|
||||||
CreateMpdListenerInternal(stream, mpd_notifier_));
|
CreateMpdListenerInternal(stream, mpd_notifier_));
|
||||||
}
|
}
|
||||||
if (hls_notifier_) {
|
|
||||||
|
if (hls_notifier_ && !stream.dash_only) {
|
||||||
for (auto& listener :
|
for (auto& listener :
|
||||||
CreateHlsListenersInternal(stream, stream_index, hls_notifier_)) {
|
CreateHlsListenersInternal(stream, stream_index, hls_notifier_)) {
|
||||||
combined_listener->AddListener(std::move(listener));
|
combined_listener->AddListener(std::move(listener));
|
||||||
|
|
|
@ -46,11 +46,13 @@ class MuxerListenerFactory {
|
||||||
std::string hls_playlist_name;
|
std::string hls_playlist_name;
|
||||||
std::string hls_iframe_playlist_name;
|
std::string hls_iframe_playlist_name;
|
||||||
std::vector<std::string> hls_characteristics;
|
std::vector<std::string> hls_characteristics;
|
||||||
|
bool hls_only = false;
|
||||||
|
|
||||||
// DASH specific values needed to write DASH mpd. Will only be used if an
|
// DASH specific values needed to write DASH mpd. Will only be used if an
|
||||||
// MpdNotifier is given to the factory.
|
// MpdNotifier is given to the factory.
|
||||||
std::vector<std::string> dash_accessiblities;
|
std::vector<std::string> dash_accessiblities;
|
||||||
std::vector<std::string> dash_roles;
|
std::vector<std::string> dash_roles;
|
||||||
|
bool dash_only = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Create a new muxer listener.
|
/// Create a new muxer listener.
|
||||||
|
|
|
@ -93,9 +93,11 @@ MuxerListenerFactory::StreamData ToMuxerListenerData(
|
||||||
data.hls_playlist_name = stream.hls_playlist_name;
|
data.hls_playlist_name = stream.hls_playlist_name;
|
||||||
data.hls_iframe_playlist_name = stream.hls_iframe_playlist_name;
|
data.hls_iframe_playlist_name = stream.hls_iframe_playlist_name;
|
||||||
data.hls_characteristics = stream.hls_characteristics;
|
data.hls_characteristics = stream.hls_characteristics;
|
||||||
|
data.hls_only = stream.hls_only;
|
||||||
|
|
||||||
data.dash_accessiblities = stream.dash_accessiblities;
|
data.dash_accessiblities = stream.dash_accessiblities;
|
||||||
data.dash_roles = stream.dash_roles;
|
data.dash_roles = stream.dash_roles;
|
||||||
|
data.dash_only = stream.dash_only;
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -128,6 +128,11 @@ struct StreamDescriptor {
|
||||||
std::vector<std::string> dash_accessiblities;
|
std::vector<std::string> dash_accessiblities;
|
||||||
/// Optional for DASH output. It defines Role elements of the stream.
|
/// Optional for DASH output. It defines Role elements of the stream.
|
||||||
std::vector<std::string> dash_roles;
|
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 {
|
class SHAKA_EXPORT Packager {
|
||||||
|
|
Loading…
Reference in New Issue