Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / media / formats / mp4 / track_run_iterator_unittest.cc
blobe220f9130bc98f6c73c300517d84826be27902d3
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/basictypes.h"
6 #include "base/logging.h"
7 #include "base/memory/scoped_ptr.h"
8 #include "base/strings/string_split.h"
9 #include "media/base/mock_media_log.h"
10 #include "media/formats/mp4/box_definitions.h"
11 #include "media/formats/mp4/rcheck.h"
12 #include "media/formats/mp4/track_run_iterator.h"
13 #include "testing/gmock/include/gmock/gmock.h"
14 #include "testing/gtest/include/gtest/gtest.h"
16 using ::testing::StrictMock;
18 // The sum of the elements in a vector initialized with SumAscending,
19 // less the value of the last element.
20 static const int kSumAscending1 = 45;
22 static const int kAudioScale = 48000;
23 static const int kVideoScale = 25;
25 static const uint8 kAuxInfo[] = {
26 0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
27 0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x32,
28 0x00, 0x02,
29 0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
30 0x00, 0x03, 0x00, 0x00, 0x00, 0x04
33 static const char kIv1[] = {
34 0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
35 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
38 static const uint8 kKeyId[] = {
39 0x41, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x54,
40 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x44
43 static const uint8 kTrackCencSampleGroupKeyId[] = {
44 0x46, 0x72, 0x61, 0x67, 0x53, 0x61, 0x6d, 0x70,
45 0x6c, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4b
48 static const uint8 kFragmentCencSampleGroupKeyId[] = {
49 0x6b, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e,
50 0x74, 0x43, 0x65, 0x6e, 0x63, 0x53, 0x61, 0x6d
53 namespace media {
54 namespace mp4 {
56 MATCHER(ReservedValueInSampleDependencyInfo, "") {
57 return CONTAINS_STRING(arg, "Reserved value used in sample dependency info.");
60 class TrackRunIteratorTest : public testing::Test {
61 public:
62 TrackRunIteratorTest() : media_log_(new StrictMock<MockMediaLog>()) {
63 CreateMovie();
66 protected:
67 Movie moov_;
68 scoped_refptr<StrictMock<MockMediaLog>> media_log_;
69 scoped_ptr<TrackRunIterator> iter_;
71 void CreateMovie() {
72 moov_.header.timescale = 1000;
73 moov_.tracks.resize(3);
74 moov_.extends.tracks.resize(2);
75 moov_.tracks[0].header.track_id = 1;
76 moov_.tracks[0].media.header.timescale = kAudioScale;
77 SampleDescription& desc1 =
78 moov_.tracks[0].media.information.sample_table.description;
79 AudioSampleEntry aud_desc;
80 aud_desc.format = FOURCC_MP4A;
81 aud_desc.sinf.info.track_encryption.is_encrypted = false;
82 desc1.type = kAudio;
83 desc1.audio_entries.push_back(aud_desc);
84 moov_.extends.tracks[0].track_id = 1;
85 moov_.extends.tracks[0].default_sample_description_index = 1;
86 moov_.tracks[1].header.track_id = 2;
87 moov_.tracks[1].media.header.timescale = kVideoScale;
88 SampleDescription& desc2 =
89 moov_.tracks[1].media.information.sample_table.description;
90 VideoSampleEntry vid_desc;
91 vid_desc.format = FOURCC_AVC1;
92 vid_desc.sinf.info.track_encryption.is_encrypted = false;
93 desc2.type = kVideo;
94 desc2.video_entries.push_back(vid_desc);
95 moov_.extends.tracks[1].track_id = 2;
96 moov_.extends.tracks[1].default_sample_description_index = 1;
98 moov_.tracks[2].header.track_id = 3;
99 moov_.tracks[2].media.information.sample_table.description.type = kHint;
102 uint32 ToSampleFlags(const std::string& str) {
103 CHECK_EQ(str.length(), 2u);
105 SampleDependsOn sample_depends_on = kSampleDependsOnReserved;
106 bool is_non_sync_sample = false;
107 switch(str[0]) {
108 case 'U':
109 sample_depends_on = kSampleDependsOnUnknown;
110 break;
111 case 'O':
112 sample_depends_on = kSampleDependsOnOthers;
113 break;
114 case 'N':
115 sample_depends_on = kSampleDependsOnNoOther;
116 break;
117 case 'R':
118 sample_depends_on = kSampleDependsOnReserved;
119 break;
120 default:
121 CHECK(false) << "Invalid sample dependency character '"
122 << str[0] << "'";
123 break;
126 switch(str[1]) {
127 case 'S':
128 is_non_sync_sample = false;
129 break;
130 case 'N':
131 is_non_sync_sample = true;
132 break;
133 default:
134 CHECK(false) << "Invalid sync sample character '"
135 << str[1] << "'";
136 break;
138 uint32 flags = static_cast<uint32>(sample_depends_on) << 24;
139 if (is_non_sync_sample)
140 flags |= kSampleIsNonSyncSample;
141 return flags;
144 void SetFlagsOnSamples(const std::string& sample_info,
145 TrackFragmentRun* trun) {
146 // US - SampleDependsOnUnknown & IsSyncSample
147 // UN - SampleDependsOnUnknown & IsNonSyncSample
148 // OS - SampleDependsOnOthers & IsSyncSample
149 // ON - SampleDependsOnOthers & IsNonSyncSample
150 // NS - SampleDependsOnNoOthers & IsSyncSample
151 // NN - SampleDependsOnNoOthers & IsNonSyncSample
152 std::vector<std::string> flags_data = base::SplitString(
153 sample_info, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
155 if (flags_data.size() == 1u) {
156 // Simulates the first_sample_flags_present set scenario,
157 // where only one sample_flag value is set and the default
158 // flags are used for everything else.
159 ASSERT_GE(trun->sample_count, flags_data.size());
160 } else {
161 ASSERT_EQ(trun->sample_count, flags_data.size());
164 trun->sample_flags.resize(flags_data.size());
165 for (size_t i = 0; i < flags_data.size(); i++)
166 trun->sample_flags[i] = ToSampleFlags(flags_data[i]);
169 std::string KeyframeAndRAPInfo(TrackRunIterator* iter) {
170 CHECK(iter->IsRunValid());
171 std::stringstream ss;
172 ss << iter->track_id();
174 while (iter->IsSampleValid()) {
175 ss << " " << (iter->is_keyframe() ? "K" : "P");
176 iter->AdvanceSample();
179 return ss.str();
182 MovieFragment CreateFragment() {
183 MovieFragment moof;
184 moof.tracks.resize(2);
185 moof.tracks[0].decode_time.decode_time = 0;
186 moof.tracks[0].header.track_id = 1;
187 moof.tracks[0].header.has_default_sample_flags = true;
188 moof.tracks[0].header.default_sample_flags = ToSampleFlags("US");
189 moof.tracks[0].header.default_sample_duration = 1024;
190 moof.tracks[0].header.default_sample_size = 4;
191 moof.tracks[0].runs.resize(2);
192 moof.tracks[0].runs[0].sample_count = 10;
193 moof.tracks[0].runs[0].data_offset = 100;
194 SetAscending(&moof.tracks[0].runs[0].sample_sizes);
196 moof.tracks[0].runs[1].sample_count = 10;
197 moof.tracks[0].runs[1].data_offset = 10000;
199 moof.tracks[1].header.track_id = 2;
200 moof.tracks[1].header.has_default_sample_flags = false;
201 moof.tracks[1].decode_time.decode_time = 10;
202 moof.tracks[1].runs.resize(1);
203 moof.tracks[1].runs[0].sample_count = 10;
204 moof.tracks[1].runs[0].data_offset = 200;
205 SetAscending(&moof.tracks[1].runs[0].sample_sizes);
206 SetAscending(&moof.tracks[1].runs[0].sample_durations);
207 SetFlagsOnSamples("US UN UN UN UN UN UN UN UN UN", &moof.tracks[1].runs[0]);
209 return moof;
212 // Update the first sample description of a Track to indicate encryption
213 void AddEncryption(Track* track) {
214 SampleDescription* stsd =
215 &track->media.information.sample_table.description;
216 ProtectionSchemeInfo* sinf;
217 if (!stsd->video_entries.empty()) {
218 sinf = &stsd->video_entries[0].sinf;
219 } else {
220 sinf = &stsd->audio_entries[0].sinf;
223 sinf->type.type = FOURCC_CENC;
224 sinf->info.track_encryption.is_encrypted = true;
225 sinf->info.track_encryption.default_iv_size = 8;
226 sinf->info.track_encryption.default_kid.assign(kKeyId,
227 kKeyId + arraysize(kKeyId));
230 // Add SampleGroupDescription Box to track level sample table and to
231 // fragment. Populate SampleToGroup Box from input array.
232 void AddCencSampleGroup(Track* track,
233 TrackFragment* frag,
234 const SampleToGroupEntry* sample_to_group_entries,
235 size_t num_entries) {
236 auto& track_cenc_group =
237 track->media.information.sample_table.sample_group_description;
238 track_cenc_group.grouping_type = FOURCC_SEIG;
239 track_cenc_group.entries.resize(1);
240 track_cenc_group.entries[0].is_encrypted = true;
241 track_cenc_group.entries[0].iv_size = 8;
242 track_cenc_group.entries[0].key_id.assign(
243 kTrackCencSampleGroupKeyId,
244 kTrackCencSampleGroupKeyId + arraysize(kTrackCencSampleGroupKeyId));
246 frag->sample_group_description.grouping_type = FOURCC_SEIG;
247 frag->sample_group_description.entries.resize(2);
248 frag->sample_group_description.entries[0].is_encrypted = false;
249 frag->sample_group_description.entries[0].iv_size = 0;
250 frag->sample_group_description.entries[1].is_encrypted = true;
251 frag->sample_group_description.entries[1].iv_size = 8;
252 frag->sample_group_description.entries[1].key_id.assign(
253 kFragmentCencSampleGroupKeyId,
254 kFragmentCencSampleGroupKeyId +
255 arraysize(kFragmentCencSampleGroupKeyId));
257 frag->sample_to_group.grouping_type = FOURCC_SEIG;
258 frag->sample_to_group.entries.assign(sample_to_group_entries,
259 sample_to_group_entries + num_entries);
262 // Add aux info covering the first track run to a TrackFragment, and update
263 // the run to ensure it matches length and subsample information.
264 void AddAuxInfoHeaders(int offset, TrackFragment* frag) {
265 frag->auxiliary_offset.offsets.push_back(offset);
266 frag->auxiliary_size.sample_count = 2;
267 frag->auxiliary_size.sample_info_sizes.push_back(8);
268 frag->auxiliary_size.sample_info_sizes.push_back(22);
269 frag->runs[0].sample_count = 2;
270 frag->runs[0].sample_sizes[1] = 10;
273 bool InitMoofWithArbitraryAuxInfo(MovieFragment* moof) {
274 // Add aux info header (equal sized aux info for every sample).
275 for (uint32 i = 0; i < moof->tracks.size(); ++i) {
276 moof->tracks[i].auxiliary_offset.offsets.push_back(50);
277 moof->tracks[i].auxiliary_size.sample_count = 10;
278 moof->tracks[i].auxiliary_size.default_sample_info_size = 8;
281 // We don't care about the actual data in aux.
282 std::vector<uint8> aux_info(1000);
283 return iter_->Init(*moof) &&
284 iter_->CacheAuxInfo(&aux_info[0], aux_info.size());
287 void SetAscending(std::vector<uint32>* vec) {
288 vec->resize(10);
289 for (size_t i = 0; i < vec->size(); i++)
290 (*vec)[i] = i+1;
294 TEST_F(TrackRunIteratorTest, NoRunsTest) {
295 iter_.reset(new TrackRunIterator(&moov_, media_log_));
296 ASSERT_TRUE(iter_->Init(MovieFragment()));
297 EXPECT_FALSE(iter_->IsRunValid());
298 EXPECT_FALSE(iter_->IsSampleValid());
301 TEST_F(TrackRunIteratorTest, BasicOperationTest) {
302 iter_.reset(new TrackRunIterator(&moov_, media_log_));
303 MovieFragment moof = CreateFragment();
305 // Test that runs are sorted correctly, and that properties of the initial
306 // sample of the first run are correct
307 ASSERT_TRUE(iter_->Init(moof));
308 EXPECT_TRUE(iter_->IsRunValid());
309 EXPECT_FALSE(iter_->is_encrypted());
310 EXPECT_EQ(iter_->track_id(), 1u);
311 EXPECT_EQ(iter_->sample_offset(), 100);
312 EXPECT_EQ(iter_->sample_size(), 1);
313 EXPECT_EQ(iter_->dts(), DecodeTimestampFromRational(0, kAudioScale));
314 EXPECT_EQ(iter_->cts(), TimeDeltaFromRational(0, kAudioScale));
315 EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(1024, kAudioScale));
316 EXPECT_TRUE(iter_->is_keyframe());
318 // Advance to the last sample in the current run, and test its properties
319 for (int i = 0; i < 9; i++) iter_->AdvanceSample();
320 EXPECT_EQ(iter_->track_id(), 1u);
321 EXPECT_EQ(iter_->sample_offset(), 100 + kSumAscending1);
322 EXPECT_EQ(iter_->sample_size(), 10);
323 EXPECT_EQ(iter_->dts(), DecodeTimestampFromRational(1024 * 9, kAudioScale));
324 EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(1024, kAudioScale));
325 EXPECT_TRUE(iter_->is_keyframe());
327 // Test end-of-run
328 iter_->AdvanceSample();
329 EXPECT_FALSE(iter_->IsSampleValid());
331 // Test last sample of next run
332 iter_->AdvanceRun();
333 EXPECT_TRUE(iter_->is_keyframe());
334 for (int i = 0; i < 9; i++) iter_->AdvanceSample();
335 EXPECT_EQ(iter_->track_id(), 2u);
336 EXPECT_EQ(iter_->sample_offset(), 200 + kSumAscending1);
337 EXPECT_EQ(iter_->sample_size(), 10);
338 int64 base_dts = kSumAscending1 + moof.tracks[1].decode_time.decode_time;
339 EXPECT_EQ(iter_->dts(), DecodeTimestampFromRational(base_dts, kVideoScale));
340 EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(10, kVideoScale));
341 EXPECT_FALSE(iter_->is_keyframe());
343 // Test final run
344 iter_->AdvanceRun();
345 EXPECT_EQ(iter_->track_id(), 1u);
346 EXPECT_EQ(iter_->dts(), DecodeTimestampFromRational(1024 * 10, kAudioScale));
347 iter_->AdvanceSample();
348 EXPECT_EQ(moof.tracks[0].runs[1].data_offset +
349 moof.tracks[0].header.default_sample_size,
350 iter_->sample_offset());
351 iter_->AdvanceRun();
352 EXPECT_FALSE(iter_->IsRunValid());
355 TEST_F(TrackRunIteratorTest, TrackExtendsDefaultsTest) {
356 moov_.extends.tracks[0].default_sample_duration = 50;
357 moov_.extends.tracks[0].default_sample_size = 3;
358 moov_.extends.tracks[0].default_sample_flags = ToSampleFlags("UN");
359 iter_.reset(new TrackRunIterator(&moov_, media_log_));
360 MovieFragment moof = CreateFragment();
361 moof.tracks[0].header.has_default_sample_flags = false;
362 moof.tracks[0].header.default_sample_size = 0;
363 moof.tracks[0].header.default_sample_duration = 0;
364 moof.tracks[0].runs[0].sample_sizes.clear();
365 ASSERT_TRUE(iter_->Init(moof));
366 iter_->AdvanceSample();
367 EXPECT_FALSE(iter_->is_keyframe());
368 EXPECT_EQ(iter_->sample_size(), 3);
369 EXPECT_EQ(iter_->sample_offset(), moof.tracks[0].runs[0].data_offset + 3);
370 EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(50, kAudioScale));
371 EXPECT_EQ(iter_->dts(), DecodeTimestampFromRational(50, kAudioScale));
374 TEST_F(TrackRunIteratorTest, FirstSampleFlagTest) {
375 // Ensure that keyframes are flagged correctly in the face of BMFF boxes which
376 // explicitly specify the flags for the first sample in a run and rely on
377 // defaults for all subsequent samples
378 iter_.reset(new TrackRunIterator(&moov_, media_log_));
379 MovieFragment moof = CreateFragment();
380 moof.tracks[1].header.has_default_sample_flags = true;
381 moof.tracks[1].header.default_sample_flags = ToSampleFlags("UN");
382 SetFlagsOnSamples("US", &moof.tracks[1].runs[0]);
384 ASSERT_TRUE(iter_->Init(moof));
385 EXPECT_EQ("1 K K K K K K K K K K", KeyframeAndRAPInfo(iter_.get()));
387 iter_->AdvanceRun();
388 EXPECT_EQ("2 K P P P P P P P P P", KeyframeAndRAPInfo(iter_.get()));
391 // Verify that parsing fails if a reserved value is in the sample flags.
392 TEST_F(TrackRunIteratorTest, SampleInfoTest_ReservedInSampleFlags) {
393 EXPECT_MEDIA_LOG(ReservedValueInSampleDependencyInfo());
394 iter_.reset(new TrackRunIterator(&moov_, media_log_));
395 MovieFragment moof = CreateFragment();
396 // Change the "depends on" field on one of the samples to a
397 // reserved value.
398 moof.tracks[1].runs[0].sample_flags[0] = ToSampleFlags("RS");
399 ASSERT_FALSE(iter_->Init(moof));
402 // Verify that parsing fails if a reserved value is in the default sample flags.
403 TEST_F(TrackRunIteratorTest, SampleInfoTest_ReservedInDefaultSampleFlags) {
404 EXPECT_MEDIA_LOG(ReservedValueInSampleDependencyInfo());
405 iter_.reset(new TrackRunIterator(&moov_, media_log_));
406 MovieFragment moof = CreateFragment();
407 // Set the default flag to contain a reserved "depends on" value.
408 moof.tracks[0].header.default_sample_flags = ToSampleFlags("RN");
409 ASSERT_FALSE(iter_->Init(moof));
412 TEST_F(TrackRunIteratorTest, ReorderingTest) {
413 // Test frame reordering and edit list support. The frames have the following
414 // decode timestamps:
416 // 0ms 40ms 120ms 240ms
417 // | 0 | 1 - | 2 - - |
419 // ...and these composition timestamps, after edit list adjustment:
421 // 0ms 40ms 160ms 240ms
422 // | 0 | 2 - - | 1 - |
424 // Create an edit list with one entry, with an initial start time of 80ms
425 // (that is, 2 / kVideoTimescale) and a duration of zero (which is treated as
426 // infinite according to 14496-12:2012). This will cause the first 80ms of the
427 // media timeline - which will be empty, due to CTS biasing - to be discarded.
428 iter_.reset(new TrackRunIterator(&moov_, media_log_));
429 EditListEntry entry;
430 entry.segment_duration = 0;
431 entry.media_time = 2;
432 entry.media_rate_integer = 1;
433 entry.media_rate_fraction = 0;
434 moov_.tracks[1].edit.list.edits.push_back(entry);
436 // Add CTS offsets. Without bias, the CTS offsets for the first three frames
437 // would simply be [0, 3, -2]. Since CTS offsets should be non-negative for
438 // maximum compatibility, these values are biased up to [2, 5, 0], and the
439 // extra 80ms is removed via the edit list.
440 MovieFragment moof = CreateFragment();
441 std::vector<int32>& cts_offsets =
442 moof.tracks[1].runs[0].sample_composition_time_offsets;
443 cts_offsets.resize(10);
444 cts_offsets[0] = 2;
445 cts_offsets[1] = 5;
446 cts_offsets[2] = 0;
447 moof.tracks[1].decode_time.decode_time = 0;
449 ASSERT_TRUE(iter_->Init(moof));
450 iter_->AdvanceRun();
451 EXPECT_EQ(iter_->dts(), DecodeTimestampFromRational(0, kVideoScale));
452 EXPECT_EQ(iter_->cts(), TimeDeltaFromRational(0, kVideoScale));
453 EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(1, kVideoScale));
454 iter_->AdvanceSample();
455 EXPECT_EQ(iter_->dts(), DecodeTimestampFromRational(1, kVideoScale));
456 EXPECT_EQ(iter_->cts(), TimeDeltaFromRational(4, kVideoScale));
457 EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(2, kVideoScale));
458 iter_->AdvanceSample();
459 EXPECT_EQ(iter_->dts(), DecodeTimestampFromRational(3, kVideoScale));
460 EXPECT_EQ(iter_->cts(), TimeDeltaFromRational(1, kVideoScale));
461 EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(3, kVideoScale));
464 TEST_F(TrackRunIteratorTest, IgnoreUnknownAuxInfoTest) {
465 iter_.reset(new TrackRunIterator(&moov_, media_log_));
466 MovieFragment moof = CreateFragment();
467 moof.tracks[1].auxiliary_offset.offsets.push_back(50);
468 moof.tracks[1].auxiliary_size.default_sample_info_size = 2;
469 moof.tracks[1].auxiliary_size.sample_count = 2;
470 moof.tracks[1].runs[0].sample_count = 2;
471 ASSERT_TRUE(iter_->Init(moof));
472 iter_->AdvanceRun();
473 EXPECT_FALSE(iter_->AuxInfoNeedsToBeCached());
476 TEST_F(TrackRunIteratorTest, DecryptConfigTest) {
477 AddEncryption(&moov_.tracks[1]);
478 iter_.reset(new TrackRunIterator(&moov_, media_log_));
480 MovieFragment moof = CreateFragment();
481 AddAuxInfoHeaders(50, &moof.tracks[1]);
483 ASSERT_TRUE(iter_->Init(moof));
485 // The run for track 2 will be first, since its aux info offset is the first
486 // element in the file.
487 EXPECT_EQ(iter_->track_id(), 2u);
488 EXPECT_TRUE(iter_->is_encrypted());
489 EXPECT_TRUE(iter_->AuxInfoNeedsToBeCached());
490 EXPECT_EQ(static_cast<uint32>(iter_->aux_info_size()), arraysize(kAuxInfo));
491 EXPECT_EQ(iter_->aux_info_offset(), 50);
492 EXPECT_EQ(iter_->GetMaxClearOffset(), 50);
493 EXPECT_FALSE(iter_->CacheAuxInfo(NULL, 0));
494 EXPECT_FALSE(iter_->CacheAuxInfo(kAuxInfo, 3));
495 EXPECT_TRUE(iter_->AuxInfoNeedsToBeCached());
496 EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
497 EXPECT_FALSE(iter_->AuxInfoNeedsToBeCached());
498 EXPECT_EQ(iter_->sample_offset(), 200);
499 EXPECT_EQ(iter_->GetMaxClearOffset(), moof.tracks[0].runs[0].data_offset);
500 scoped_ptr<DecryptConfig> config = iter_->GetDecryptConfig();
501 ASSERT_EQ(arraysize(kKeyId), config->key_id().size());
502 EXPECT_TRUE(!memcmp(kKeyId, config->key_id().data(),
503 config->key_id().size()));
504 ASSERT_EQ(arraysize(kIv1), config->iv().size());
505 EXPECT_TRUE(!memcmp(kIv1, config->iv().data(), config->iv().size()));
506 EXPECT_TRUE(config->subsamples().empty());
507 iter_->AdvanceSample();
508 config = iter_->GetDecryptConfig();
509 EXPECT_EQ(config->subsamples().size(), 2u);
510 EXPECT_EQ(config->subsamples()[0].clear_bytes, 1u);
511 EXPECT_EQ(config->subsamples()[1].cypher_bytes, 4u);
514 TEST_F(TrackRunIteratorTest, CencSampleGroupTest) {
515 MovieFragment moof = CreateFragment();
517 const SampleToGroupEntry kSampleToGroupTable[] = {
518 // Associated with the second entry in SampleGroupDescription Box.
519 {1, SampleToGroupEntry::kFragmentGroupDescriptionIndexBase + 2},
520 // Associated with the first entry in SampleGroupDescription Box.
521 {1, SampleToGroupEntry::kFragmentGroupDescriptionIndexBase + 1}};
522 AddCencSampleGroup(&moov_.tracks[0], &moof.tracks[0], kSampleToGroupTable,
523 arraysize(kSampleToGroupTable));
525 iter_.reset(new TrackRunIterator(&moov_, media_log_));
526 ASSERT_TRUE(InitMoofWithArbitraryAuxInfo(&moof));
528 std::string cenc_sample_group_key_id(
529 kFragmentCencSampleGroupKeyId,
530 kFragmentCencSampleGroupKeyId + arraysize(kFragmentCencSampleGroupKeyId));
531 // The first sample is encrypted and the second sample is unencrypted.
532 EXPECT_TRUE(iter_->is_encrypted());
533 EXPECT_EQ(cenc_sample_group_key_id, iter_->GetDecryptConfig()->key_id());
534 iter_->AdvanceSample();
535 EXPECT_FALSE(iter_->is_encrypted());
538 TEST_F(TrackRunIteratorTest, CencSampleGroupWithTrackEncryptionBoxTest) {
539 // Add TrackEncryption Box.
540 AddEncryption(&moov_.tracks[0]);
542 MovieFragment moof = CreateFragment();
544 const SampleToGroupEntry kSampleToGroupTable[] = {
545 // Associated with the 2nd entry in fragment SampleGroupDescription Box.
546 {2, SampleToGroupEntry::kFragmentGroupDescriptionIndexBase + 2},
547 // Associated with the default values specified in TrackEncryption Box.
548 {1, 0},
549 // Associated with the 1st entry in fragment SampleGroupDescription Box.
550 {3, SampleToGroupEntry::kFragmentGroupDescriptionIndexBase + 1},
551 // Associated with the 1st entry in track SampleGroupDescription Box.
552 {2, 1}};
553 AddCencSampleGroup(&moov_.tracks[0], &moof.tracks[0], kSampleToGroupTable,
554 arraysize(kSampleToGroupTable));
556 iter_.reset(new TrackRunIterator(&moov_, media_log_));
557 ASSERT_TRUE(InitMoofWithArbitraryAuxInfo(&moof));
559 std::string track_encryption_key_id(kKeyId, kKeyId + arraysize(kKeyId));
560 std::string track_cenc_sample_group_key_id(
561 kTrackCencSampleGroupKeyId,
562 kTrackCencSampleGroupKeyId + arraysize(kTrackCencSampleGroupKeyId));
563 std::string fragment_cenc_sample_group_key_id(
564 kFragmentCencSampleGroupKeyId,
565 kFragmentCencSampleGroupKeyId + arraysize(kFragmentCencSampleGroupKeyId));
567 for (size_t i = 0; i < kSampleToGroupTable[0].sample_count; ++i) {
568 EXPECT_TRUE(iter_->is_encrypted());
569 EXPECT_EQ(fragment_cenc_sample_group_key_id,
570 iter_->GetDecryptConfig()->key_id());
571 iter_->AdvanceSample();
574 for (size_t i = 0; i < kSampleToGroupTable[1].sample_count; ++i) {
575 EXPECT_TRUE(iter_->is_encrypted());
576 EXPECT_EQ(track_encryption_key_id, iter_->GetDecryptConfig()->key_id());
577 iter_->AdvanceSample();
580 for (size_t i = 0; i < kSampleToGroupTable[2].sample_count; ++i) {
581 EXPECT_FALSE(iter_->is_encrypted());
582 iter_->AdvanceSample();
585 for (size_t i = 0; i < kSampleToGroupTable[3].sample_count; ++i) {
586 EXPECT_TRUE(iter_->is_encrypted());
587 EXPECT_EQ(track_cenc_sample_group_key_id,
588 iter_->GetDecryptConfig()->key_id());
589 iter_->AdvanceSample();
592 // The remaining samples should be associated with the default values
593 // specified in TrackEncryption Box.
594 EXPECT_TRUE(iter_->is_encrypted());
595 EXPECT_EQ(track_encryption_key_id, iter_->GetDecryptConfig()->key_id());
598 // It is legal for aux info blocks to be shared among multiple formats.
599 TEST_F(TrackRunIteratorTest, SharedAuxInfoTest) {
600 AddEncryption(&moov_.tracks[0]);
601 AddEncryption(&moov_.tracks[1]);
602 iter_.reset(new TrackRunIterator(&moov_, media_log_));
604 MovieFragment moof = CreateFragment();
605 moof.tracks[0].runs.resize(1);
606 AddAuxInfoHeaders(50, &moof.tracks[0]);
607 AddAuxInfoHeaders(50, &moof.tracks[1]);
608 moof.tracks[0].auxiliary_size.default_sample_info_size = 8;
610 ASSERT_TRUE(iter_->Init(moof));
611 EXPECT_EQ(iter_->track_id(), 1u);
612 EXPECT_EQ(iter_->aux_info_offset(), 50);
613 EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
614 scoped_ptr<DecryptConfig> config = iter_->GetDecryptConfig();
615 ASSERT_EQ(arraysize(kIv1), config->iv().size());
616 EXPECT_TRUE(!memcmp(kIv1, config->iv().data(), config->iv().size()));
617 iter_->AdvanceSample();
618 EXPECT_EQ(iter_->GetMaxClearOffset(), 50);
619 iter_->AdvanceRun();
620 EXPECT_EQ(iter_->GetMaxClearOffset(), 50);
621 EXPECT_EQ(iter_->aux_info_offset(), 50);
622 EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
623 EXPECT_EQ(iter_->GetMaxClearOffset(), 200);
624 ASSERT_EQ(arraysize(kIv1), config->iv().size());
625 EXPECT_TRUE(!memcmp(kIv1, config->iv().data(), config->iv().size()));
626 iter_->AdvanceSample();
627 EXPECT_EQ(iter_->GetMaxClearOffset(), 201);
630 // Sensible files are expected to place auxiliary information for a run
631 // immediately before the main data for that run. Alternative schemes are
632 // possible, however, including the somewhat reasonable behavior of placing all
633 // aux info at the head of the 'mdat' box together, and the completely
634 // unreasonable behavior demonstrated here:
635 // byte 50: track 2, run 1 aux info
636 // byte 100: track 1, run 1 data
637 // byte 200: track 2, run 1 data
638 // byte 201: track 1, run 2 aux info (*inside* track 2, run 1 data)
639 // byte 10000: track 1, run 2 data
640 // byte 20000: track 1, run 1 aux info
641 TEST_F(TrackRunIteratorTest, UnexpectedOrderingTest) {
642 AddEncryption(&moov_.tracks[0]);
643 AddEncryption(&moov_.tracks[1]);
644 iter_.reset(new TrackRunIterator(&moov_, media_log_));
646 MovieFragment moof = CreateFragment();
647 AddAuxInfoHeaders(20000, &moof.tracks[0]);
648 moof.tracks[0].auxiliary_offset.offsets.push_back(201);
649 moof.tracks[0].auxiliary_size.sample_count += 2;
650 moof.tracks[0].auxiliary_size.default_sample_info_size = 8;
651 moof.tracks[0].runs[1].sample_count = 2;
652 AddAuxInfoHeaders(50, &moof.tracks[1]);
653 moof.tracks[1].runs[0].sample_sizes[0] = 5;
655 ASSERT_TRUE(iter_->Init(moof));
656 EXPECT_EQ(iter_->track_id(), 2u);
657 EXPECT_EQ(iter_->aux_info_offset(), 50);
658 EXPECT_EQ(iter_->sample_offset(), 200);
659 EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
660 EXPECT_EQ(iter_->GetMaxClearOffset(), 100);
661 iter_->AdvanceRun();
662 EXPECT_EQ(iter_->track_id(), 1u);
663 EXPECT_EQ(iter_->aux_info_offset(), 20000);
664 EXPECT_EQ(iter_->sample_offset(), 100);
665 EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
666 EXPECT_EQ(iter_->GetMaxClearOffset(), 100);
667 iter_->AdvanceSample();
668 EXPECT_EQ(iter_->GetMaxClearOffset(), 101);
669 iter_->AdvanceRun();
670 EXPECT_EQ(iter_->track_id(), 1u);
671 EXPECT_EQ(iter_->aux_info_offset(), 201);
672 EXPECT_EQ(iter_->sample_offset(), 10000);
673 EXPECT_EQ(iter_->GetMaxClearOffset(), 201);
674 EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
675 EXPECT_EQ(iter_->GetMaxClearOffset(), 10000);
678 TEST_F(TrackRunIteratorTest, KeyFrameFlagCombinations) {
679 // Setup both audio and video tracks to each have 6 samples covering all the
680 // combinations of mp4 "sync sample" and "depends on" relationships.
681 MovieFragment moof = CreateFragment();
682 moof.tracks[0].runs.resize(1);
683 moof.tracks[1].runs.resize(1);
684 moof.tracks[0].runs[0].sample_count = 6;
685 moof.tracks[1].runs[0].sample_count = 6;
686 SetFlagsOnSamples("US UN OS ON NS NN", &moof.tracks[0].runs[0]);
687 SetFlagsOnSamples("US UN OS ON NS NN", &moof.tracks[1].runs[0]);
688 iter_.reset(new TrackRunIterator(&moov_, media_log_));
690 ASSERT_TRUE(iter_->Init(moof));
691 EXPECT_TRUE(iter_->IsRunValid());
693 // Keyframes should be marked according to downstream's expectations that
694 // keyframes serve as points of random access for seeking.
696 // For audio, any sync sample should be marked as a key frame. Whether a
697 // sample "depends on" other samples is not considered. Unlike video samples,
698 // audio samples are often marked as depending on other samples but are still
699 // workable for random access. While we allow for parsing of audio samples
700 // that are non-sync samples, we generally expect all audio samples to be sync
701 // samples and downstream will log and discard any non-sync audio samples.
702 EXPECT_EQ("1 K P K P K P", KeyframeAndRAPInfo(iter_.get()));
704 iter_->AdvanceRun();
706 // For video, any key frame should be both a sync sample and have no known
707 // dependents. Ideally, a video sync sample should always be marked as having
708 // no dependents, but we occasionally encounter media where all samples are
709 // marked "sync" and we must rely on combining the two flags to pick out the
710 // true key frames. See http://crbug.com/310712 and http://crbug.com/507916.
711 // Realiably knowing the keyframes for video is also critical to SPS PPS
712 // insertion.
713 EXPECT_EQ("2 K P P P K P", KeyframeAndRAPInfo(iter_.get()));
716 } // namespace mp4
717 } // namespace media