Skip to content

Commit

Permalink
Merge branch 'pluto-cmake' of github.com:Pluto-tv/shaka-packager-live…
Browse files Browse the repository at this point in the history
… into feature/TRANS-5811-custom-logging

# Conflicts:
#	packager/live_packager_test.cc
  • Loading branch information
lee.fordyce committed May 31, 2024
2 parents 4f0c09e + 5521bd9 commit f34401c
Show file tree
Hide file tree
Showing 39 changed files with 220 additions and 13 deletions.
150 changes: 138 additions & 12 deletions packager/live_packager_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <packager/media/formats/mp2t/ts_packet.h>
#include <packager/media/formats/mp2t/ts_section.h>
#include <packager/media/formats/mp4/box_definitions.h>
#include <packager/media/formats/mp4/box_definitions_comparison.h>
#include <packager/media/formats/mp4/box_reader.h>
#include <packager/media/formats/mp4/mp4_media_parser.h>

Expand Down Expand Up @@ -298,6 +299,31 @@ class MP4MediaParserTest {
std::vector<std::shared_ptr<media::mp4::DASHEventMessageBox>> 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<media::mp4::BoxReader> 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());
Expand Down Expand Up @@ -846,6 +872,41 @@ TEST_F(LivePackagerBaseTest, VerifyPrdDecryptReEncrypt) {
}
}

TEST_F(LivePackagerBaseTest, MoovAfterRepackage) {
std::vector<uint8_t> 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.extends.header, act_moov.extends.header);

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);
}

EXPECT_EQ(exp_moov.extends.tracks, act_moov.extends.tracks);
}

TEST_F(LivePackagerBaseTest, EncryptionFailure) {
std::vector<uint8_t> init_segment_buffer = ReadTestDataFile("input/init.mp4");
ASSERT_FALSE(init_segment_buffer.empty());
Expand Down Expand Up @@ -1016,6 +1077,8 @@ struct LivePackagerTestCase {
LiveConfig::TrackType track_type;
const char* media_segment_format;
bool compare_samples;
std::string exp_init;
const char* exp_media_seg;
};

class LivePackagerEncryptionTest
Expand All @@ -1035,13 +1098,14 @@ class LivePackagerEncryptionTest
protected:
static std::vector<uint8_t> ReadExpectedData() {
// TODO: make this more generic to handle mp2t as well
std::vector<uint8_t> buf = ReadTestDataFile("expected/fmp4/init.mp4");
std::vector<uint8_t> buf = ReadTestDataFile(GetParam().exp_init);
for (unsigned int i = 0; i < GetParam().num_segments; i++) {
auto seg_buf =
ReadTestDataFile(absl::StrFormat("expected/fmp4/%04d.m4s", i + 1));
buf.insert(buf.end(), seg_buf.begin(), seg_buf.end());
std::string input_fname;
if (FormatWithIndex(GetParam().exp_media_seg, i, input_fname)) {
auto seg_buf = ReadTestDataFile(input_fname);
buf.insert(buf.end(), seg_buf.begin(), seg_buf.end());
}
}

return buf;
}

Expand Down Expand Up @@ -1117,32 +1181,62 @@ INSTANTIATE_TEST_CASE_P(
LivePackagerTestCase{
10, "input/init.mp4", LiveConfig::EncryptionScheme::SAMPLE_AES,
LiveConfig::OutputFormat::TS, LiveConfig::TrackType::VIDEO,
"input/%04d.m4s", false},
"input/%04d.m4s", false, "expected/fmp4/init.mp4",
"expected/fmp4/%04d.m4s"},
// Verify FMP4 to TS with AES-128 encryption.
LivePackagerTestCase{
10, "input/init.mp4", LiveConfig::EncryptionScheme::AES_128,
LiveConfig::OutputFormat::TS, LiveConfig::TrackType::VIDEO,
"input/%04d.m4s", false},
"input/%04d.m4s", false, "expected/fmp4/init.mp4",
"expected/fmp4/%04d.m4s"},
// Verify FMP4 to FMP4 with Sample AES encryption.
LivePackagerTestCase{
10, "input/init.mp4", LiveConfig::EncryptionScheme::SAMPLE_AES,
LiveConfig::OutputFormat::FMP4, LiveConfig::TrackType::VIDEO,
"input/%04d.m4s", true},
"input/%04d.m4s", true, "expected/fmp4/init.mp4",
"expected/fmp4/%04d.m4s"},
// Verify FMP4 to FMP4 with CENC encryption.
LivePackagerTestCase{
10, "input/init.mp4", LiveConfig::EncryptionScheme::CENC,
LiveConfig::OutputFormat::FMP4, LiveConfig::TrackType::VIDEO,
"input/%04d.m4s", true},
"input/%04d.m4s", true, "expected/fmp4/init.mp4",
"expected/fmp4/%04d.m4s"},
// Verify FMP4 to FMP4 with CBCS encryption.
LivePackagerTestCase{
10, "input/init.mp4", LiveConfig::EncryptionScheme::CBCS,
LiveConfig::OutputFormat::FMP4, LiveConfig::TrackType::VIDEO,
"input/%04d.m4s", true},
"input/%04d.m4s", true, "expected/fmp4/init.mp4",
"expected/fmp4/%04d.m4s"},
// Verify AUDIO segments only to TS with Sample AES encryption.
LivePackagerTestCase{
5, "audio/en/init.mp4", LiveConfig::EncryptionScheme::SAMPLE_AES,
LiveConfig::OutputFormat::TS, LiveConfig::TrackType::AUDIO,
"audio/en/%05d.m4s", false}));
"audio/en/%05d.m4s", false, "expected/fmp4/init.mp4",
"expected/fmp4/%04d.m4s"},
// Verify packaging of CMAF Video segments, no encryption
LivePackagerTestCase{
10, "cmaf/video/init.mp4", LiveConfig::EncryptionScheme::NONE,
LiveConfig::OutputFormat::FMP4, LiveConfig::TrackType::VIDEO,
"cmaf/video/seg_34313817%01d.m4s", true, "cmaf/video/init.mp4",
"cmaf/video/seg_34313817%01d.m4s"},
// Verify packaging of CMAF Audio segments
LivePackagerTestCase{
10, "cmaf/audio/init.mp4", LiveConfig::EncryptionScheme::NONE,
LiveConfig::OutputFormat::FMP4, LiveConfig::TrackType::AUDIO,
"cmaf/audio/seg_34313817%01d.m4s", true, "cmaf/audio/init.mp4",
"cmaf/audio/seg_34313817%01d.m4s"},
// Verify packaging of CMAF audio segments, with encryption
LivePackagerTestCase{
10, "cmaf/audio/init.mp4", LiveConfig::EncryptionScheme::CENC,
LiveConfig::OutputFormat::FMP4, LiveConfig::TrackType::AUDIO,
"cmaf/audio/seg_34313817%01d.m4s", true, "cmaf/audio/init.mp4",
"cmaf/audio/seg_34313817%01d.m4s"},
// Verify packaging of CMAF video segments, with encryption
LivePackagerTestCase{
10, "cmaf/video/init.mp4", LiveConfig::EncryptionScheme::CENC,
LiveConfig::OutputFormat::FMP4, LiveConfig::TrackType::VIDEO,
"cmaf/video/seg_34313817%01d.m4s", true, "cmaf/video/init.mp4",
"cmaf/video/seg_34313817%01d.m4s"}));

