From ed4d950e09087dcf01147eed5345b14b4d84da40 Mon Sep 17 00:00:00 2001 From: Marcus Wichelmann Date: Wed, 4 Aug 2021 16:44:43 +0200 Subject: [PATCH 1/6] Add support for the EXT-X-START tag (fixes #970) --- docs/source/options/hls_options.rst | 8 ++++++++ packager/app/hls_flags.cc | 10 ++++++++++ packager/app/hls_flags.h | 1 + packager/app/packager_main.cc | 1 + packager/hls/base/media_playlist.cc | 11 +++++++++-- packager/hls/public/hls_params.h | 7 +++++++ 6 files changed, 36 insertions(+), 2 deletions(-) diff --git a/docs/source/options/hls_options.rst b/docs/source/options/hls_options.rst index 8ec27f368df..f6e74380e3f 100644 --- a/docs/source/options/hls_options.rst +++ b/docs/source/options/hls_options.rst @@ -76,6 +76,14 @@ HLS options The EXT-X-MEDIA-SEQUENCE documentation can be read here: https://tools.ietf.org/html/rfc8216#section-4.3.3.2. +--hls_start_time_offset + + Sets EXT-X-START on the media playlists to specify the preferred point + at wich the player should start playing. + A positive number indicates a time offset from the beginning of the playlist. + A negative number indicates a negative time offset from the end of the + last media segment in the playlist. + --hls_only=0|1 Optional. Defaults to 0 if not specified. If it is set to 1, indicates the diff --git a/packager/app/hls_flags.cc b/packager/app/hls_flags.cc index 2415ebe0e11..0038c6c6af8 100644 --- a/packager/app/hls_flags.cc +++ b/packager/app/hls_flags.cc @@ -6,6 +6,8 @@ #include "packager/app/hls_flags.h" +#include + DEFINE_string(hls_master_playlist_output, "", "Output path for the master playlist for HLS. This flag must be" @@ -30,3 +32,11 @@ DEFINE_int32(hls_media_sequence_number, "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."); +DEFINE_double(hls_start_time_offset, + std::numeric_limits::lowest(), + "Floating-point number. Sets EXT-X-START on the media playlists " + "to specify the preferred point at wich the player should start " + "playing. A positive number indicates a time offset from the " + "beginning of the playlist. A negative number indicates a " + "negative time offset from the end of the last media segment " + "in the playlist."); \ No newline at end of file diff --git a/packager/app/hls_flags.h b/packager/app/hls_flags.h index c48a528e439..8811019518e 100644 --- a/packager/app/hls_flags.h +++ b/packager/app/hls_flags.h @@ -14,5 +14,6 @@ DECLARE_string(hls_base_url); DECLARE_string(hls_key_uri); DECLARE_string(hls_playlist_type); DECLARE_int32(hls_media_sequence_number); +DECLARE_double(hls_start_time_offset); #endif // PACKAGER_APP_HLS_FLAGS_H_ diff --git a/packager/app/packager_main.cc b/packager/app/packager_main.cc index 179ecfd9260..69fe4bd97ff 100644 --- a/packager/app/packager_main.cc +++ b/packager/app/packager_main.cc @@ -488,6 +488,7 @@ base::Optional GetPackagingParams() { 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; + hls_params.start_time_offset = FLAGS_hls_start_time_offset; 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 e1089448d21..26d6333d60a 100644 --- a/packager/hls/base/media_playlist.cc +++ b/packager/hls/base/media_playlist.cc @@ -10,6 +10,7 @@ #include #include +#include #include #include "packager/base/logging.h" @@ -107,7 +108,8 @@ std::string CreatePlaylistHeader( HlsPlaylistType type, MediaPlaylist::MediaPlaylistStreamType stream_type, uint32_t media_sequence_number, - int discontinuity_sequence_number) { + int discontinuity_sequence_number, + double start_time_offset) { const std::string version = GetPackagerVersion(); std::string version_line; if (!version.empty()) { @@ -148,6 +150,10 @@ std::string CreatePlaylistHeader( MediaPlaylist::MediaPlaylistStreamType::kVideoIFramesOnly) { base::StringAppendF(&header, "#EXT-X-I-FRAMES-ONLY\n"); } + if (start_time_offset > std::numeric_limits::lowest()) { + base::StringAppendF(&header, "#EXT-X-START:TIME-OFFSET=%f\n", + start_time_offset); + } // Put EXT-X-MAP at the end since the rest of the playlist is about the // segment and key info. @@ -476,7 +482,8 @@ bool MediaPlaylist::WriteToFile(const std::string& file_path) { std::string content = CreatePlaylistHeader( media_info_, target_duration_, hls_params_.playlist_type, stream_type_, - media_sequence_number_, discontinuity_sequence_number_); + media_sequence_number_, discontinuity_sequence_number_, + hls_params_.start_time_offset); for (const auto& entry : entries_) base::StringAppendF(&content, "%s\n", entry->ToString().c_str()); diff --git a/packager/hls/public/hls_params.h b/packager/hls/public/hls_params.h index d1f7200f439..138f913c70d 100644 --- a/packager/hls/public/hls_params.h +++ b/packager/hls/public/hls_params.h @@ -7,6 +7,7 @@ #ifndef PACKAGER_HLS_PUBLIC_HLS_PARAMS_H_ #define PACKAGER_HLS_PUBLIC_HLS_PARAMS_H_ +#include #include namespace shaka { @@ -62,6 +63,12 @@ struct HlsParams { /// Custom EXT-X-MEDIA-SEQUENCE value to allow continuous media playback /// across packager restarts. See #691 for details. uint32_t media_sequence_number = 0; + /// Sets EXT-X-START on the media playlists to specify the preferred point + /// at wich the player should start playing. + /// A positive number indicates a time offset from the beginning of the playlist. + /// A negative number indicates a negative time offset from the end of the + /// last media segment in the playlist. + double start_time_offset = std::numeric_limits::lowest(); }; } // namespace shaka From 381ec3046967b17622029633d9b6316afa68c3c5 Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Thu, 15 Feb 2024 11:00:47 -0800 Subject: [PATCH 2/6] Fix linter --- include/packager/hls_params.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/packager/hls_params.h b/include/packager/hls_params.h index ad26839f0e7..c79d8d12ac8 100644 --- a/include/packager/hls_params.h +++ b/include/packager/hls_params.h @@ -66,9 +66,9 @@ struct HlsParams { uint32_t media_sequence_number = 0; /// Sets EXT-X-START on the media playlists to specify the preferred point /// at wich the player should start playing. - /// A positive number indicates a time offset from the beginning of the playlist. - /// A negative number indicates a negative time offset from the end of the - /// last media segment in the playlist. + /// A positive number indicates a time offset from the beginning of the + /// playlist. A negative number indicates a negative time offset from the end + /// of the last media segment in the playlist. double start_time_offset = std::numeric_limits::lowest(); }; From 5e6cf264fa2ad29112e225c71b6927827d8aa72e Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Thu, 15 Feb 2024 11:01:05 -0800 Subject: [PATCH 3/6] Fix base:: call to absl:: --- packager/hls/base/media_playlist.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packager/hls/base/media_playlist.cc b/packager/hls/base/media_playlist.cc index 657c43c2177..a2f6bc6e691 100644 --- a/packager/hls/base/media_playlist.cc +++ b/packager/hls/base/media_playlist.cc @@ -154,8 +154,8 @@ std::string CreatePlaylistHeader( absl::StrAppendFormat(&header, "#EXT-X-I-FRAMES-ONLY\n"); } if (start_time_offset > std::numeric_limits::lowest()) { - base::StringAppendF(&header, "#EXT-X-START:TIME-OFFSET=%f\n", - start_time_offset); + absl::StrAppendFormat(&header, "#EXT-X-START:TIME-OFFSET=%f\n", + start_time_offset); } // Put EXT-X-MAP at the end since the rest of the playlist is about the From f098e4d1b1ab71046aa3c20d3661531900a63b9c Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Thu, 15 Feb 2024 11:03:37 -0800 Subject: [PATCH 4/6] Use std::optional instead of numeric_limits for unset value --- include/packager/hls_params.h | 4 ++-- packager/hls/base/media_playlist.cc | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/packager/hls_params.h b/include/packager/hls_params.h index c79d8d12ac8..e8c86aa9063 100644 --- a/include/packager/hls_params.h +++ b/include/packager/hls_params.h @@ -8,7 +8,7 @@ #define PACKAGER_PUBLIC_HLS_PARAMS_H_ #include -#include +#include #include namespace shaka { @@ -69,7 +69,7 @@ struct HlsParams { /// A positive number indicates a time offset from the beginning of the /// playlist. A negative number indicates a negative time offset from the end /// of the last media segment in the playlist. - double start_time_offset = std::numeric_limits::lowest(); + std::optional start_time_offset; }; } // namespace shaka diff --git a/packager/hls/base/media_playlist.cc b/packager/hls/base/media_playlist.cc index a2f6bc6e691..3dd79e32657 100644 --- a/packager/hls/base/media_playlist.cc +++ b/packager/hls/base/media_playlist.cc @@ -9,8 +9,8 @@ #include #include #include -#include #include +#include #include #include @@ -111,7 +111,7 @@ std::string CreatePlaylistHeader( MediaPlaylist::MediaPlaylistStreamType stream_type, uint32_t media_sequence_number, int discontinuity_sequence_number, - double start_time_offset) { + std::optional start_time_offset) { const std::string version = GetPackagerVersion(); std::string version_line; if (!version.empty()) { @@ -153,9 +153,9 @@ std::string CreatePlaylistHeader( MediaPlaylist::MediaPlaylistStreamType::kVideoIFramesOnly) { absl::StrAppendFormat(&header, "#EXT-X-I-FRAMES-ONLY\n"); } - if (start_time_offset > std::numeric_limits::lowest()) { + if (start_time_offset.has_value()) { absl::StrAppendFormat(&header, "#EXT-X-START:TIME-OFFSET=%f\n", - start_time_offset); + start_time_offset.value()); } // Put EXT-X-MAP at the end since the rest of the playlist is about the From 161943e4d2affcbf70df12b51b0a54337d594f7b Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Thu, 15 Feb 2024 11:17:52 -0800 Subject: [PATCH 5/6] Add unit tests --- packager/hls/base/media_playlist_unittest.cc | 88 ++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/packager/hls/base/media_playlist_unittest.cc b/packager/hls/base/media_playlist_unittest.cc index 580dee4b836..d92190d0ca4 100644 --- a/packager/hls/base/media_playlist_unittest.cc +++ b/packager/hls/base/media_playlist_unittest.cc @@ -51,6 +51,10 @@ class MediaPlaylistTest : public ::testing::Test { default_group_id_("default_group_id") { hls_params_.playlist_type = type; hls_params_.time_shift_buffer_depth = kTimeShiftBufferDepth; + + // NOTE: hls_params_ is passed by and stored by reference in MediaPlaylist, + // so changed made to it through mutable_hls_params() after this point + // still affect what the playlist see in its own hls_params_ later. media_playlist_.reset(new MediaPlaylist(hls_params_, default_file_name_, default_name_, default_group_id_)); } @@ -658,6 +662,90 @@ TEST_F(MediaPlaylistMultiSegmentTest, MultipleEncryptionInfo) { ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput); } +TEST_F(MediaPlaylistSingleSegmentTest, StartTimeEmpty) { + const std::string kExpectedOutput = + "#EXTM3U\n" + "#EXT-X-VERSION:6\n" + "## Generated with https://github.com/shaka-project/shaka-packager " + "version test\n" + "#EXT-X-TARGETDURATION:0\n" + "#EXT-X-PLAYLIST-TYPE:VOD\n" + "#EXT-X-ENDLIST\n"; + + // Because this is std::nullopt, the tag isn't in the playlist at all. + mutable_hls_params()->start_time_offset = std::nullopt; + + ASSERT_TRUE(media_playlist_->SetMediaInfo(valid_video_media_info_)); + + const char kMemoryFilePath[] = "memory://media.m3u8"; + EXPECT_TRUE(media_playlist_->WriteToFile(kMemoryFilePath)); + + ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput); +} + +TEST_F(MediaPlaylistSingleSegmentTest, StartTimeZero) { + const std::string kExpectedOutput = + "#EXTM3U\n" + "#EXT-X-VERSION:6\n" + "## Generated with https://github.com/shaka-project/shaka-packager " + "version test\n" + "#EXT-X-TARGETDURATION:0\n" + "#EXT-X-PLAYLIST-TYPE:VOD\n" + "#EXT-X-START:TIME-OFFSET=0.000000\n" + "#EXT-X-ENDLIST\n"; + + mutable_hls_params()->start_time_offset = 0; + + ASSERT_TRUE(media_playlist_->SetMediaInfo(valid_video_media_info_)); + + const char kMemoryFilePath[] = "memory://media.m3u8"; + EXPECT_TRUE(media_playlist_->WriteToFile(kMemoryFilePath)); + + ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput); +} + +TEST_F(MediaPlaylistSingleSegmentTest, StartTimePositive) { + const std::string kExpectedOutput = + "#EXTM3U\n" + "#EXT-X-VERSION:6\n" + "## Generated with https://github.com/shaka-project/shaka-packager " + "version test\n" + "#EXT-X-TARGETDURATION:0\n" + "#EXT-X-PLAYLIST-TYPE:VOD\n" + "#EXT-X-START:TIME-OFFSET=20.000000\n" + "#EXT-X-ENDLIST\n"; + + mutable_hls_params()->start_time_offset = 20; + + ASSERT_TRUE(media_playlist_->SetMediaInfo(valid_video_media_info_)); + + const char kMemoryFilePath[] = "memory://media.m3u8"; + EXPECT_TRUE(media_playlist_->WriteToFile(kMemoryFilePath)); + + ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput); +} + +TEST_F(MediaPlaylistSingleSegmentTest, StartTimeNegative) { + const std::string kExpectedOutput = + "#EXTM3U\n" + "#EXT-X-VERSION:6\n" + "## Generated with https://github.com/shaka-project/shaka-packager " + "version test\n" + "#EXT-X-TARGETDURATION:0\n" + "#EXT-X-PLAYLIST-TYPE:VOD\n" + "#EXT-X-START:TIME-OFFSET=-3.141590\n" + "#EXT-X-ENDLIST\n"; + + mutable_hls_params()->start_time_offset = -3.14159; + + ASSERT_TRUE(media_playlist_->SetMediaInfo(valid_video_media_info_)); + + const char kMemoryFilePath[] = "memory://media.m3u8"; + EXPECT_TRUE(media_playlist_->WriteToFile(kMemoryFilePath)); + + ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput); +} + class LiveMediaPlaylistTest : public MediaPlaylistMultiSegmentTest { protected: LiveMediaPlaylistTest() From 2258170340b9c292e6f27a966c43eafca0c3c6d7 Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Thu, 15 Feb 2024 11:18:26 -0800 Subject: [PATCH 6/6] Fix flag definitions, broken by dumb git stash mistakes --- packager/app/hls_flags.cc | 6 +++--- packager/app/hls_flags.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packager/app/hls_flags.cc b/packager/app/hls_flags.cc index 11c82b8ba5c..653830ca4f7 100644 --- a/packager/app/hls_flags.cc +++ b/packager/app/hls_flags.cc @@ -6,7 +6,7 @@ #include -#include +#include ABSL_FLAG(std::string, hls_master_playlist_output, @@ -37,9 +37,9 @@ ABSL_FLAG(int32_t, "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."); -ABSL_FLAG(double, +ABSL_FLAG(std::optional, hls_start_time_offset, - std::numeric_limits::lowest(), + std::nullopt, "Floating-point number. Sets EXT-X-START on the media playlists " "to specify the preferred point at wich the player should start " "playing. A positive number indicates a time offset from the " diff --git a/packager/app/hls_flags.h b/packager/app/hls_flags.h index eb49a2f3ec9..5bb7ae754ab 100644 --- a/packager/app/hls_flags.h +++ b/packager/app/hls_flags.h @@ -15,6 +15,6 @@ ABSL_DECLARE_FLAG(std::string, hls_base_url); ABSL_DECLARE_FLAG(std::string, hls_key_uri); ABSL_DECLARE_FLAG(std::string, hls_playlist_type); ABSL_DECLARE_FLAG(int32_t, hls_media_sequence_number); -ABSL_DECLARE_FLAG(double, hls_start_time_offset); +ABSL_DECLARE_FLAG(std::optional, hls_start_time_offset); #endif // PACKAGER_APP_HLS_FLAGS_H_