From ee407107b75225b8ce7094bac6dbf88e5a3f5996 Mon Sep 17 00:00:00 2001 From: David Chen <89227356+chenda6@users.noreply.github.com> Date: Mon, 13 May 2024 09:38:05 -0700 Subject: [PATCH] fix: preserve EditList from source (#27) --- packager/live_packager_test.cc | 58 +++++++++++++++++++ packager/media/base/stream_info.h | 6 ++ .../media/formats/mp4/mp4_media_parser.cc | 10 ++++ packager/media/formats/mp4/mp4_muxer.cc | 5 ++ 4 files changed, 79 insertions(+) diff --git a/packager/live_packager_test.cc b/packager/live_packager_test.cc index 0332b865455..b895da47f26 100644 --- a/packager/live_packager_test.cc +++ b/packager/live_packager_test.cc @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -296,6 +297,31 @@ class MP4MediaParserTest { std::vector> emsg_samples_; }; +bool GetBox(const Segment& buffer, media::mp4::Box& out) { + bool err(true); + size_t bytes_to_read(buffer.Size()); + const uint8_t* data(buffer.Data()); + + while (bytes_to_read > 0) { + std::unique_ptr reader( + media::mp4::BoxReader::ReadBox(data, bytes_to_read, &err)); + + if (err) { + return false; + } + + if (reader->type() == out.BoxType()) { + out.Parse(reader.get()); + return true; + } + + data += reader->size(); + bytes_to_read -= reader->size(); + } + + return false; +} + void CheckVideoInitSegment(const SegmentBuffer& buffer, media::FourCC format) { bool err(true); size_t bytes_to_read(buffer.Size()); @@ -844,6 +870,38 @@ TEST_F(LivePackagerBaseTest, VerifyPrdDecryptReEncrypt) { } } +TEST_F(LivePackagerBaseTest, EditListAfterRepackage) { + std::vector init_segment_buffer = + ReadTestDataFile("encrypted/prd_data/init.mp4"); + ASSERT_FALSE(init_segment_buffer.empty()); + + LiveConfig live_config; + live_config.format = LiveConfig::OutputFormat::FMP4; + live_config.track_type = LiveConfig::TrackType::VIDEO; + live_config.protection_scheme = LiveConfig::EncryptionScheme::CENC; + live_config.decryption_key = HexStringToVector(kKeyHex); + live_config.decryption_key_id = HexStringToVector(kKeyIdHex); + SetupLivePackagerConfig(live_config); + + SegmentData init_seg(init_segment_buffer.data(), init_segment_buffer.size()); + SegmentBuffer actual_buf; + const auto status = live_packager_->PackageInit(init_seg, actual_buf); + ASSERT_EQ(Status::OK, status); + ASSERT_GT(actual_buf.Size(), 0); + + media::mp4::Movie exp_moov; + ASSERT_TRUE(GetBox(init_seg, exp_moov)); + media::mp4::Movie act_moov; + ASSERT_TRUE(GetBox(actual_buf, act_moov)); + + ASSERT_EQ(exp_moov.tracks.size(), act_moov.tracks.size()); + for (size_t i(0); i < exp_moov.tracks.size(); ++i) { + const auto& exp_track = exp_moov.tracks[i]; + const auto& act_track = act_moov.tracks[i]; + EXPECT_EQ(exp_track.edit.list.edits, act_track.edit.list.edits); + } +} + TEST_F(LivePackagerBaseTest, EncryptionFailure) { std::vector init_segment_buffer = ReadTestDataFile("input/init.mp4"); ASSERT_FALSE(init_segment_buffer.empty()); diff --git a/packager/media/base/stream_info.h b/packager/media/base/stream_info.h index 4ad716741fa..3317dc6d781 100644 --- a/packager/media/base/stream_info.h +++ b/packager/media/base/stream_info.h @@ -106,6 +106,7 @@ class StreamInfo { const EncryptionConfig& encryption_config() const { return encryption_config_; } + int64_t media_time() const { return media_time_; } void set_duration(int64_t duration) { duration_ = duration; } void set_codec(Codec codec) { codec_ = codec; } @@ -123,6 +124,7 @@ class StreamInfo { void set_encryption_config(const EncryptionConfig& encryption_config) { encryption_config_ = encryption_config; } + void set_media_time(int64_t media_time) { media_time_ = media_time; } private: // Whether the stream is Audio or Video. @@ -146,6 +148,10 @@ class StreamInfo { // codebooks. std::vector codec_config_; + // Optional data required for preserving edit lists when repackaging an + // init segment alone. + int64_t media_time_ = 0; + // Not using DISALLOW_COPY_AND_ASSIGN here intentionally to allow the compiler // generated copy constructor and assignment operator. Since the extra data is // typically small, the performance impact is minimal. diff --git a/packager/media/formats/mp4/mp4_media_parser.cc b/packager/media/formats/mp4/mp4_media_parser.cc index 999be7883dc..1ebf126678b 100644 --- a/packager/media/formats/mp4/mp4_media_parser.cc +++ b/packager/media/formats/mp4/mp4_media_parser.cc @@ -590,6 +590,11 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) { num_channels, sampling_frequency, seek_preroll_ns, codec_delay_ns, max_bitrate, avg_bitrate, track->media.header.language.code, is_encrypted)); + + const EditList& edit_list = track->edit.list; + if (edit_list.edits.size() == 1u) { + streams.back()->set_media_time(edit_list.edits.front().media_time); + } } if (samp_descr.type == kVideo) { @@ -758,6 +763,11 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) { pssh_raw_data.size()); } + const EditList& edit_list = track->edit.list; + if (edit_list.edits.size() == 1u) { + video_stream_info->set_media_time(edit_list.edits.front().media_time); + } + streams.push_back(video_stream_info); } } diff --git a/packager/media/formats/mp4/mp4_muxer.cc b/packager/media/formats/mp4/mp4_muxer.cc index 013e58f8fc4..e5c61bff1df 100644 --- a/packager/media/formats/mp4/mp4_muxer.cc +++ b/packager/media/formats/mp4/mp4_muxer.cc @@ -293,6 +293,11 @@ Status MP4Muxer::DelayInitializeMuxer() { entry.media_time = edit_list_offset_.value(); entry.media_rate_integer = 1; trak.edit.list.edits.push_back(entry); + } else if (stream->media_time() != 0) { + EditListEntry entry; + entry.media_time = stream->media_time(); + entry.media_rate_integer = 1; + trak.edit.list.edits.push_back(entry); } if (stream->is_encrypted() && options().mp4_params.include_pssh_in_stream) {