struct LivePackagerReEncryptCase {
unsigned int num_segments;
Expand Down Expand Up @@ -1471,7 +1565,7 @@ TEST(LivePackagerLoggingTest, InvalidDecryptKeyID) {
"(ERROR): Error retrieving decryption key: 14 (INTERNAL_ERROR): Key for key_id=00000000621f2afe7ab2c868d5fd2e2e was not found.",
"(ERROR): Cannot decrypt samples.",
"(ERROR): Error while parsing MP4",
};
};

int num_errors = 0;
const auto messages = lp_getErrorMessages(&num_errors);
Expand All @@ -1483,6 +1577,38 @@ TEST(LivePackagerLoggingTest, InvalidDecryptKeyID) {
lp_freeErrorMessages(messages, num_errors);
lp_removeCustomLogSink();
}

// Exercise edge case found in webvtt_to_mp4_handler for large decode times.
// Issue was a narrow conversion from int64_t to int (32 bit) for segment_start.
// Valid decode times can be int64_t.
TEST_F(LivePackagerBaseTest, TestCmafTimedText) {
std::vector<uint8_t> segment_buffer =
ReadTestDataFile("timed_text/cmaf/text_cmaf_fragment_343138171.vtt");
ASSERT_FALSE(segment_buffer.empty());

SegmentData media_seg(segment_buffer.data(), segment_buffer.size());

FullSegmentBuffer out;

LiveConfig live_config;
live_config.format = LiveConfig::OutputFormat::VTTMP4;
live_config.track_type = LiveConfig::TrackType::TEXT;
live_config.protection_scheme = LiveConfig::EncryptionScheme::NONE;
live_config.segment_number = 343138171;
live_config.timed_text_decode_time = (154412176500000 / 90000) * 1000;

SetupLivePackagerConfig(live_config);
ASSERT_EQ(Status::OK, live_packager_->PackageTimedText(media_seg, out));
ASSERT_GT(out.SegmentSize(), 0);

CheckTextInitSegment(out, media::FourCC::FOURCC_text,
media::FourCC::FOURCC_wvtt);

SegmentBuffer seg;
seg.AppendData(out.SegmentData(), out.SegmentSize());
CheckSegment(live_config, seg, 1000, true);
}

} // namespace shaka

