From b0c5722d9c5a276d6e6ac793eba70db74ed75864 Mon Sep 17 00:00:00 2001 From: "lee.fordyce" Date: Thu, 29 Feb 2024 16:48:41 -0700 Subject: [PATCH] fix: produce output segment for header only VTT input --- include/packager/packager.h | 4 ++++ packager/live_packager.cc | 1 + packager/live_packager_test.cc | 2 +- packager/media/demuxer/demuxer.cc | 2 +- packager/media/demuxer/demuxer.h | 8 ++++++++ .../media/formats/webvtt/webvtt_parser.cc | 19 ++++++++++++++++-- packager/media/formats/webvtt/webvtt_parser.h | 3 ++- .../formats/webvtt/webvtt_parser_unittest.cc | 4 ++-- .../hybrik_comp/expected/00001.m4s | Bin 0 -> 196 bytes .../text_in/en.m3u8_0000000000.vtt | 1 + packager/packager.cc | 2 ++ 11 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 packager/media/test/data/live_packager_tests/hybrik_comp/expected/00001.m4s create mode 100644 packager/media/test/data/live_packager_tests/hybrik_comp/text_in/en.m3u8_0000000000.vtt diff --git a/include/packager/packager.h b/include/packager/packager.h index 112da82188a..575d0c153cb 100644 --- a/include/packager/packager.h +++ b/include/packager/packager.h @@ -81,6 +81,10 @@ struct PackagingParams { /// Flag used to adjust negative CTS offset values to correct PTS < DTS bool cts_offset_adjustment = false; + + /// Flag used as a workaround to produce output segments when the input is a + /// header only VTT file + bool webvtt_header_only_output_segment = false; }; /// Defines a single input/output stream. diff --git a/packager/live_packager.cc b/packager/live_packager.cc index b8ade6fd191..bf666b0645e 100644 --- a/packager/live_packager.cc +++ b/packager/live_packager.cc @@ -433,6 +433,7 @@ Status LivePackager::PackageTimedText(const Segment& in, packaging_params.mp4_output_params.sequence_number = config_.segment_number; packaging_params.chunking_params.adjust_sample_boundaries = true; packaging_params.mp4_output_params.include_pssh_in_stream = false; + packaging_params.webvtt_header_only_output_segment = true; StreamDescriptors descriptors = setupStreamDescriptors(config_, callback_params, init_callback_params); diff --git a/packager/live_packager_test.cc b/packager/live_packager_test.cc index 0e57c369e8c..3ac2a7aea1c 100644 --- a/packager/live_packager_test.cc +++ b/packager/live_packager_test.cc @@ -951,7 +951,7 @@ INSTANTIATE_TEST_CASE_P( "audio/en/%05d.m4s", false})); TEST_F(LivePackagerBaseTest, TestPackageTimedTextHybrikComp) { - for (unsigned int i = 1; i < kNumSegments; i++) { + for (unsigned int i = 0; i < kNumSegments; i++) { std::string segment_num = absl::StrFormat("hybrik_comp/text_in/en.m3u8_%010d.vtt", i); std::vector segment_buffer = ReadTestDataFile(segment_num); diff --git a/packager/media/demuxer/demuxer.cc b/packager/media/demuxer/demuxer.cc index c30ee361486..d975d10325d 100644 --- a/packager/media/demuxer/demuxer.cc +++ b/packager/media/demuxer/demuxer.cc @@ -203,7 +203,7 @@ Status Demuxer::InitializeParser() { parser_.reset(new WebMMediaParser()); break; case CONTAINER_WEBVTT: - parser_.reset(new WebVttParser()); + parser_.reset(new WebVttParser(webvtt_header_only_output_segment_)); break; case CONTAINER_UNKNOWN: { const int64_t kDumpSizeLimit = 512; diff --git a/packager/media/demuxer/demuxer.h b/packager/media/demuxer/demuxer.h index 6b588514814..49caf9d35e6 100644 --- a/packager/media/demuxer/demuxer.h +++ b/packager/media/demuxer/demuxer.h @@ -79,6 +79,11 @@ class Demuxer : public OriginHandler { cts_offset_adjustment_ = cts_offset_adjustment; } + void set_webvtt_header_only_output_segment( + bool webvtt_header_only_output_segment) { + webvtt_header_only_output_segment_ = webvtt_header_only_output_segment; + } + protected: /// @name MediaHandler implementation overrides. /// @{ @@ -153,6 +158,9 @@ class Demuxer : public OriginHandler { bool dump_stream_info_ = false; // flag used to adjust negative CTS offset values to correct PTS < DTS bool cts_offset_adjustment_ = false; + // flag used as a workaround to generate output segment in the case of input + // WEBVTT with header only + bool webvtt_header_only_output_segment_ = false; Status init_event_status_; }; diff --git a/packager/media/formats/webvtt/webvtt_parser.cc b/packager/media/formats/webvtt/webvtt_parser.cc index f31c07bcf20..91f7aa7363e 100644 --- a/packager/media/formats/webvtt/webvtt_parser.cc +++ b/packager/media/formats/webvtt/webvtt_parser.cc @@ -190,7 +190,8 @@ void ParseSettings(const std::string& id, } // namespace -WebVttParser::WebVttParser() {} +WebVttParser::WebVttParser(bool webvtt_header_only_output_segment) + : webvtt_header_only_output_segment_(webvtt_header_only_output_segment) {} void WebVttParser::Init(const InitCB& init_cb, const NewMediaSampleCB& new_media_sample_cb, @@ -207,7 +208,21 @@ void WebVttParser::Init(const InitCB& init_cb, bool WebVttParser::Flush() { reader_.Flush(); - return Parse(); + const bool isOK = Parse(); + // Handle case when Parser was initialized but stream information not filled + // This happens when we parse an empty webvtt file (only contains a header) + if (initialized_ && !stream_info_dispatched_) { + if (webvtt_header_only_output_segment_) { + // This is a workaround in the case of input VTT and output VTT (or TTML) + // in MP4 where we need to generate and output MP4 segment. + std::vector block; + block.emplace_back(R"(00:00:00.000 --> 00:00:00.001)"); + ParseCue("", block.data(), block.size()); + } else { + DispatchTextStreamInfo(); + } + } + return isOK; } bool WebVttParser::Parse(const uint8_t* buf, int size) { diff --git a/packager/media/formats/webvtt/webvtt_parser.h b/packager/media/formats/webvtt/webvtt_parser.h index 5491b09b1df..3770adbebaa 100644 --- a/packager/media/formats/webvtt/webvtt_parser.h +++ b/packager/media/formats/webvtt/webvtt_parser.h @@ -22,7 +22,7 @@ namespace media { // Used to parse a WebVTT source into Cues that will be sent downstream. class WebVttParser : public MediaParser { public: - WebVttParser(); + WebVttParser(bool webvtt_header_only_output_segment = false); void Init(const InitCB& init_cb, const NewMediaSampleCB& new_media_sample_cb, @@ -52,6 +52,7 @@ class WebVttParser : public MediaParser { bool saw_cue_ = false; bool stream_info_dispatched_ = false; bool initialized_ = false; + bool webvtt_header_only_output_segment_ = false; }; } // namespace media diff --git a/packager/media/formats/webvtt/webvtt_parser_unittest.cc b/packager/media/formats/webvtt/webvtt_parser_unittest.cc index 9f0287e611d..f058d5c2ee4 100644 --- a/packager/media/formats/webvtt/webvtt_parser_unittest.cc +++ b/packager/media/formats/webvtt/webvtt_parser_unittest.cc @@ -101,7 +101,7 @@ TEST_F(WebVttParserTest, ParseOnlyHeader) { ASSERT_TRUE(parser_->Parse(text, sizeof(text) - 1)); ASSERT_TRUE(parser_->Flush()); - ASSERT_TRUE(streams_.empty()); + ASSERT_FALSE(streams_.empty()); ASSERT_TRUE(samples_.empty()); } @@ -115,7 +115,7 @@ TEST_F(WebVttParserTest, ParseHeaderWithBOM) { ASSERT_TRUE(parser_->Parse(text, sizeof(text) - 1)); ASSERT_TRUE(parser_->Flush()); - ASSERT_TRUE(streams_.empty()); + ASSERT_FALSE(streams_.empty()); ASSERT_TRUE(samples_.empty()); } diff --git a/packager/media/test/data/live_packager_tests/hybrik_comp/expected/00001.m4s b/packager/media/test/data/live_packager_tests/hybrik_comp/expected/00001.m4s new file mode 100644 index 0000000000000000000000000000000000000000..6d673515257045f1efe0e182d135475d7e141086 GIT binary patch literal 196 zcmX|(K?=e!6hueebQ!1zaN*L8;6glumk54C3w~3YU(p+M=`lTq^J#Tp$jr+m0JgNCY(hXM8Yo}g&Xi7{wuWZv-&&elig8*{$I iiP demuxer = std::make_shared(stream.input); demuxer->set_dump_stream_info(packaging_params.test_params.dump_stream_info); demuxer->set_cts_offset_adjustment(packaging_params.cts_offset_adjustment); + demuxer->set_webvtt_header_only_output_segment( + packaging_params.webvtt_header_only_output_segment); if (packaging_params.decryption_params.key_provider != KeyProvider::kNone) { std::unique_ptr decryption_key_source(