diff --git a/AUTHORS b/AUTHORS index 95b23e51f2..ca8ecf6851 100644 --- a/AUTHORS +++ b/AUTHORS @@ -23,5 +23,6 @@ More Screens Ltd. <*@morescreens.net> Philo Inc. <*@philo.com> Piotr Srebrny Richard Eklycke +Sanil Raut Sergio Ammirata The Chromium Authors <*@chromium.org> diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 3d9c544fb4..cfa8e208e2 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -37,5 +37,6 @@ Leo Law Piotr Srebrny Richard Eklycke Rintaro Kuroiwa +Sanil Raut Sergio Ammirata Thomas Inskip diff --git a/docs/source/options/dash_options.rst b/docs/source/options/dash_options.rst index 0b25db78a2..5d8b3b7d85 100644 --- a/docs/source/options/dash_options.rst +++ b/docs/source/options/dash_options.rst @@ -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. diff --git a/docs/source/options/hls_options.rst b/docs/source/options/hls_options.rst index c2e77f7c9a..8ec27f368d 100644 --- a/docs/source/options/hls_options.rst +++ b/docs/source/options/hls_options.rst @@ -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. diff --git a/docs/source/tutorials/dash_hls_example.rst b/docs/source/tutorials/dash_hls_example.rst index ab53468ae8..8d422d2cc2 100644 --- a/docs/source/tutorials/dash_hls_example.rst +++ b/docs/source/tutorials/dash_hls_example.rst @@ -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. + diff --git a/packager/app/stream_descriptor.cc b/packager/app/stream_descriptor.cc index 08cd670010..aa2208f9a5 100644 --- a/packager/app/stream_descriptor.cc +++ b/packager/app/stream_descriptor.cc @@ -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 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 << "\")."; diff --git a/packager/app/test/packager_test.py b/packager/app/test/packager_test.py index 3a125d5507..c30223e412 100755 --- a/packager/app/test/packager_test.py +++ b/packager/app/test/packager_test.py @@ -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), diff --git a/packager/app/test/testdata/hls-only-dash-only/bear-640x360-audio.mp4 b/packager/app/test/testdata/hls-only-dash-only/bear-640x360-audio.mp4 new file mode 100644 index 0000000000..87f89a93c0 Binary files /dev/null and b/packager/app/test/testdata/hls-only-dash-only/bear-640x360-audio.mp4 differ diff --git a/packager/app/test/testdata/hls-only-dash-only/bear-640x360-video.mp4 b/packager/app/test/testdata/hls-only-dash-only/bear-640x360-video.mp4 new file mode 100644 index 0000000000..9bc668f8f6 Binary files /dev/null and b/packager/app/test/testdata/hls-only-dash-only/bear-640x360-video.mp4 differ diff --git a/packager/app/test/testdata/hls-only-dash-only/output.m3u8 b/packager/app/test/testdata/hls-only-dash-only/output.m3u8 new file mode 100644 index 0000000000..e0789dccf6 --- /dev/null +++ b/packager/app/test/testdata/hls-only-dash-only/output.m3u8 @@ -0,0 +1,5 @@ +#EXTM3U +## Generated with https://github.com/google/shaka-packager version -- + +#EXT-X-STREAM-INF:BANDWIDTH=973483,AVERAGE-BANDWIDTH=879459,CODECS="avc1.64001e",RESOLUTION=640x360,FRAME-RATE=29.970 +stream_1.m3u8 diff --git a/packager/app/test/testdata/hls-only-dash-only/output.mpd b/packager/app/test/testdata/hls-only-dash-only/output.mpd new file mode 100644 index 0000000000..8326573a5a --- /dev/null +++ b/packager/app/test/testdata/hls-only-dash-only/output.mpd @@ -0,0 +1,15 @@ + + + + + + + + bear-640x360-audio.mp4 + + + + + + + diff --git a/packager/app/test/testdata/hls-only-dash-only/stream_1.m3u8 b/packager/app/test/testdata/hls-only-dash-only/stream_1.m3u8 new file mode 100644 index 0000000000..baf17ef66b --- /dev/null +++ b/packager/app/test/testdata/hls-only-dash-only/stream_1.m3u8 @@ -0,0 +1,16 @@ +#EXTM3U +#EXT-X-VERSION:6 +## Generated with https://github.com/google/shaka-packager version -- +#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 diff --git a/packager/media/event/muxer_listener_factory.cc b/packager/media/event/muxer_listener_factory.cc index add40a0038..820c540858 100644 --- a/packager/media/event/muxer_listener_factory.cc +++ b/packager/media/event/muxer_listener_factory.cc @@ -105,11 +105,13 @@ std::unique_ptr 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)); diff --git a/packager/media/event/muxer_listener_factory.h b/packager/media/event/muxer_listener_factory.h index a87bb746bd..841d42e995 100644 --- a/packager/media/event/muxer_listener_factory.h +++ b/packager/media/event/muxer_listener_factory.h @@ -46,11 +46,13 @@ class MuxerListenerFactory { std::string hls_playlist_name; std::string hls_iframe_playlist_name; std::vector 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 dash_accessiblities; std::vector dash_roles; + bool dash_only = false; }; /// Create a new muxer listener. diff --git a/packager/packager.cc b/packager/packager.cc index 40d76147db..e8a395134f 100644 --- a/packager/packager.cc +++ b/packager/packager.cc @@ -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; }; diff --git a/packager/packager.h b/packager/packager.h index b420189ae2..19d041ebd9 100644 --- a/packager/packager.h +++ b/packager/packager.h @@ -128,6 +128,11 @@ struct StreamDescriptor { std::vector dash_accessiblities; /// Optional for DASH output. It defines Role elements of the stream. std::vector 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 {