int main(int argc, char** argv) {
Expand Down
26 changes: 26 additions & 0 deletions packager/media/base/stream_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,13 @@ class StreamInfo {
const EncryptionConfig& encryption_config() const {
return encryption_config_;
}
int64_t media_time() const { return media_time_; }
uint32_t get_default_sample_duration() const {
return default_sample_duration_;
}
int64_t get_default_fragment_duration() const {
return default_fragment_duration_;
};

void set_duration(int64_t duration) { duration_ = duration; }
void set_codec(Codec codec) { codec_ = codec; }
Expand All @@ -123,6 +130,14 @@ 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; }
void set_default_sample_duration(uint32_t duration) {
default_sample_duration_ = duration;
}

void set_default_fragment_duration(int64_t default_fragment_duration) {
default_fragment_duration_ = default_fragment_duration;
}

private:
// Whether the stream is Audio or Video.
Expand All @@ -146,6 +161,17 @@ class StreamInfo {
// codebooks.
std::vector<uint8_t> codec_config_;

// Optional data required for preserving media time when repackaging an
// init segment alone.
int64_t media_time_ = 0;
// Optional data required for preserving default sample duration when
// repackaging an init segment alone.
uint32_t default_sample_duration_ = 0;

// Data to indicate whether the fragment duration originated in the movie
// extends header and should carry over to the output segment.
int64_t default_fragment_duration_ = 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.
Expand Down
33 changes: 33 additions & 0 deletions packager/media/formats/mp4/mp4_media_parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -410,12 +410,16 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {

// Calculate duration (based on timescale).
int64_t duration = 0;
int64_t default_fragment_duration = 0;
if (track->media.header.duration > 0) {
duration = track->media.header.duration;
} else if (moov_->extends.header.fragment_duration > 0) {
DCHECK(moov_->header.timescale != 0);
duration = Rescale(moov_->extends.header.fragment_duration,
moov_->header.timescale, timescale);
if (duration > 0) {
default_fragment_duration = duration;
}
} else if (moov_->header.duration > 0 &&
moov_->header.duration != std::numeric_limits<uint64_t>::max()) {
DCHECK(moov_->header.timescale != 0);
Expand Down Expand Up @@ -590,6 +594,20 @@ 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));
streams.back()->set_default_fragment_duration(default_fragment_duration);

const EditList& edit_list = track->edit.list;
if (edit_list.edits.size() == 1u) {
streams.back()->set_media_time(edit_list.edits.front().media_time);
}

for (const auto& trex : moov_->extends.tracks) {
if (trex.track_id == track->header.track_id) {
streams.back()->set_default_sample_duration(
trex.default_sample_duration);
break;
}
}
}

if (samp_descr.type == kVideo) {
Expand Down Expand Up @@ -758,6 +776,21 @@ 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);
}

for (const auto& trex : moov_->extends.tracks) {
if (trex.track_id == track->header.track_id) {
video_stream_info->set_default_sample_duration(
trex.default_sample_duration);
break;
}
}
video_stream_info->set_default_fragment_duration(
default_fragment_duration);

streams.push_back(video_stream_info);
}
}
Expand Down
13 changes: 13 additions & 0 deletions packager/media/formats/mp4/mp4_muxer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,11 @@ Status MP4Muxer::DelayInitializeMuxer() {
// supported yet.
if (codec_fourcc != FOURCC_avc3 && codec_fourcc != FOURCC_hev1)
ftyp->compatible_brands.push_back(FOURCC_cmfc);

// Carry over movie extends header duration from init segment.
if (streams()[0].get()->get_default_fragment_duration() > 0)
moov->extends.header.fragment_duration =
streams()[0].get()->get_default_fragment_duration();
}

moov->header.creation_time = IsoTimeNow();
Expand All @@ -264,6 +269,9 @@ Status MP4Muxer::DelayInitializeMuxer() {
TrackExtends& trex = moov->extends.tracks[i];
trex.track_id = trak.header.track_id;
trex.default_sample_description_index = 1;
if (stream->get_default_sample_duration() != 0) {
trex.default_sample_duration = stream->get_default_sample_duration();
}

bool generate_trak_result = false;
switch (stream->stream_type()) {
Expand Down Expand Up @@ -293,6 +301,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) {
Expand Down
2 changes: 1 addition & 1 deletion packager/media/formats/webvtt/webvtt_to_mp4_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ Status WebVttToMp4Handler::DispatchCurrentSegment(int64_t segment_start,

// Move through the segment, jumping between each change to the current state.
// A change is defined as a group of one or more DisplayActions.
int section_start = segment_start;
int64_t section_start = segment_start;

// |actions| is a map of [time] -> [action].
auto actions = CreateActionList(segment_start, segment_end, current_segment_);
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
WEBVTT
1
476580:47:31.733 --> 476580:47:34.600 align:center
was three boxes downat number 22.

2
476580:47:34.666 --> 476580:47:35.000 align:center
And now that prizeis in position here.

0 comments on commit f34401c

Please sign in to comment.