diff --git a/include/packager/live_packager.h b/include/packager/live_packager.h index 0665ab4af3c..6c57c11cba7 100644 --- a/include/packager/live_packager.h +++ b/include/packager/live_packager.h @@ -61,11 +61,15 @@ struct LiveConfig { enum class OutputFormat { FMP4, TS, + VTTMP4, + TTMLMP4, + TTML, }; enum class TrackType { AUDIO, VIDEO, + TEXT, }; enum class EncryptionScheme { @@ -89,7 +93,7 @@ struct LiveConfig { /// For FMP4 output: /// It can be used to set the moof header sequence number if > 0. /// For M2TS output: - /// It is be used to set the continuity counter (TODO: UNIMPLEMENTED). + /// It is be used to set the continuity counter. uint32_t segment_number = 0; /// The offset to be applied to transport stream (e.g. MPEG2-TS, HLS packed @@ -118,6 +122,9 @@ class LivePackager { const Segment& media_segment, FullSegmentBuffer& output); + Status PackageTimedText(const Segment& media_segment, + FullSegmentBuffer& output); + LivePackager(const LivePackager&) = delete; LivePackager& operator=(const LivePackager&) = delete; diff --git a/packager/live_packager.cc b/packager/live_packager.cc index 28e7da49925..1a4d05de816 100644 --- a/packager/live_packager.cc +++ b/packager/live_packager.cc @@ -43,12 +43,27 @@ const std::string INPUT_FNAME = "memory://input_file"; const std::string INIT_SEGMENT_FNAME = "init.mp4"; std::string getSegmentTemplate(const LiveConfig& config) { - return LiveConfig::OutputFormat::TS == config.format ? "$Number$.ts" - : "$Number$.m4s"; + switch (config.format) { + case LiveConfig::OutputFormat::TS: + return "$Number$.ts"; + case LiveConfig::OutputFormat::TTML: + return "$Number$.ttml"; + case LiveConfig::OutputFormat::VTTMP4: + case LiveConfig::OutputFormat::TTMLMP4: + case LiveConfig::OutputFormat::FMP4: + return "$Number$.m4s"; + } } std::string getStreamSelector(const LiveConfig& config) { - return LiveConfig::TrackType::VIDEO == config.track_type ? "video" : "audio"; + switch (config.track_type) { + case LiveConfig::TrackType::VIDEO: + return "video"; + case LiveConfig::TrackType::AUDIO: + return "audio"; + case LiveConfig::TrackType::TEXT: + return "text"; + } } StreamDescriptors setupStreamDescriptors( @@ -61,10 +76,25 @@ StreamDescriptors setupStreamDescriptors( desc.stream_selector = getStreamSelector(config); - if (LiveConfig::OutputFormat::FMP4 == config.format) { - // init segment - desc.output = - File::MakeCallbackFileName(init_cb_params, INIT_SEGMENT_FNAME); + switch (config.format) { + case LiveConfig::OutputFormat::VTTMP4: + desc.output_format = "vtt+mp4"; + desc.output = + File::MakeCallbackFileName(init_cb_params, INIT_SEGMENT_FNAME); + break; + case LiveConfig::OutputFormat::TTMLMP4: + desc.output_format = "ttml+mp4"; + desc.output = + File::MakeCallbackFileName(init_cb_params, INIT_SEGMENT_FNAME); + break; + case LiveConfig::OutputFormat::FMP4: + // init segment + desc.output = + File::MakeCallbackFileName(init_cb_params, INIT_SEGMENT_FNAME); + break; + case LiveConfig::OutputFormat::TS: + case LiveConfig::OutputFormat::TTML: + break; } desc.segment_template = @@ -356,6 +386,48 @@ Status LivePackager::Package(const Segment& init_segment, return packager.Run(); } +Status LivePackager::PackageTimedText(const Segment& in, + FullSegmentBuffer& out) { + SegmentDataReader reader(in); + shaka::BufferCallbackParams callback_params; + callback_params.read_func = [&reader](const std::string& name, void* buffer, + uint64_t size) { + return reader.Read(buffer, size); + }; + + callback_params.write_func = [&out](const std::string& name, const void* data, + uint64_t size) { + out.AppendData(reinterpret_cast(data), size); + return size; + }; + + shaka::BufferCallbackParams init_callback_params; + init_callback_params.write_func = [&out](const std::string& name, + const void* data, uint64_t size) { + if (out.InitSegmentSize() == 0) { + out.SetInitSegment(reinterpret_cast(data), size); + } + return size; + }; + + shaka::PackagingParams packaging_params; + packaging_params.chunking_params.segment_duration_in_seconds = + DEFAULT_SEGMENT_DURATION; + + packaging_params.mp4_output_params.include_pssh_in_stream = false; + + StreamDescriptors descriptors = + setupStreamDescriptors(config_, callback_params, init_callback_params); + + shaka::Packager packager; + shaka::Status status = packager.Initialize(packaging_params, descriptors); + + if (status != Status::OK) { + return status; + } + return packager.Run(); +} + SegmentManager::SegmentManager() = default; int64_t SegmentManager::OnSegmentWrite(const std::string& name, diff --git a/packager/live_packager_test.cc b/packager/live_packager_test.cc index 0cda88eb7d4..64eb55821d9 100644 --- a/packager/live_packager_test.cc +++ b/packager/live_packager_test.cc @@ -942,4 +942,86 @@ INSTANTIATE_TEST_CASE_P( 5, "audio/en/init.mp4", LiveConfig::EncryptionScheme::SAMPLE_AES, LiveConfig::OutputFormat::TS, LiveConfig::TrackType::AUDIO, "audio/en/%05d.m4s", false})); + +TEST_F(LivePackagerBaseTest, TestPackageTimedTextVTTMp4) { + std::vector init_segment_buffer = + ReadTestDataFile("timed_text_vtt_mp4/init.mp4"); + ASSERT_FALSE(init_segment_buffer.empty()); + + for (unsigned int i = 1; i < kNumSegments; i++) { + std::string segment_num = absl::StrFormat("timed_text_vtt_mp4/%04d.m4s", i); + std::vector segment_buffer = ReadTestDataFile(segment_num); + ASSERT_FALSE(segment_buffer.empty()); + + FullSegmentBuffer in; + in.SetInitSegment(init_segment_buffer.data(), init_segment_buffer.size()); + in.AppendData(segment_buffer.data(), segment_buffer.size()); + + FullSegmentBuffer out; + + LiveConfig live_config; + live_config.format = LiveConfig::OutputFormat::FMP4; + live_config.track_type = LiveConfig::TrackType::TEXT; + live_config.protection_scheme = LiveConfig::EncryptionScheme::NONE; + + SetupLivePackagerConfig(live_config); + // TODO: investigate failure. possibly caused by bad input - generate valid + // VTT in MP4 input + ASSERT_NE(Status::OK, live_packager_->PackageTimedText(in, out)); + } +} + +struct TimedTextTestCase { + const char* media_segment_format; + LiveConfig::OutputFormat output_format; +}; + +class TimedTextParameterizedTest + : public LivePackagerBaseTest, + public ::testing::WithParamInterface { + void SetUp() override { + LivePackagerBaseTest::SetUp(); + + LiveConfig live_config; + live_config.format = GetParam().output_format; + live_config.track_type = LiveConfig::TrackType::TEXT; + live_config.protection_scheme = LiveConfig::EncryptionScheme::NONE; + SetupLivePackagerConfig(live_config); + } +}; + +TEST_P(TimedTextParameterizedTest, VerifyTimedText) { + for (unsigned int i = 0; i < kNumSegments; i++) { + std::string format_output; + + std::vector format_args; + format_args.emplace_back(i); + absl::UntypedFormatSpec format(GetParam().media_segment_format); + + ASSERT_TRUE(absl::FormatUntyped(&format_output, format, format_args)); + std::vector segment_buffer = ReadTestDataFile(format_output); + ASSERT_FALSE(segment_buffer.empty()); + + SegmentData media_seg(segment_buffer.data(), segment_buffer.size()); + FullSegmentBuffer out; + + ASSERT_EQ(Status::OK, live_packager_->PackageTimedText(media_seg, out)); + ASSERT_GT(out.SegmentSize(), 0); + } +} + +INSTANTIATE_TEST_CASE_P(LivePackagerTimedText, + TimedTextParameterizedTest, + ::testing::Values( + // VTT in text --> VTT in MP4 + TimedTextTestCase{"timed_text_vtt/%04d.vtt", + LiveConfig::OutputFormat::VTTMP4}, + // VTT in text --> TTML in Text + TimedTextTestCase{"timed_text_vtt/%04d.vtt", + LiveConfig::OutputFormat::TTML}, + // VTT in text --> TTML in MP4 + TimedTextTestCase{ + "timed_text_vtt/%04d.vtt", + LiveConfig::OutputFormat::TTMLMP4})); + } // namespace shaka diff --git a/packager/media/test/data/live_packager_tests/timed_text_vtt/0000.vtt b/packager/media/test/data/live_packager_tests/timed_text_vtt/0000.vtt new file mode 100644 index 00000000000..0d7bb026d18 --- /dev/null +++ b/packager/media/test/data/live_packager_tests/timed_text_vtt/0000.vtt @@ -0,0 +1,7 @@ +WEBVTT + +00:16.858 --> 00:19.444 +I thought you'd never get back. + +00:19.569 --> 00:21.571 +There was a heck of a crowd on the piste. diff --git a/packager/media/test/data/live_packager_tests/timed_text_vtt/0001.vtt b/packager/media/test/data/live_packager_tests/timed_text_vtt/0001.vtt new file mode 100644 index 00000000000..d25a1d8e595 --- /dev/null +++ b/packager/media/test/data/live_packager_tests/timed_text_vtt/0001.vtt @@ -0,0 +1,4 @@ +WEBVTT + +00:22.113 --> 00:23.573 +So I see. diff --git a/packager/media/test/data/live_packager_tests/timed_text_vtt/0002.vtt b/packager/media/test/data/live_packager_tests/timed_text_vtt/0002.vtt new file mode 100644 index 00000000000..e64318d5085 --- /dev/null +++ b/packager/media/test/data/live_packager_tests/timed_text_vtt/0002.vtt @@ -0,0 +1,5 @@ +WEBVTT + +00:24.616 --> 00:27.953 +- Mission accomplished? +- Best beluga. diff --git a/packager/media/test/data/live_packager_tests/timed_text_vtt/0003.vtt b/packager/media/test/data/live_packager_tests/timed_text_vtt/0003.vtt new file mode 100644 index 00000000000..acc1cd4cc82 --- /dev/null +++ b/packager/media/test/data/live_packager_tests/timed_text_vtt/0003.vtt @@ -0,0 +1,5 @@ +WEBVTT + +00:29.120 --> 00:34.042 +Vodka - rather shaken - +and one microchip. diff --git a/packager/media/test/data/live_packager_tests/timed_text_vtt/0004.vtt b/packager/media/test/data/live_packager_tests/timed_text_vtt/0004.vtt new file mode 100644 index 00000000000..f7ae1635b49 --- /dev/null +++ b/packager/media/test/data/live_packager_tests/timed_text_vtt/0004.vtt @@ -0,0 +1,4 @@ +WEBVTT + +00:34.125 --> 00:37.587 +Good. I'll make a signal to M. diff --git a/packager/media/test/data/live_packager_tests/timed_text_vtt/0005.vtt b/packager/media/test/data/live_packager_tests/timed_text_vtt/0005.vtt new file mode 100644 index 00000000000..0c8733b79fa --- /dev/null +++ b/packager/media/test/data/live_packager_tests/timed_text_vtt/0005.vtt @@ -0,0 +1,5 @@ +WEBVTT + +00:42.133 --> 00:45.470 +Be a good girl, would you, +and put her on automatic. diff --git a/packager/media/test/data/live_packager_tests/timed_text_vtt/0006.vtt b/packager/media/test/data/live_packager_tests/timed_text_vtt/0006.vtt new file mode 100644 index 00000000000..2d96097a825 --- /dev/null +++ b/packager/media/test/data/live_packager_tests/timed_text_vtt/0006.vtt @@ -0,0 +1,4 @@ +WEBVTT + +00:50.267 --> 00:53.270 +And we could do with a couple of glasses. diff --git a/packager/media/test/data/live_packager_tests/timed_text_vtt/0007.vtt b/packager/media/test/data/live_packager_tests/timed_text_vtt/0007.vtt new file mode 100644 index 00000000000..70e1aac419f --- /dev/null +++ b/packager/media/test/data/live_packager_tests/timed_text_vtt/0007.vtt @@ -0,0 +1,4 @@ +WEBVTT + +00:54.187 --> 00:56.439 +They're in the overhead rack. diff --git a/packager/media/test/data/live_packager_tests/timed_text_vtt/0008.vtt b/packager/media/test/data/live_packager_tests/timed_text_vtt/0008.vtt new file mode 100644 index 00000000000..41b49cc83ec --- /dev/null +++ b/packager/media/test/data/live_packager_tests/timed_text_vtt/0008.vtt @@ -0,0 +1,4 @@ +WEBVTT + +01:01.444 --> 01:03.280 +Commander Bond... diff --git a/packager/media/test/data/live_packager_tests/timed_text_vtt/0009.vtt b/packager/media/test/data/live_packager_tests/timed_text_vtt/0009.vtt new file mode 100644 index 00000000000..e1e43f450f5 --- /dev/null +++ b/packager/media/test/data/live_packager_tests/timed_text_vtt/0009.vtt @@ -0,0 +1,4 @@ +WEBVTT + +01:03.363 --> 01:06.950 +Call me James. It's five days to Alaska. diff --git a/packager/media/test/data/live_packager_tests/timed_text_vtt/0010.vtt b/packager/media/test/data/live_packager_tests/timed_text_vtt/0010.vtt new file mode 100644 index 00000000000..934862e6ea2 --- /dev/null +++ b/packager/media/test/data/live_packager_tests/timed_text_vtt/0010.vtt @@ -0,0 +1,5 @@ +WEBVTT + +02:53.885 --> 02:58.223 +- Thank goodness you're here, James. +- That's very nice, Moneypenny. diff --git a/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0001.m4s b/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0001.m4s new file mode 100644 index 00000000000..6baf376dc11 Binary files /dev/null and b/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0001.m4s differ diff --git a/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0002.m4s b/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0002.m4s new file mode 100644 index 00000000000..28ef24e04ee Binary files /dev/null and b/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0002.m4s differ diff --git a/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0003.m4s b/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0003.m4s new file mode 100644 index 00000000000..11c1cf29940 Binary files /dev/null and b/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0003.m4s differ diff --git a/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0004.m4s b/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0004.m4s new file mode 100644 index 00000000000..37794a6f97d Binary files /dev/null and b/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0004.m4s differ diff --git a/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0005.m4s b/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0005.m4s new file mode 100644 index 00000000000..beb94ab2dd2 Binary files /dev/null and b/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0005.m4s differ diff --git a/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0006.m4s b/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0006.m4s new file mode 100644 index 00000000000..411b7baf7c5 Binary files /dev/null and b/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0006.m4s differ diff --git a/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0007.m4s b/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0007.m4s new file mode 100644 index 00000000000..8066fb956bf Binary files /dev/null and b/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0007.m4s differ diff --git a/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0008.m4s b/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0008.m4s new file mode 100644 index 00000000000..f6e683f3cc6 Binary files /dev/null and b/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0008.m4s differ diff --git a/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0009.m4s b/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0009.m4s new file mode 100644 index 00000000000..46922fc7214 Binary files /dev/null and b/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/0009.m4s differ diff --git a/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/init.mp4 b/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/init.mp4 new file mode 100644 index 00000000000..a099b53cc52 Binary files /dev/null and b/packager/media/test/data/live_packager_tests/timed_text_vtt_mp4/init.mp4 differ