summaryrefslogtreecommitdiffstats
path: root/media/mp4/track_run_iterator_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'media/mp4/track_run_iterator_unittest.cc')
-rw-r--r--media/mp4/track_run_iterator_unittest.cc240
1 files changed, 211 insertions, 29 deletions
diff --git a/media/mp4/track_run_iterator_unittest.cc b/media/mp4/track_run_iterator_unittest.cc
index 0b5a967..e1c1a03 100644
--- a/media/mp4/track_run_iterator_unittest.cc
+++ b/media/mp4/track_run_iterator_unittest.cc
@@ -19,6 +19,24 @@ static const int kVideoScale = 25;
static const uint32 kSampleIsDifferenceSampleFlagMask = 0x10000;
+static const uint8 kAuxInfo[] = {
+ 0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
+ 0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x32,
+ 0x00, 0x02,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x03, 0x00, 0x00, 0x00, 0x04
+};
+
+static const char kIv1[] = {
+ 0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8 kKeyId[] = {
+ 0x41, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x54,
+ 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x44
+};
+
namespace media {
namespace mp4 {
@@ -40,19 +58,25 @@ class TrackRunIteratorTest : public testing::Test {
moov_.tracks[0].media.header.timescale = kAudioScale;
SampleDescription& desc1 =
moov_.tracks[0].media.information.sample_table.description;
+ AudioSampleEntry aud_desc;
+ aud_desc.format = FOURCC_MP4A;
+ aud_desc.sinf.info.track_encryption.is_encrypted = false;
desc1.type = kAudio;
- desc1.audio_entries.resize(1);
- desc1.audio_entries[0].format = FOURCC_MP4A;
+ desc1.audio_entries.push_back(aud_desc);
moov_.extends.tracks[0].track_id = 1;
+ moov_.extends.tracks[0].default_sample_description_index = 1;
moov_.tracks[1].header.track_id = 2;
moov_.tracks[1].media.header.timescale = kVideoScale;
SampleDescription& desc2 =
moov_.tracks[1].media.information.sample_table.description;
+ VideoSampleEntry vid_desc;
+ vid_desc.format = FOURCC_AVC1;
+ vid_desc.sinf.info.track_encryption.is_encrypted = false;
desc2.type = kVideo;
- desc2.video_entries.resize(1);
- desc2.video_entries[0].sinf.info.track_encryption.is_encrypted = false;
+ desc2.video_entries.push_back(vid_desc);
moov_.extends.tracks[1].track_id = 2;
+ moov_.extends.tracks[1].default_sample_description_index = 1;
moov_.tracks[2].header.track_id = 3;
moov_.tracks[2].media.information.sample_table.description.type = kHint;
@@ -66,7 +90,6 @@ class TrackRunIteratorTest : public testing::Test {
moof.tracks[0].header.has_default_sample_flags = true;
moof.tracks[0].header.default_sample_duration = 1024;
moof.tracks[0].header.default_sample_size = 4;
- moof.tracks[0].header.sample_description_index = 0;
moof.tracks[0].runs.resize(2);
moof.tracks[0].runs[0].sample_count = 10;
moof.tracks[0].runs[0].data_offset = 100;
@@ -77,7 +100,6 @@ class TrackRunIteratorTest : public testing::Test {
moof.tracks[1].header.track_id = 2;
moof.tracks[1].header.has_default_sample_flags = false;
- moof.tracks[1].header.sample_description_index = 0;
moof.tracks[1].decode_time.decode_time = 10;
moof.tracks[1].runs.resize(1);
moof.tracks[1].runs[0].sample_count = 10;
@@ -93,6 +115,36 @@ class TrackRunIteratorTest : public testing::Test {
return moof;
}
+ // Update the first sample description of a Track to indicate encryption
+ void AddEncryption(Track* track) {
+ SampleDescription* stsd =
+ &track->media.information.sample_table.description;
+ ProtectionSchemeInfo* sinf;
+ if (!stsd->video_entries.empty()) {
+ sinf = &stsd->video_entries[0].sinf;
+ } else {
+ sinf = &stsd->audio_entries[0].sinf;
+ }
+
+ sinf->type.type = FOURCC_CENC;
+ sinf->info.track_encryption.is_encrypted = true;
+ sinf->info.track_encryption.default_iv_size = 8;
+ sinf->info.track_encryption.default_kid.insert(
+ sinf->info.track_encryption.default_kid.begin(),
+ kKeyId, kKeyId + arraysize(kKeyId));
+ }
+
+ // Add aux info covering the first track run to a TrackFragment, and update
+ // the run to ensure it matches length and subsample information.
+ void AddAuxInfoHeaders(int offset, TrackFragment* frag) {
+ frag->auxiliary_offset.offsets.push_back(offset);
+ frag->auxiliary_size.sample_count = 2;
+ frag->auxiliary_size.sample_info_sizes.push_back(8);
+ frag->auxiliary_size.sample_info_sizes.push_back(22);
+ frag->runs[0].sample_count = 2;
+ frag->runs[0].sample_sizes[1] = 10;
+ }
+
void SetAscending(std::vector<uint32>* vec) {
vec->resize(10);
for (size_t i = 0; i < vec->size(); i++)
@@ -103,8 +155,8 @@ class TrackRunIteratorTest : public testing::Test {
TEST_F(TrackRunIteratorTest, NoRunsTest) {
iter_.reset(new TrackRunIterator(&moov_));
ASSERT_TRUE(iter_->Init(MovieFragment()));
- EXPECT_FALSE(iter_->RunIsValid());
- EXPECT_FALSE(iter_->SampleIsValid());
+ EXPECT_FALSE(iter_->IsRunValid());
+ EXPECT_FALSE(iter_->IsSampleValid());
}
TEST_F(TrackRunIteratorTest, BasicOperationTest) {
@@ -114,14 +166,14 @@ TEST_F(TrackRunIteratorTest, BasicOperationTest) {
// Test that runs are sorted correctly, and that properties of the initial
// sample of the first run are correct
ASSERT_TRUE(iter_->Init(moof));
- EXPECT_TRUE(iter_->RunIsValid());
+ EXPECT_TRUE(iter_->IsRunValid());
EXPECT_FALSE(iter_->is_encrypted());
EXPECT_EQ(iter_->track_id(), 1u);
EXPECT_EQ(iter_->sample_offset(), 100);
EXPECT_EQ(iter_->sample_size(), 1);
- EXPECT_EQ(iter_->dts(), TimeDeltaFromFrac(0, kAudioScale));
- EXPECT_EQ(iter_->cts(), TimeDeltaFromFrac(0, kAudioScale));
- EXPECT_EQ(iter_->duration(), TimeDeltaFromFrac(1024, kAudioScale));
+ EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(0, kAudioScale));
+ EXPECT_EQ(iter_->cts(), TimeDeltaFromRational(0, kAudioScale));
+ EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(1024, kAudioScale));
EXPECT_TRUE(iter_->is_keyframe());
// Advance to the last sample in the current run, and test its properties
@@ -129,13 +181,13 @@ TEST_F(TrackRunIteratorTest, BasicOperationTest) {
EXPECT_EQ(iter_->track_id(), 1u);
EXPECT_EQ(iter_->sample_offset(), 100 + kSumAscending1);
EXPECT_EQ(iter_->sample_size(), 10);
- EXPECT_EQ(iter_->dts(), TimeDeltaFromFrac(1024 * 9, kAudioScale));
- EXPECT_EQ(iter_->duration(), TimeDeltaFromFrac(1024, kAudioScale));
+ EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(1024 * 9, kAudioScale));
+ EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(1024, kAudioScale));
EXPECT_TRUE(iter_->is_keyframe());
// Test end-of-run
iter_->AdvanceSample();
- EXPECT_FALSE(iter_->SampleIsValid());
+ EXPECT_FALSE(iter_->IsSampleValid());
// Test last sample of next run
iter_->AdvanceRun();
@@ -145,20 +197,20 @@ TEST_F(TrackRunIteratorTest, BasicOperationTest) {
EXPECT_EQ(iter_->sample_offset(), 200 + kSumAscending1);
EXPECT_EQ(iter_->sample_size(), 10);
int64 base_dts = kSumAscending1 + moof.tracks[1].decode_time.decode_time;
- EXPECT_EQ(iter_->dts(), TimeDeltaFromFrac(base_dts, kVideoScale));
- EXPECT_EQ(iter_->duration(), TimeDeltaFromFrac(10, kVideoScale));
+ EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(base_dts, kVideoScale));
+ EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(10, kVideoScale));
EXPECT_FALSE(iter_->is_keyframe());
// Test final run
iter_->AdvanceRun();
EXPECT_EQ(iter_->track_id(), 1u);
- EXPECT_EQ(iter_->dts(), TimeDeltaFromFrac(1024 * 10, kAudioScale));
+ EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(1024 * 10, kAudioScale));
iter_->AdvanceSample();
EXPECT_EQ(moof.tracks[0].runs[1].data_offset +
moof.tracks[0].header.default_sample_size,
iter_->sample_offset());
iter_->AdvanceRun();
- EXPECT_FALSE(iter_->RunIsValid());
+ EXPECT_FALSE(iter_->IsRunValid());
}
TEST_F(TrackRunIteratorTest, TrackExtendsDefaultsTest) {
@@ -177,8 +229,8 @@ TEST_F(TrackRunIteratorTest, TrackExtendsDefaultsTest) {
EXPECT_FALSE(iter_->is_keyframe());
EXPECT_EQ(iter_->sample_size(), 3);
EXPECT_EQ(iter_->sample_offset(), moof.tracks[0].runs[0].data_offset + 3);
- EXPECT_EQ(iter_->duration(), TimeDeltaFromFrac(50, kAudioScale));
- EXPECT_EQ(iter_->dts(), TimeDeltaFromFrac(50, kAudioScale));
+ EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(50, kAudioScale));
+ EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(50, kAudioScale));
}
TEST_F(TrackRunIteratorTest, FirstSampleFlagTest) {
@@ -203,8 +255,8 @@ TEST_F(TrackRunIteratorTest, MinDecodeTest) {
MovieFragment moof = CreateFragment();
moof.tracks[0].decode_time.decode_time = kAudioScale;
ASSERT_TRUE(iter_->Init(moof));
- EXPECT_EQ(TimeDeltaFromFrac(moof.tracks[1].decode_time.decode_time,
- kVideoScale),
+ EXPECT_EQ(TimeDeltaFromRational(moof.tracks[1].decode_time.decode_time,
+ kVideoScale),
iter_->GetMinDecodeTimestamp());
}
@@ -219,13 +271,143 @@ TEST_F(TrackRunIteratorTest, ReorderingTest) {
moof.tracks[1].decode_time.decode_time = 0;
ASSERT_TRUE(iter_->Init(moof));
iter_->AdvanceRun();
- EXPECT_EQ(iter_->dts(), TimeDeltaFromFrac(0, kVideoScale));
- EXPECT_EQ(iter_->cts(), TimeDeltaFromFrac(2, kVideoScale));
- EXPECT_EQ(iter_->duration(), TimeDeltaFromFrac(1, kVideoScale));
+ EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(0, kVideoScale));
+ EXPECT_EQ(iter_->cts(), TimeDeltaFromRational(2, kVideoScale));
+ EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(1, kVideoScale));
iter_->AdvanceSample();
- EXPECT_EQ(iter_->dts(), TimeDeltaFromFrac(1, kVideoScale));
- EXPECT_EQ(iter_->cts(), TimeDeltaFromFrac(0, kVideoScale));
- EXPECT_EQ(iter_->duration(), TimeDeltaFromFrac(2, kVideoScale));
+ EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(1, kVideoScale));
+ EXPECT_EQ(iter_->cts(), TimeDeltaFromRational(0, kVideoScale));
+ EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(2, kVideoScale));
+}
+
+TEST_F(TrackRunIteratorTest, IgnoreUnknownAuxInfoTest) {
+ iter_.reset(new TrackRunIterator(&moov_));
+ MovieFragment moof = CreateFragment();
+ moof.tracks[1].auxiliary_offset.offsets.push_back(50);
+ moof.tracks[1].auxiliary_size.default_sample_info_size = 2;
+ moof.tracks[1].auxiliary_size.sample_count = 2;
+ moof.tracks[1].runs[0].sample_count = 2;
+ ASSERT_TRUE(iter_->Init(moof));
+ iter_->AdvanceRun();
+ EXPECT_FALSE(iter_->AuxInfoNeedsToBeCached());
+}
+
+TEST_F(TrackRunIteratorTest, DecryptConfigTest) {
+ AddEncryption(&moov_.tracks[1]);
+ iter_.reset(new TrackRunIterator(&moov_));
+
+ MovieFragment moof = CreateFragment();
+ AddAuxInfoHeaders(50, &moof.tracks[1]);
+
+ ASSERT_TRUE(iter_->Init(moof));
+
+ // The run for track 2 will be first, since its aux info offset is the first
+ // element in the file.
+ EXPECT_EQ(iter_->track_id(), 2u);
+ EXPECT_TRUE(iter_->is_encrypted());
+ EXPECT_TRUE(iter_->AuxInfoNeedsToBeCached());
+ EXPECT_EQ(static_cast<uint32>(iter_->aux_info_size()), arraysize(kAuxInfo));
+ EXPECT_EQ(iter_->aux_info_offset(), 50);
+ EXPECT_EQ(iter_->GetMaxClearOffset(), 50);
+ EXPECT_FALSE(iter_->CacheAuxInfo(NULL, 0));
+ EXPECT_FALSE(iter_->CacheAuxInfo(kAuxInfo, 3));
+ EXPECT_TRUE(iter_->AuxInfoNeedsToBeCached());
+ EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
+ EXPECT_FALSE(iter_->AuxInfoNeedsToBeCached());
+ EXPECT_EQ(iter_->sample_offset(), 200);
+ EXPECT_EQ(iter_->GetMaxClearOffset(), moof.tracks[0].runs[0].data_offset);
+ scoped_ptr<DecryptConfig> config = iter_->GetDecryptConfig();
+ ASSERT_EQ(arraysize(kKeyId), config->key_id().size());
+ EXPECT_TRUE(!memcmp(kKeyId, config->key_id().data(),
+ config->key_id().size()));
+ ASSERT_EQ(arraysize(kIv1), config->iv().size());
+ EXPECT_TRUE(!memcmp(kIv1, config->iv().data(), config->iv().size()));
+ EXPECT_TRUE(config->subsamples().empty());
+ iter_->AdvanceSample();
+ config = iter_->GetDecryptConfig();
+ EXPECT_EQ(config->subsamples().size(), 2u);
+ EXPECT_EQ(config->subsamples()[0].clear_bytes, 1u);
+ EXPECT_EQ(config->subsamples()[1].cypher_bytes, 4u);
+}
+
+// It is legal for aux info blocks to be shared among multiple formats.
+TEST_F(TrackRunIteratorTest, SharedAuxInfoTest) {
+ AddEncryption(&moov_.tracks[0]);
+ AddEncryption(&moov_.tracks[1]);
+ iter_.reset(new TrackRunIterator(&moov_));
+
+ MovieFragment moof = CreateFragment();
+ moof.tracks[0].runs.resize(1);
+ AddAuxInfoHeaders(50, &moof.tracks[0]);
+ AddAuxInfoHeaders(50, &moof.tracks[1]);
+ moof.tracks[0].auxiliary_size.default_sample_info_size = 8;
+
+ ASSERT_TRUE(iter_->Init(moof));
+ EXPECT_EQ(iter_->track_id(), 1u);
+ EXPECT_EQ(iter_->aux_info_offset(), 50);
+ EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
+ scoped_ptr<DecryptConfig> config = iter_->GetDecryptConfig();
+ ASSERT_EQ(arraysize(kIv1), config->iv().size());
+ EXPECT_TRUE(!memcmp(kIv1, config->iv().data(), config->iv().size()));
+ iter_->AdvanceSample();
+ EXPECT_EQ(iter_->GetMaxClearOffset(), 50);
+ iter_->AdvanceRun();
+ EXPECT_EQ(iter_->GetMaxClearOffset(), 50);
+ EXPECT_EQ(iter_->aux_info_offset(), 50);
+ EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
+ EXPECT_EQ(iter_->GetMaxClearOffset(), 200);
+ ASSERT_EQ(arraysize(kIv1), config->iv().size());
+ EXPECT_TRUE(!memcmp(kIv1, config->iv().data(), config->iv().size()));
+ iter_->AdvanceSample();
+ EXPECT_EQ(iter_->GetMaxClearOffset(), 201);
+}
+
+// Sensible files are expected to place auxiliary information for a run
+// immediately before the main data for that run. Alternative schemes are
+// possible, however, including the somewhat reasonable behavior of placing all
+// aux info at the head of the 'mdat' box together, and the completely
+// unreasonable behavior demonstrated here:
+// byte 50: track 2, run 1 aux info
+// byte 100: track 1, run 1 data
+// byte 200: track 2, run 1 data
+// byte 201: track 1, run 2 aux info (*inside* track 2, run 1 data)
+// byte 10000: track 1, run 2 data
+// byte 20000: track 1, run 1 aux info
+TEST_F(TrackRunIteratorTest, UnexpectedOrderingTest) {
+ AddEncryption(&moov_.tracks[0]);
+ AddEncryption(&moov_.tracks[1]);
+ iter_.reset(new TrackRunIterator(&moov_));
+
+ MovieFragment moof = CreateFragment();
+ AddAuxInfoHeaders(20000, &moof.tracks[0]);
+ moof.tracks[0].auxiliary_offset.offsets.push_back(201);
+ moof.tracks[0].auxiliary_size.sample_count += 2;
+ moof.tracks[0].auxiliary_size.default_sample_info_size = 8;
+ moof.tracks[0].runs[1].sample_count = 2;
+ AddAuxInfoHeaders(50, &moof.tracks[1]);
+ moof.tracks[1].runs[0].sample_sizes[0] = 5;
+
+ ASSERT_TRUE(iter_->Init(moof));
+ EXPECT_EQ(iter_->track_id(), 2u);
+ EXPECT_EQ(iter_->aux_info_offset(), 50);
+ EXPECT_EQ(iter_->sample_offset(), 200);
+ EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
+ EXPECT_EQ(iter_->GetMaxClearOffset(), 100);
+ iter_->AdvanceRun();
+ EXPECT_EQ(iter_->track_id(), 1u);
+ EXPECT_EQ(iter_->aux_info_offset(), 20000);
+ EXPECT_EQ(iter_->sample_offset(), 100);
+ EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
+ EXPECT_EQ(iter_->GetMaxClearOffset(), 100);
+ iter_->AdvanceSample();
+ EXPECT_EQ(iter_->GetMaxClearOffset(), 101);
+ iter_->AdvanceRun();
+ EXPECT_EQ(iter_->track_id(), 1u);
+ EXPECT_EQ(iter_->aux_info_offset(), 201);
+ EXPECT_EQ(iter_->sample_offset(), 10000);
+ EXPECT_EQ(iter_->GetMaxClearOffset(), 201);
+ EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
+ EXPECT_EQ(iter_->GetMaxClearOffset(), 10000);
}
} // namespace mp4