diff --git a/AUTHORS b/AUTHORS index d2bdbd68e2..95b23e51f2 100644 --- a/AUTHORS +++ b/AUTHORS @@ -15,6 +15,7 @@ Anders Hasselqvist Chun-da Chen +Daniel CantarĂ­n Google Inc. <*@google.com> Leandro Moreira Leo Law diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 3bdbfc1a95..3d9c544fb4 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -25,6 +25,7 @@ Anders Hasselqvist Bei Li Chun-da Chen +Daniel CantarĂ­n David Cavar Gabe Kopley Haoming Chen diff --git a/docs/source/options/hls_options.rst b/docs/source/options/hls_options.rst index 476f354627..c2e77f7c9a 100644 --- a/docs/source/options/hls_options.rst +++ b/docs/source/options/hls_options.rst @@ -51,3 +51,27 @@ HLS options Same as above, but this applies to text tracks only, and overrides the default language for text tracks. + +--hls_media_sequence_number + + HLS uses the EXT-X-MEDIA-SEQUENCE tag at the start of a live playlist in + order to specify the first segment sequence number. This is because any + live playlist have a limited number of segments, and they also keep + updating with new segments while removing old ones. When a player refreshes + the playlist, this information is important for keeping track of segments + positions. + + When the packager starts, it naturally starts this count from zero. However, + there are many situations where the packager may be restarted, without this + meaning starting this value from zero (but continuing a previous sequence). + The most common situations are problems in the encoder feeding the packager. + + With those cases in mind, this parameter allows to set the initial + EXT-X-MEDIA-SEQUENCE value. This way, it's possible to continue the sequence + number from previous packager run. + + For more information about the reasoning of this, please see issue + `#691 `_. + + The EXT-X-MEDIA-SEQUENCE documentation can be read here: + https://tools.ietf.org/html/rfc8216#section-4.3.3.2. diff --git a/packager/app/hls_flags.cc b/packager/app/hls_flags.cc index 956041c8b3..2415ebe0e1 100644 --- a/packager/app/hls_flags.cc +++ b/packager/app/hls_flags.cc @@ -24,3 +24,9 @@ DEFINE_string(hls_playlist_type, "VOD, EVENT, or LIVE. This defines the EXT-X-PLAYLIST-TYPE in " "the HLS specification. For hls_playlist_type of LIVE, " "EXT-X-PLAYLIST-TYPE tag is omitted."); +DEFINE_int32(hls_media_sequence_number, + 0, + "Number. This HLS-only parameter defines the initial " + "EXT-X-MEDIA-SEQUENCE value, which allows continuous media " + "sequence across packager restarts. See #691 for more " + "information about the reasoning of this and its use cases."); diff --git a/packager/app/hls_flags.h b/packager/app/hls_flags.h index aa4ea461c9..c48a528e43 100644 --- a/packager/app/hls_flags.h +++ b/packager/app/hls_flags.h @@ -13,5 +13,6 @@ DECLARE_string(hls_master_playlist_output); DECLARE_string(hls_base_url); DECLARE_string(hls_key_uri); DECLARE_string(hls_playlist_type); +DECLARE_int32(hls_media_sequence_number); #endif // PACKAGER_APP_HLS_FLAGS_H_ diff --git a/packager/app/packager_main.cc b/packager/app/packager_main.cc index 926fb6ebe6..31fbb2edc4 100644 --- a/packager/app/packager_main.cc +++ b/packager/app/packager_main.cc @@ -470,6 +470,7 @@ base::Optional GetPackagingParams() { FLAGS_preserved_segments_outside_live_window; hls_params.default_language = FLAGS_default_language; hls_params.default_text_language = FLAGS_default_text_language; + hls_params.media_sequence_number = FLAGS_hls_media_sequence_number; TestParams& test_params = packaging_params.test_params; test_params.dump_stream_info = FLAGS_dump_stream_info; diff --git a/packager/hls/base/media_playlist.cc b/packager/hls/base/media_playlist.cc index 98362a0216..9d547f43b4 100644 --- a/packager/hls/base/media_playlist.cc +++ b/packager/hls/base/media_playlist.cc @@ -106,7 +106,7 @@ std::string CreatePlaylistHeader( uint32_t target_duration, HlsPlaylistType type, MediaPlaylist::MediaPlaylistStreamType stream_type, - int media_sequence_number, + uint32_t media_sequence_number, int discontinuity_sequence_number) { const std::string version = GetPackagerVersion(); std::string version_line; @@ -343,7 +343,12 @@ MediaPlaylist::MediaPlaylist(const HlsParams& hls_params, : hls_params_(hls_params), file_name_(file_name), name_(name), - group_id_(group_id) {} + group_id_(group_id), + media_sequence_number_(hls_params_.media_sequence_number) { + // When there's a forced media_sequence_number, start with discontinuity + if (media_sequence_number_ > 0) + entries_.emplace_back(new DiscontinuityEntry()); + } MediaPlaylist::~MediaPlaylist() {} @@ -390,6 +395,7 @@ bool MediaPlaylist::SetMediaInfo(const MediaInfo& media_info) { characteristics_ = std::vector(media_info_.hls_characteristics().begin(), media_info_.hls_characteristics().end()); + return true; } diff --git a/packager/hls/base/media_playlist.h b/packager/hls/base/media_playlist.h index 2f595dc2ab..52465c8835 100644 --- a/packager/hls/base/media_playlist.h +++ b/packager/hls/base/media_playlist.h @@ -236,7 +236,7 @@ class MediaPlaylist { std::string codec_; std::string language_; std::vector characteristics_; - int media_sequence_number_ = 0; + uint32_t media_sequence_number_ = 0; bool inserted_discontinuity_tag_ = false; int discontinuity_sequence_number_ = 0; diff --git a/packager/hls/public/hls_params.h b/packager/hls/public/hls_params.h index 65f31bffb3..0100fb7969 100644 --- a/packager/hls/public/hls_params.h +++ b/packager/hls/public/hls_params.h @@ -56,6 +56,9 @@ struct HlsParams { /// be populated from segment duration specified in ChunkingParams if not /// specified. double target_segment_duration = 0; + /// Custom EXT-X-MEDIA-SEQUENCE value to allow continuous media playback + /// across packager restarts. See #691 for details. + uint32_t media_sequence_number = 0; }; } // namespace shaka diff --git a/packager/media/formats/mp2t/ts_segmenter.cc b/packager/media/formats/mp2t/ts_segmenter.cc index 3474ec1a6c..aaea5ced17 100644 --- a/packager/media/formats/mp2t/ts_segmenter.cc +++ b/packager/media/formats/mp2t/ts_segmenter.cc @@ -125,7 +125,7 @@ void TsSegmenter::SetTsWriterFileOpenedForTesting(bool value) { ts_writer_file_opened_ = value; } -Status TsSegmenter::OpenNewSegmentIfClosed(uint32_t next_pts) { +Status TsSegmenter::OpenNewSegmentIfClosed(int64_t next_pts) { if (ts_writer_file_opened_) return Status::OK; const std::string segment_name = diff --git a/packager/media/formats/mp2t/ts_segmenter.h b/packager/media/formats/mp2t/ts_segmenter.h index 78a88d2213..b140040eb7 100644 --- a/packager/media/formats/mp2t/ts_segmenter.h +++ b/packager/media/formats/mp2t/ts_segmenter.h @@ -71,7 +71,7 @@ class TsSegmenter { void SetTsWriterFileOpenedForTesting(bool value); private: - Status OpenNewSegmentIfClosed(uint32_t next_pts); + Status OpenNewSegmentIfClosed(int64_t next_pts); // Writes PES packets (carried in TsPackets) to a file. If a file is not open, // it will open one. This will not close the file.