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/formats/mp4/box_definitions.h"
10 #include "media/formats/mp4/rcheck.h"
11 #include "media/formats/mp4/track_run_iterator.h"
12 #include "testing/gtest/include/gtest/gtest.h"
14 // The sum of the elements in a vector initialized with SumAscending,
15 // less the value of the last element.
16 static const int kSumAscending1
= 45;
18 static const int kAudioScale
= 48000;
19 static const int kVideoScale
= 25;
21 static const uint8 kAuxInfo
[] = {
22 0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
23 0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x32,
25 0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
26 0x00, 0x03, 0x00, 0x00, 0x00, 0x04
29 static const char kIv1
[] = {
30 0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
31 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
34 static const uint8 kKeyId
[] = {
35 0x41, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x54,
36 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x44
39 static const uint8 kTrackCencSampleGroupKeyId
[] = {
40 0x46, 0x72, 0x61, 0x67, 0x53, 0x61, 0x6d, 0x70,
41 0x6c, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4b
44 static const uint8 kFragmentCencSampleGroupKeyId
[] = {
45 0x6b, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e,
46 0x74, 0x43, 0x65, 0x6e, 0x63, 0x53, 0x61, 0x6d
52 class TrackRunIteratorTest
: public testing::Test
{
54 TrackRunIteratorTest() : media_log_(new MediaLog()) { CreateMovie(); }
58 scoped_refptr
<MediaLog
> media_log_
;
59 scoped_ptr
<TrackRunIterator
> iter_
;
62 moov_
.header
.timescale
= 1000;
63 moov_
.tracks
.resize(3);
64 moov_
.extends
.tracks
.resize(2);
65 moov_
.tracks
[0].header
.track_id
= 1;
66 moov_
.tracks
[0].media
.header
.timescale
= kAudioScale
;
67 SampleDescription
& desc1
=
68 moov_
.tracks
[0].media
.information
.sample_table
.description
;
69 AudioSampleEntry aud_desc
;
70 aud_desc
.format
= FOURCC_MP4A
;
71 aud_desc
.sinf
.info
.track_encryption
.is_encrypted
= false;
73 desc1
.audio_entries
.push_back(aud_desc
);
74 moov_
.extends
.tracks
[0].track_id
= 1;
75 moov_
.extends
.tracks
[0].default_sample_description_index
= 1;
76 moov_
.tracks
[0].media
.information
.sample_table
.sync_sample
.is_present
=
78 moov_
.tracks
[1].header
.track_id
= 2;
79 moov_
.tracks
[1].media
.header
.timescale
= kVideoScale
;
80 SampleDescription
& desc2
=
81 moov_
.tracks
[1].media
.information
.sample_table
.description
;
82 VideoSampleEntry vid_desc
;
83 vid_desc
.format
= FOURCC_AVC1
;
84 vid_desc
.sinf
.info
.track_encryption
.is_encrypted
= false;
86 desc2
.video_entries
.push_back(vid_desc
);
87 moov_
.extends
.tracks
[1].track_id
= 2;
88 moov_
.extends
.tracks
[1].default_sample_description_index
= 1;
89 SyncSample
& video_sync_sample
=
90 moov_
.tracks
[1].media
.information
.sample_table
.sync_sample
;
91 video_sync_sample
.is_present
= true;
92 video_sync_sample
.entries
.resize(1);
93 video_sync_sample
.entries
[0] = 0;
95 moov_
.tracks
[2].header
.track_id
= 3;
96 moov_
.tracks
[2].media
.information
.sample_table
.description
.type
= kHint
;
99 uint32
ToSampleFlags(const std::string
& str
) {
100 CHECK_EQ(str
.length(), 2u);
102 SampleDependsOn sample_depends_on
= kSampleDependsOnReserved
;
103 bool is_non_sync_sample
= false;
106 sample_depends_on
= kSampleDependsOnUnknown
;
109 sample_depends_on
= kSampleDependsOnOthers
;
112 sample_depends_on
= kSampleDependsOnNoOther
;
115 sample_depends_on
= kSampleDependsOnReserved
;
118 CHECK(false) << "Invalid sample dependency character '"
125 is_non_sync_sample
= false;
128 is_non_sync_sample
= true;
131 CHECK(false) << "Invalid sync sample character '"
135 uint32 flags
= static_cast<uint32
>(sample_depends_on
) << 24;
136 if (is_non_sync_sample
)
137 flags
|= kSampleIsNonSyncSample
;
141 void SetFlagsOnSamples(const std::string
& sample_info
,
142 TrackFragmentRun
* trun
) {
143 // US - SampleDependsOnUnknown & IsSyncSample
144 // UN - SampleDependsOnUnknown & IsNonSyncSample
145 // OS - SampleDependsOnOthers & IsSyncSample
146 // ON - SampleDependsOnOthers & IsNonSyncSample
147 // NS - SampleDependsOnNoOthers & IsSyncSample
148 // NN - SampleDependsOnNoOthers & IsNonSyncSample
149 std::vector
<std::string
> flags_data
;
150 base::SplitString(sample_info
, ' ', &flags_data
);
152 if (flags_data
.size() == 1u) {
153 // Simulates the first_sample_flags_present set scenario,
154 // where only one sample_flag value is set and the default
155 // flags are used for everything else.
156 ASSERT_GE(trun
->sample_count
, flags_data
.size());
158 ASSERT_EQ(trun
->sample_count
, flags_data
.size());
161 trun
->sample_flags
.resize(flags_data
.size());
162 for (size_t i
= 0; i
< flags_data
.size(); i
++)
163 trun
->sample_flags
[i
] = ToSampleFlags(flags_data
[i
]);
166 std::string
KeyframeAndRAPInfo(TrackRunIterator
* iter
) {
167 CHECK(iter
->IsRunValid());
168 std::stringstream ss
;
169 ss
<< iter
->track_id();
171 while (iter
->IsSampleValid()) {
172 ss
<< " " << (iter
->is_keyframe() ? "K" : "P");
173 if (iter
->is_random_access_point())
175 iter
->AdvanceSample();
181 MovieFragment
CreateFragment() {
183 moof
.tracks
.resize(2);
184 moof
.tracks
[0].decode_time
.decode_time
= 0;
185 moof
.tracks
[0].header
.track_id
= 1;
186 moof
.tracks
[0].header
.has_default_sample_flags
= true;
187 moof
.tracks
[0].header
.default_sample_flags
= ToSampleFlags("US");
188 moof
.tracks
[0].header
.default_sample_duration
= 1024;
189 moof
.tracks
[0].header
.default_sample_size
= 4;
190 moof
.tracks
[0].runs
.resize(2);
191 moof
.tracks
[0].runs
[0].sample_count
= 10;
192 moof
.tracks
[0].runs
[0].data_offset
= 100;
193 SetAscending(&moof
.tracks
[0].runs
[0].sample_sizes
);
195 moof
.tracks
[0].runs
[1].sample_count
= 10;
196 moof
.tracks
[0].runs
[1].data_offset
= 10000;
198 moof
.tracks
[1].header
.track_id
= 2;
199 moof
.tracks
[1].header
.has_default_sample_flags
= false;
200 moof
.tracks
[1].decode_time
.decode_time
= 10;
201 moof
.tracks
[1].runs
.resize(1);
202 moof
.tracks
[1].runs
[0].sample_count
= 10;
203 moof
.tracks
[1].runs
[0].data_offset
= 200;
204 SetAscending(&moof
.tracks
[1].runs
[0].sample_sizes
);
205 SetAscending(&moof
.tracks
[1].runs
[0].sample_durations
);
206 SetFlagsOnSamples("US UN UN UN UN UN UN UN UN UN", &moof
.tracks
[1].runs
[0]);
211 // Update the first sample description of a Track to indicate encryption
212 void AddEncryption(Track
* track
) {
213 SampleDescription
* stsd
=
214 &track
->media
.information
.sample_table
.description
;
215 ProtectionSchemeInfo
* sinf
;
216 if (!stsd
->video_entries
.empty()) {
217 sinf
= &stsd
->video_entries
[0].sinf
;
219 sinf
= &stsd
->audio_entries
[0].sinf
;
222 sinf
->type
.type
= FOURCC_CENC
;
223 sinf
->info
.track_encryption
.is_encrypted
= true;
224 sinf
->info
.track_encryption
.default_iv_size
= 8;
225 sinf
->info
.track_encryption
.default_kid
.assign(kKeyId
,
226 kKeyId
+ arraysize(kKeyId
));
229 // Add SampleGroupDescription Box to track level sample table and to
230 // fragment. Populate SampleToGroup Box from input array.
231 void AddCencSampleGroup(Track
* track
,
233 const SampleToGroupEntry
* sample_to_group_entries
,
234 size_t num_entries
) {
235 auto& track_cenc_group
=
236 track
->media
.information
.sample_table
.sample_group_description
;
237 track_cenc_group
.grouping_type
= FOURCC_SEIG
;
238 track_cenc_group
.entries
.resize(1);
239 track_cenc_group
.entries
[0].is_encrypted
= true;
240 track_cenc_group
.entries
[0].iv_size
= 8;
241 track_cenc_group
.entries
[0].key_id
.assign(
242 kTrackCencSampleGroupKeyId
,
243 kTrackCencSampleGroupKeyId
+ arraysize(kTrackCencSampleGroupKeyId
));
245 frag
->sample_group_description
.grouping_type
= FOURCC_SEIG
;
246 frag
->sample_group_description
.entries
.resize(2);
247 frag
->sample_group_description
.entries
[0].is_encrypted
= false;
248 frag
->sample_group_description
.entries
[0].iv_size
= 0;
249 frag
->sample_group_description
.entries
[1].is_encrypted
= true;
250 frag
->sample_group_description
.entries
[1].iv_size
= 8;
251 frag
->sample_group_description
.entries
[1].key_id
.assign(
252 kFragmentCencSampleGroupKeyId
,
253 kFragmentCencSampleGroupKeyId
+
254 arraysize(kFragmentCencSampleGroupKeyId
));
256 frag
->sample_to_group
.grouping_type
= FOURCC_SEIG
;
257 frag
->sample_to_group
.entries
.assign(sample_to_group_entries
,
258 sample_to_group_entries
+ num_entries
);
261 // Add aux info covering the first track run to a TrackFragment, and update
262 // the run to ensure it matches length and subsample information.
263 void AddAuxInfoHeaders(int offset
, TrackFragment
* frag
) {
264 frag
->auxiliary_offset
.offsets
.push_back(offset
);
265 frag
->auxiliary_size
.sample_count
= 2;
266 frag
->auxiliary_size
.sample_info_sizes
.push_back(8);
267 frag
->auxiliary_size
.sample_info_sizes
.push_back(22);
268 frag
->runs
[0].sample_count
= 2;
269 frag
->runs
[0].sample_sizes
[1] = 10;
272 bool InitMoofWithArbitraryAuxInfo(MovieFragment
* moof
) {
273 // Add aux info header (equal sized aux info for every sample).
274 for (uint32 i
= 0; i
< moof
->tracks
.size(); ++i
) {
275 moof
->tracks
[i
].auxiliary_offset
.offsets
.push_back(50);
276 moof
->tracks
[i
].auxiliary_size
.sample_count
= 10;
277 moof
->tracks
[i
].auxiliary_size
.default_sample_info_size
= 8;
280 // We don't care about the actual data in aux.
281 std::vector
<uint8
> aux_info(1000);
282 return iter_
->Init(*moof
) &&
283 iter_
->CacheAuxInfo(&aux_info
[0], aux_info
.size());
286 void SetAscending(std::vector
<uint32
>* vec
) {
288 for (size_t i
= 0; i
< vec
->size(); i
++)
293 TEST_F(TrackRunIteratorTest
, NoRunsTest
) {
294 iter_
.reset(new TrackRunIterator(&moov_
, media_log_
));
295 ASSERT_TRUE(iter_
->Init(MovieFragment()));
296 EXPECT_FALSE(iter_
->IsRunValid());
297 EXPECT_FALSE(iter_
->IsSampleValid());
300 TEST_F(TrackRunIteratorTest
, BasicOperationTest
) {
301 iter_
.reset(new TrackRunIterator(&moov_
, media_log_
));
302 MovieFragment moof
= CreateFragment();
304 // Test that runs are sorted correctly, and that properties of the initial
305 // sample of the first run are correct
306 ASSERT_TRUE(iter_
->Init(moof
));
307 EXPECT_TRUE(iter_
->IsRunValid());
308 EXPECT_FALSE(iter_
->is_encrypted());
309 EXPECT_EQ(iter_
->track_id(), 1u);
310 EXPECT_EQ(iter_
->sample_offset(), 100);
311 EXPECT_EQ(iter_
->sample_size(), 1);
312 EXPECT_EQ(iter_
->dts(), DecodeTimestampFromRational(0, kAudioScale
));
313 EXPECT_EQ(iter_
->cts(), TimeDeltaFromRational(0, kAudioScale
));
314 EXPECT_EQ(iter_
->duration(), TimeDeltaFromRational(1024, kAudioScale
));
315 EXPECT_TRUE(iter_
->is_keyframe());
317 // Advance to the last sample in the current run, and test its properties
318 for (int i
= 0; i
< 9; i
++) iter_
->AdvanceSample();
319 EXPECT_EQ(iter_
->track_id(), 1u);
320 EXPECT_EQ(iter_
->sample_offset(), 100 + kSumAscending1
);
321 EXPECT_EQ(iter_
->sample_size(), 10);
322 EXPECT_EQ(iter_
->dts(), DecodeTimestampFromRational(1024 * 9, kAudioScale
));
323 EXPECT_EQ(iter_
->duration(), TimeDeltaFromRational(1024, kAudioScale
));
324 EXPECT_TRUE(iter_
->is_keyframe());
327 iter_
->AdvanceSample();
328 EXPECT_FALSE(iter_
->IsSampleValid());
330 // Test last sample of next run
332 EXPECT_TRUE(iter_
->is_keyframe());
333 for (int i
= 0; i
< 9; i
++) iter_
->AdvanceSample();
334 EXPECT_EQ(iter_
->track_id(), 2u);
335 EXPECT_EQ(iter_
->sample_offset(), 200 + kSumAscending1
);
336 EXPECT_EQ(iter_
->sample_size(), 10);
337 int64 base_dts
= kSumAscending1
+ moof
.tracks
[1].decode_time
.decode_time
;
338 EXPECT_EQ(iter_
->dts(), DecodeTimestampFromRational(base_dts
, kVideoScale
));
339 EXPECT_EQ(iter_
->duration(), TimeDeltaFromRational(10, kVideoScale
));
340 EXPECT_FALSE(iter_
->is_keyframe());
344 EXPECT_EQ(iter_
->track_id(), 1u);
345 EXPECT_EQ(iter_
->dts(), DecodeTimestampFromRational(1024 * 10, kAudioScale
));
346 iter_
->AdvanceSample();
347 EXPECT_EQ(moof
.tracks
[0].runs
[1].data_offset
+
348 moof
.tracks
[0].header
.default_sample_size
,
349 iter_
->sample_offset());
351 EXPECT_FALSE(iter_
->IsRunValid());
354 TEST_F(TrackRunIteratorTest
, TrackExtendsDefaultsTest
) {
355 moov_
.extends
.tracks
[0].default_sample_duration
= 50;
356 moov_
.extends
.tracks
[0].default_sample_size
= 3;
357 moov_
.extends
.tracks
[0].default_sample_flags
= ToSampleFlags("UN");
358 iter_
.reset(new TrackRunIterator(&moov_
, media_log_
));
359 MovieFragment moof
= CreateFragment();
360 moof
.tracks
[0].header
.has_default_sample_flags
= false;
361 moof
.tracks
[0].header
.default_sample_size
= 0;
362 moof
.tracks
[0].header
.default_sample_duration
= 0;
363 moof
.tracks
[0].runs
[0].sample_sizes
.clear();
364 ASSERT_TRUE(iter_
->Init(moof
));
365 iter_
->AdvanceSample();
366 EXPECT_FALSE(iter_
->is_keyframe());
367 EXPECT_EQ(iter_
->sample_size(), 3);
368 EXPECT_EQ(iter_
->sample_offset(), moof
.tracks
[0].runs
[0].data_offset
+ 3);
369 EXPECT_EQ(iter_
->duration(), TimeDeltaFromRational(50, kAudioScale
));
370 EXPECT_EQ(iter_
->dts(), DecodeTimestampFromRational(50, kAudioScale
));
373 TEST_F(TrackRunIteratorTest
, FirstSampleFlagTest
) {
374 // Ensure that keyframes are flagged correctly in the face of BMFF boxes which
375 // explicitly specify the flags for the first sample in a run and rely on
376 // defaults for all subsequent samples
377 iter_
.reset(new TrackRunIterator(&moov_
, media_log_
));
378 MovieFragment moof
= CreateFragment();
379 moof
.tracks
[1].header
.has_default_sample_flags
= true;
380 moof
.tracks
[1].header
.default_sample_flags
= ToSampleFlags("UN");
381 SetFlagsOnSamples("US", &moof
.tracks
[1].runs
[0]);
383 ASSERT_TRUE(iter_
->Init(moof
));
384 EXPECT_EQ("1 KR KR KR KR KR KR KR KR KR KR", KeyframeAndRAPInfo(iter_
.get()));
387 EXPECT_EQ("2 KR P P P P P P P P P", KeyframeAndRAPInfo(iter_
.get()));
390 // Verify that parsing fails if a reserved value is in the sample flags.
391 TEST_F(TrackRunIteratorTest
, SampleInfoTest_ReservedInSampleFlags
) {
392 iter_
.reset(new TrackRunIterator(&moov_
, media_log_
));
393 MovieFragment moof
= CreateFragment();
394 // Change the "depends on" field on one of the samples to a
396 moof
.tracks
[1].runs
[0].sample_flags
[0] = ToSampleFlags("RS");
397 ASSERT_FALSE(iter_
->Init(moof
));
400 // Verify that parsing fails if a reserved value is in the default sample flags.
401 TEST_F(TrackRunIteratorTest
, SampleInfoTest_ReservedInDefaultSampleFlags
) {
402 iter_
.reset(new TrackRunIterator(&moov_
, media_log_
));
403 MovieFragment moof
= CreateFragment();
404 // Set the default flag to contain a reserved "depends on" value.
405 moof
.tracks
[0].header
.default_sample_flags
= ToSampleFlags("RN");
406 ASSERT_FALSE(iter_
->Init(moof
));
409 TEST_F(TrackRunIteratorTest
, ReorderingTest
) {
410 // Test frame reordering and edit list support. The frames have the following
411 // decode timestamps:
413 // 0ms 40ms 120ms 240ms
414 // | 0 | 1 - | 2 - - |
416 // ...and these composition timestamps, after edit list adjustment:
418 // 0ms 40ms 160ms 240ms
419 // | 0 | 2 - - | 1 - |
421 // Create an edit list with one entry, with an initial start time of 80ms
422 // (that is, 2 / kVideoTimescale) and a duration of zero (which is treated as
423 // infinite according to 14496-12:2012). This will cause the first 80ms of the
424 // media timeline - which will be empty, due to CTS biasing - to be discarded.
425 iter_
.reset(new TrackRunIterator(&moov_
, media_log_
));
427 entry
.segment_duration
= 0;
428 entry
.media_time
= 2;
429 entry
.media_rate_integer
= 1;
430 entry
.media_rate_fraction
= 0;
431 moov_
.tracks
[1].edit
.list
.edits
.push_back(entry
);
433 // Add CTS offsets. Without bias, the CTS offsets for the first three frames
434 // would simply be [0, 3, -2]. Since CTS offsets should be non-negative for
435 // maximum compatibility, these values are biased up to [2, 5, 0], and the
436 // extra 80ms is removed via the edit list.
437 MovieFragment moof
= CreateFragment();
438 std::vector
<int32
>& cts_offsets
=
439 moof
.tracks
[1].runs
[0].sample_composition_time_offsets
;
440 cts_offsets
.resize(10);
444 moof
.tracks
[1].decode_time
.decode_time
= 0;
446 ASSERT_TRUE(iter_
->Init(moof
));
448 EXPECT_EQ(iter_
->dts(), DecodeTimestampFromRational(0, kVideoScale
));
449 EXPECT_EQ(iter_
->cts(), TimeDeltaFromRational(0, kVideoScale
));
450 EXPECT_EQ(iter_
->duration(), TimeDeltaFromRational(1, kVideoScale
));
451 iter_
->AdvanceSample();
452 EXPECT_EQ(iter_
->dts(), DecodeTimestampFromRational(1, kVideoScale
));
453 EXPECT_EQ(iter_
->cts(), TimeDeltaFromRational(4, kVideoScale
));
454 EXPECT_EQ(iter_
->duration(), TimeDeltaFromRational(2, kVideoScale
));
455 iter_
->AdvanceSample();
456 EXPECT_EQ(iter_
->dts(), DecodeTimestampFromRational(3, kVideoScale
));
457 EXPECT_EQ(iter_
->cts(), TimeDeltaFromRational(1, kVideoScale
));
458 EXPECT_EQ(iter_
->duration(), TimeDeltaFromRational(3, kVideoScale
));
461 TEST_F(TrackRunIteratorTest
, IgnoreUnknownAuxInfoTest
) {
462 iter_
.reset(new TrackRunIterator(&moov_
, media_log_
));
463 MovieFragment moof
= CreateFragment();
464 moof
.tracks
[1].auxiliary_offset
.offsets
.push_back(50);
465 moof
.tracks
[1].auxiliary_size
.default_sample_info_size
= 2;
466 moof
.tracks
[1].auxiliary_size
.sample_count
= 2;
467 moof
.tracks
[1].runs
[0].sample_count
= 2;
468 ASSERT_TRUE(iter_
->Init(moof
));
470 EXPECT_FALSE(iter_
->AuxInfoNeedsToBeCached());
473 TEST_F(TrackRunIteratorTest
, DecryptConfigTest
) {
474 AddEncryption(&moov_
.tracks
[1]);
475 iter_
.reset(new TrackRunIterator(&moov_
, media_log_
));
477 MovieFragment moof
= CreateFragment();
478 AddAuxInfoHeaders(50, &moof
.tracks
[1]);
480 ASSERT_TRUE(iter_
->Init(moof
));
482 // The run for track 2 will be first, since its aux info offset is the first
483 // element in the file.
484 EXPECT_EQ(iter_
->track_id(), 2u);
485 EXPECT_TRUE(iter_
->is_encrypted());
486 EXPECT_TRUE(iter_
->AuxInfoNeedsToBeCached());
487 EXPECT_EQ(static_cast<uint32
>(iter_
->aux_info_size()), arraysize(kAuxInfo
));
488 EXPECT_EQ(iter_
->aux_info_offset(), 50);
489 EXPECT_EQ(iter_
->GetMaxClearOffset(), 50);
490 EXPECT_FALSE(iter_
->CacheAuxInfo(NULL
, 0));
491 EXPECT_FALSE(iter_
->CacheAuxInfo(kAuxInfo
, 3));
492 EXPECT_TRUE(iter_
->AuxInfoNeedsToBeCached());
493 EXPECT_TRUE(iter_
->CacheAuxInfo(kAuxInfo
, arraysize(kAuxInfo
)));
494 EXPECT_FALSE(iter_
->AuxInfoNeedsToBeCached());
495 EXPECT_EQ(iter_
->sample_offset(), 200);
496 EXPECT_EQ(iter_
->GetMaxClearOffset(), moof
.tracks
[0].runs
[0].data_offset
);
497 scoped_ptr
<DecryptConfig
> config
= iter_
->GetDecryptConfig();
498 ASSERT_EQ(arraysize(kKeyId
), config
->key_id().size());
499 EXPECT_TRUE(!memcmp(kKeyId
, config
->key_id().data(),
500 config
->key_id().size()));
501 ASSERT_EQ(arraysize(kIv1
), config
->iv().size());
502 EXPECT_TRUE(!memcmp(kIv1
, config
->iv().data(), config
->iv().size()));
503 EXPECT_TRUE(config
->subsamples().empty());
504 iter_
->AdvanceSample();
505 config
= iter_
->GetDecryptConfig();
506 EXPECT_EQ(config
->subsamples().size(), 2u);
507 EXPECT_EQ(config
->subsamples()[0].clear_bytes
, 1u);
508 EXPECT_EQ(config
->subsamples()[1].cypher_bytes
, 4u);
511 TEST_F(TrackRunIteratorTest
, CencSampleGroupTest
) {
512 MovieFragment moof
= CreateFragment();
514 const SampleToGroupEntry kSampleToGroupTable
[] = {
515 // Associated with the second entry in SampleGroupDescription Box.
516 {1, SampleToGroupEntry::kFragmentGroupDescriptionIndexBase
+ 2},
517 // Associated with the first entry in SampleGroupDescription Box.
518 {1, SampleToGroupEntry::kFragmentGroupDescriptionIndexBase
+ 1}};
519 AddCencSampleGroup(&moov_
.tracks
[0], &moof
.tracks
[0], kSampleToGroupTable
,
520 arraysize(kSampleToGroupTable
));
522 iter_
.reset(new TrackRunIterator(&moov_
, media_log_
));
523 ASSERT_TRUE(InitMoofWithArbitraryAuxInfo(&moof
));
525 std::string
cenc_sample_group_key_id(
526 kFragmentCencSampleGroupKeyId
,
527 kFragmentCencSampleGroupKeyId
+ arraysize(kFragmentCencSampleGroupKeyId
));
528 // The first sample is encrypted and the second sample is unencrypted.
529 EXPECT_TRUE(iter_
->is_encrypted());
530 EXPECT_EQ(cenc_sample_group_key_id
, iter_
->GetDecryptConfig()->key_id());
531 iter_
->AdvanceSample();
532 EXPECT_FALSE(iter_
->is_encrypted());
535 TEST_F(TrackRunIteratorTest
, CencSampleGroupWithTrackEncryptionBoxTest
) {
536 // Add TrackEncryption Box.
537 AddEncryption(&moov_
.tracks
[0]);
539 MovieFragment moof
= CreateFragment();
541 const SampleToGroupEntry kSampleToGroupTable
[] = {
542 // Associated with the 2nd entry in fragment SampleGroupDescription Box.
543 {2, SampleToGroupEntry::kFragmentGroupDescriptionIndexBase
+ 2},
544 // Associated with the default values specified in TrackEncryption Box.
546 // Associated with the 1st entry in fragment SampleGroupDescription Box.
547 {3, SampleToGroupEntry::kFragmentGroupDescriptionIndexBase
+ 1},
548 // Associated with the 1st entry in track SampleGroupDescription Box.
550 AddCencSampleGroup(&moov_
.tracks
[0], &moof
.tracks
[0], kSampleToGroupTable
,
551 arraysize(kSampleToGroupTable
));
553 iter_
.reset(new TrackRunIterator(&moov_
, media_log_
));
554 ASSERT_TRUE(InitMoofWithArbitraryAuxInfo(&moof
));
556 std::string
track_encryption_key_id(kKeyId
, kKeyId
+ arraysize(kKeyId
));
557 std::string
track_cenc_sample_group_key_id(
558 kTrackCencSampleGroupKeyId
,
559 kTrackCencSampleGroupKeyId
+ arraysize(kTrackCencSampleGroupKeyId
));
560 std::string
fragment_cenc_sample_group_key_id(
561 kFragmentCencSampleGroupKeyId
,
562 kFragmentCencSampleGroupKeyId
+ arraysize(kFragmentCencSampleGroupKeyId
));
564 for (size_t i
= 0; i
< kSampleToGroupTable
[0].sample_count
; ++i
) {
565 EXPECT_TRUE(iter_
->is_encrypted());
566 EXPECT_EQ(fragment_cenc_sample_group_key_id
,
567 iter_
->GetDecryptConfig()->key_id());
568 iter_
->AdvanceSample();
571 for (size_t i
= 0; i
< kSampleToGroupTable
[1].sample_count
; ++i
) {
572 EXPECT_TRUE(iter_
->is_encrypted());
573 EXPECT_EQ(track_encryption_key_id
, iter_
->GetDecryptConfig()->key_id());
574 iter_
->AdvanceSample();
577 for (size_t i
= 0; i
< kSampleToGroupTable
[2].sample_count
; ++i
) {
578 EXPECT_FALSE(iter_
->is_encrypted());
579 iter_
->AdvanceSample();
582 for (size_t i
= 0; i
< kSampleToGroupTable
[3].sample_count
; ++i
) {
583 EXPECT_TRUE(iter_
->is_encrypted());
584 EXPECT_EQ(track_cenc_sample_group_key_id
,
585 iter_
->GetDecryptConfig()->key_id());
586 iter_
->AdvanceSample();
589 // The remaining samples should be associated with the default values
590 // specified in TrackEncryption Box.
591 EXPECT_TRUE(iter_
->is_encrypted());
592 EXPECT_EQ(track_encryption_key_id
, iter_
->GetDecryptConfig()->key_id());
595 // It is legal for aux info blocks to be shared among multiple formats.
596 TEST_F(TrackRunIteratorTest
, SharedAuxInfoTest
) {
597 AddEncryption(&moov_
.tracks
[0]);
598 AddEncryption(&moov_
.tracks
[1]);
599 iter_
.reset(new TrackRunIterator(&moov_
, media_log_
));
601 MovieFragment moof
= CreateFragment();
602 moof
.tracks
[0].runs
.resize(1);
603 AddAuxInfoHeaders(50, &moof
.tracks
[0]);
604 AddAuxInfoHeaders(50, &moof
.tracks
[1]);
605 moof
.tracks
[0].auxiliary_size
.default_sample_info_size
= 8;
607 ASSERT_TRUE(iter_
->Init(moof
));
608 EXPECT_EQ(iter_
->track_id(), 1u);
609 EXPECT_EQ(iter_
->aux_info_offset(), 50);
610 EXPECT_TRUE(iter_
->CacheAuxInfo(kAuxInfo
, arraysize(kAuxInfo
)));
611 scoped_ptr
<DecryptConfig
> config
= iter_
->GetDecryptConfig();
612 ASSERT_EQ(arraysize(kIv1
), config
->iv().size());
613 EXPECT_TRUE(!memcmp(kIv1
, config
->iv().data(), config
->iv().size()));
614 iter_
->AdvanceSample();
615 EXPECT_EQ(iter_
->GetMaxClearOffset(), 50);
617 EXPECT_EQ(iter_
->GetMaxClearOffset(), 50);
618 EXPECT_EQ(iter_
->aux_info_offset(), 50);
619 EXPECT_TRUE(iter_
->CacheAuxInfo(kAuxInfo
, arraysize(kAuxInfo
)));
620 EXPECT_EQ(iter_
->GetMaxClearOffset(), 200);
621 ASSERT_EQ(arraysize(kIv1
), config
->iv().size());
622 EXPECT_TRUE(!memcmp(kIv1
, config
->iv().data(), config
->iv().size()));
623 iter_
->AdvanceSample();
624 EXPECT_EQ(iter_
->GetMaxClearOffset(), 201);
627 // Sensible files are expected to place auxiliary information for a run
628 // immediately before the main data for that run. Alternative schemes are
629 // possible, however, including the somewhat reasonable behavior of placing all
630 // aux info at the head of the 'mdat' box together, and the completely
631 // unreasonable behavior demonstrated here:
632 // byte 50: track 2, run 1 aux info
633 // byte 100: track 1, run 1 data
634 // byte 200: track 2, run 1 data
635 // byte 201: track 1, run 2 aux info (*inside* track 2, run 1 data)
636 // byte 10000: track 1, run 2 data
637 // byte 20000: track 1, run 1 aux info
638 TEST_F(TrackRunIteratorTest
, UnexpectedOrderingTest
) {
639 AddEncryption(&moov_
.tracks
[0]);
640 AddEncryption(&moov_
.tracks
[1]);
641 iter_
.reset(new TrackRunIterator(&moov_
, media_log_
));
643 MovieFragment moof
= CreateFragment();
644 AddAuxInfoHeaders(20000, &moof
.tracks
[0]);
645 moof
.tracks
[0].auxiliary_offset
.offsets
.push_back(201);
646 moof
.tracks
[0].auxiliary_size
.sample_count
+= 2;
647 moof
.tracks
[0].auxiliary_size
.default_sample_info_size
= 8;
648 moof
.tracks
[0].runs
[1].sample_count
= 2;
649 AddAuxInfoHeaders(50, &moof
.tracks
[1]);
650 moof
.tracks
[1].runs
[0].sample_sizes
[0] = 5;
652 ASSERT_TRUE(iter_
->Init(moof
));
653 EXPECT_EQ(iter_
->track_id(), 2u);
654 EXPECT_EQ(iter_
->aux_info_offset(), 50);
655 EXPECT_EQ(iter_
->sample_offset(), 200);
656 EXPECT_TRUE(iter_
->CacheAuxInfo(kAuxInfo
, arraysize(kAuxInfo
)));
657 EXPECT_EQ(iter_
->GetMaxClearOffset(), 100);
659 EXPECT_EQ(iter_
->track_id(), 1u);
660 EXPECT_EQ(iter_
->aux_info_offset(), 20000);
661 EXPECT_EQ(iter_
->sample_offset(), 100);
662 EXPECT_TRUE(iter_
->CacheAuxInfo(kAuxInfo
, arraysize(kAuxInfo
)));
663 EXPECT_EQ(iter_
->GetMaxClearOffset(), 100);
664 iter_
->AdvanceSample();
665 EXPECT_EQ(iter_
->GetMaxClearOffset(), 101);
667 EXPECT_EQ(iter_
->track_id(), 1u);
668 EXPECT_EQ(iter_
->aux_info_offset(), 201);
669 EXPECT_EQ(iter_
->sample_offset(), 10000);
670 EXPECT_EQ(iter_
->GetMaxClearOffset(), 201);
671 EXPECT_TRUE(iter_
->CacheAuxInfo(kAuxInfo
, arraysize(kAuxInfo
)));
672 EXPECT_EQ(iter_
->GetMaxClearOffset(), 10000);
675 TEST_F(TrackRunIteratorTest
, MissingAndEmptyStss
) {
676 MovieFragment moof
= CreateFragment();
678 // Setup track 0 to not have an stss box, which means that all samples should
679 // be marked as random access points unless the kSampleIsNonSyncSample flag is
680 // set in the sample flags.
681 moov_
.tracks
[0].media
.information
.sample_table
.sync_sample
.is_present
= false;
682 moov_
.tracks
[0].media
.information
.sample_table
.sync_sample
.entries
.resize(0);
683 moof
.tracks
[0].runs
.resize(1);
684 moof
.tracks
[0].runs
[0].sample_count
= 6;
685 moof
.tracks
[0].runs
[0].data_offset
= 100;
686 SetFlagsOnSamples("US UN OS ON NS NN", &moof
.tracks
[0].runs
[0]);
688 // Setup track 1 to have an stss box with no entries, which normally means
689 // that none of the samples should be random access points. If the
690 // kSampleIsNonSyncSample flag is NOT set though, the sample should be
691 // considered a random access point.
692 moov_
.tracks
[1].media
.information
.sample_table
.sync_sample
.is_present
= true;
693 moov_
.tracks
[1].media
.information
.sample_table
.sync_sample
.entries
.resize(0);
694 moof
.tracks
[1].runs
.resize(1);
695 moof
.tracks
[1].runs
[0].sample_count
= 6;
696 moof
.tracks
[1].runs
[0].data_offset
= 200;
697 SetFlagsOnSamples("US UN OS ON NS NN", &moof
.tracks
[1].runs
[0]);
699 iter_
.reset(new TrackRunIterator(&moov_
, media_log_
));
701 ASSERT_TRUE(iter_
->Init(moof
));
702 EXPECT_TRUE(iter_
->IsRunValid());
704 // Verify that all samples except for the ones that have the
705 // kSampleIsNonSyncSample flag set are marked as random access points.
706 EXPECT_EQ("1 KR P PR P KR K", KeyframeAndRAPInfo(iter_
.get()));
710 // Verify that nothing is marked as a random access point.
711 EXPECT_EQ("2 KR P PR P KR K", KeyframeAndRAPInfo(iter_
.get()));