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 kCencSampleGroupKeyId
[] = {
40 0x46, 0x72, 0x61, 0x67, 0x53, 0x61, 0x6d, 0x70,
41 0x6c, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4b
47 class TrackRunIteratorTest
: public testing::Test
{
49 TrackRunIteratorTest() {
56 scoped_ptr
<TrackRunIterator
> iter_
;
59 moov_
.header
.timescale
= 1000;
60 moov_
.tracks
.resize(3);
61 moov_
.extends
.tracks
.resize(2);
62 moov_
.tracks
[0].header
.track_id
= 1;
63 moov_
.tracks
[0].media
.header
.timescale
= kAudioScale
;
64 SampleDescription
& desc1
=
65 moov_
.tracks
[0].media
.information
.sample_table
.description
;
66 AudioSampleEntry aud_desc
;
67 aud_desc
.format
= FOURCC_MP4A
;
68 aud_desc
.sinf
.info
.track_encryption
.is_encrypted
= false;
70 desc1
.audio_entries
.push_back(aud_desc
);
71 moov_
.extends
.tracks
[0].track_id
= 1;
72 moov_
.extends
.tracks
[0].default_sample_description_index
= 1;
73 moov_
.tracks
[0].media
.information
.sample_table
.sync_sample
.is_present
=
75 moov_
.tracks
[1].header
.track_id
= 2;
76 moov_
.tracks
[1].media
.header
.timescale
= kVideoScale
;
77 SampleDescription
& desc2
=
78 moov_
.tracks
[1].media
.information
.sample_table
.description
;
79 VideoSampleEntry vid_desc
;
80 vid_desc
.format
= FOURCC_AVC1
;
81 vid_desc
.sinf
.info
.track_encryption
.is_encrypted
= false;
83 desc2
.video_entries
.push_back(vid_desc
);
84 moov_
.extends
.tracks
[1].track_id
= 2;
85 moov_
.extends
.tracks
[1].default_sample_description_index
= 1;
86 SyncSample
& video_sync_sample
=
87 moov_
.tracks
[1].media
.information
.sample_table
.sync_sample
;
88 video_sync_sample
.is_present
= true;
89 video_sync_sample
.entries
.resize(1);
90 video_sync_sample
.entries
[0] = 0;
92 moov_
.tracks
[2].header
.track_id
= 3;
93 moov_
.tracks
[2].media
.information
.sample_table
.description
.type
= kHint
;
96 uint32
ToSampleFlags(const std::string
& str
) {
97 CHECK_EQ(str
.length(), 2u);
99 SampleDependsOn sample_depends_on
= kSampleDependsOnReserved
;
100 bool is_non_sync_sample
= false;
103 sample_depends_on
= kSampleDependsOnUnknown
;
106 sample_depends_on
= kSampleDependsOnOthers
;
109 sample_depends_on
= kSampleDependsOnNoOther
;
112 sample_depends_on
= kSampleDependsOnReserved
;
115 CHECK(false) << "Invalid sample dependency character '"
122 is_non_sync_sample
= false;
125 is_non_sync_sample
= true;
128 CHECK(false) << "Invalid sync sample character '"
132 uint32 flags
= static_cast<uint32
>(sample_depends_on
) << 24;
133 if (is_non_sync_sample
)
134 flags
|= kSampleIsNonSyncSample
;
138 void SetFlagsOnSamples(const std::string
& sample_info
,
139 TrackFragmentRun
* trun
) {
140 // US - SampleDependsOnUnknown & IsSyncSample
141 // UN - SampleDependsOnUnknown & IsNonSyncSample
142 // OS - SampleDependsOnOthers & IsSyncSample
143 // ON - SampleDependsOnOthers & IsNonSyncSample
144 // NS - SampleDependsOnNoOthers & IsSyncSample
145 // NN - SampleDependsOnNoOthers & IsNonSyncSample
146 std::vector
<std::string
> flags_data
;
147 base::SplitString(sample_info
, ' ', &flags_data
);
149 if (flags_data
.size() == 1u) {
150 // Simulates the first_sample_flags_present set scenario,
151 // where only one sample_flag value is set and the default
152 // flags are used for everything else.
153 ASSERT_GE(trun
->sample_count
, flags_data
.size());
155 ASSERT_EQ(trun
->sample_count
, flags_data
.size());
158 trun
->sample_flags
.resize(flags_data
.size());
159 for (size_t i
= 0; i
< flags_data
.size(); i
++)
160 trun
->sample_flags
[i
] = ToSampleFlags(flags_data
[i
]);
163 std::string
KeyframeAndRAPInfo(TrackRunIterator
* iter
) {
164 CHECK(iter
->IsRunValid());
165 std::stringstream ss
;
166 ss
<< iter
->track_id();
168 while (iter
->IsSampleValid()) {
169 ss
<< " " << (iter
->is_keyframe() ? "K" : "P");
170 if (iter
->is_random_access_point())
172 iter
->AdvanceSample();
178 MovieFragment
CreateFragment() {
180 moof
.tracks
.resize(2);
181 moof
.tracks
[0].decode_time
.decode_time
= 0;
182 moof
.tracks
[0].header
.track_id
= 1;
183 moof
.tracks
[0].header
.has_default_sample_flags
= true;
184 moof
.tracks
[0].header
.default_sample_flags
= ToSampleFlags("US");
185 moof
.tracks
[0].header
.default_sample_duration
= 1024;
186 moof
.tracks
[0].header
.default_sample_size
= 4;
187 moof
.tracks
[0].runs
.resize(2);
188 moof
.tracks
[0].runs
[0].sample_count
= 10;
189 moof
.tracks
[0].runs
[0].data_offset
= 100;
190 SetAscending(&moof
.tracks
[0].runs
[0].sample_sizes
);
192 moof
.tracks
[0].runs
[1].sample_count
= 10;
193 moof
.tracks
[0].runs
[1].data_offset
= 10000;
195 moof
.tracks
[1].header
.track_id
= 2;
196 moof
.tracks
[1].header
.has_default_sample_flags
= false;
197 moof
.tracks
[1].decode_time
.decode_time
= 10;
198 moof
.tracks
[1].runs
.resize(1);
199 moof
.tracks
[1].runs
[0].sample_count
= 10;
200 moof
.tracks
[1].runs
[0].data_offset
= 200;
201 SetAscending(&moof
.tracks
[1].runs
[0].sample_sizes
);
202 SetAscending(&moof
.tracks
[1].runs
[0].sample_durations
);
203 SetFlagsOnSamples("US UN UN UN UN UN UN UN UN UN", &moof
.tracks
[1].runs
[0]);
208 // Update the first sample description of a Track to indicate encryption
209 void AddEncryption(Track
* track
) {
210 SampleDescription
* stsd
=
211 &track
->media
.information
.sample_table
.description
;
212 ProtectionSchemeInfo
* sinf
;
213 if (!stsd
->video_entries
.empty()) {
214 sinf
= &stsd
->video_entries
[0].sinf
;
216 sinf
= &stsd
->audio_entries
[0].sinf
;
219 sinf
->type
.type
= FOURCC_CENC
;
220 sinf
->info
.track_encryption
.is_encrypted
= true;
221 sinf
->info
.track_encryption
.default_iv_size
= 8;
222 sinf
->info
.track_encryption
.default_kid
.assign(kKeyId
,
223 kKeyId
+ arraysize(kKeyId
));
226 // Add SampleGroupDescription Box with two entries (an unencrypted entry and
227 // an encrypted entry). Populate SampleToGroup Box from input array.
228 void AddCencSampleGroup(TrackFragment
* frag
,
229 const SampleToGroupEntry
* sample_to_group_entries
,
230 size_t num_entries
) {
231 frag
->sample_group_description
.grouping_type
= FOURCC_SEIG
;
232 frag
->sample_group_description
.entries
.resize(2);
233 frag
->sample_group_description
.entries
[0].is_encrypted
= false;
234 frag
->sample_group_description
.entries
[0].iv_size
= 0;
235 frag
->sample_group_description
.entries
[1].is_encrypted
= true;
236 frag
->sample_group_description
.entries
[1].iv_size
= 8;
237 frag
->sample_group_description
.entries
[1].key_id
.assign(
238 kCencSampleGroupKeyId
,
239 kCencSampleGroupKeyId
+ arraysize(kCencSampleGroupKeyId
));
241 frag
->sample_to_group
.grouping_type
= FOURCC_SEIG
;
242 frag
->sample_to_group
.entries
.assign(sample_to_group_entries
,
243 sample_to_group_entries
+ num_entries
);
246 // Add aux info covering the first track run to a TrackFragment, and update
247 // the run to ensure it matches length and subsample information.
248 void AddAuxInfoHeaders(int offset
, TrackFragment
* frag
) {
249 frag
->auxiliary_offset
.offsets
.push_back(offset
);
250 frag
->auxiliary_size
.sample_count
= 2;
251 frag
->auxiliary_size
.sample_info_sizes
.push_back(8);
252 frag
->auxiliary_size
.sample_info_sizes
.push_back(22);
253 frag
->runs
[0].sample_count
= 2;
254 frag
->runs
[0].sample_sizes
[1] = 10;
257 bool InitMoofWithArbitraryAuxInfo(MovieFragment
* moof
) {
258 // Add aux info header (equal sized aux info for every sample).
259 for (uint32 i
= 0; i
< moof
->tracks
.size(); ++i
) {
260 moof
->tracks
[i
].auxiliary_offset
.offsets
.push_back(50);
261 moof
->tracks
[i
].auxiliary_size
.sample_count
= 10;
262 moof
->tracks
[i
].auxiliary_size
.default_sample_info_size
= 8;
265 // We don't care about the actual data in aux.
266 std::vector
<uint8
> aux_info(1000);
267 return iter_
->Init(*moof
) &&
268 iter_
->CacheAuxInfo(&aux_info
[0], aux_info
.size());
271 void SetAscending(std::vector
<uint32
>* vec
) {
273 for (size_t i
= 0; i
< vec
->size(); i
++)
278 TEST_F(TrackRunIteratorTest
, NoRunsTest
) {
279 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
280 ASSERT_TRUE(iter_
->Init(MovieFragment()));
281 EXPECT_FALSE(iter_
->IsRunValid());
282 EXPECT_FALSE(iter_
->IsSampleValid());
285 TEST_F(TrackRunIteratorTest
, BasicOperationTest
) {
286 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
287 MovieFragment moof
= CreateFragment();
289 // Test that runs are sorted correctly, and that properties of the initial
290 // sample of the first run are correct
291 ASSERT_TRUE(iter_
->Init(moof
));
292 EXPECT_TRUE(iter_
->IsRunValid());
293 EXPECT_FALSE(iter_
->is_encrypted());
294 EXPECT_EQ(iter_
->track_id(), 1u);
295 EXPECT_EQ(iter_
->sample_offset(), 100);
296 EXPECT_EQ(iter_
->sample_size(), 1);
297 EXPECT_EQ(iter_
->dts(), DecodeTimestampFromRational(0, kAudioScale
));
298 EXPECT_EQ(iter_
->cts(), TimeDeltaFromRational(0, kAudioScale
));
299 EXPECT_EQ(iter_
->duration(), TimeDeltaFromRational(1024, kAudioScale
));
300 EXPECT_TRUE(iter_
->is_keyframe());
302 // Advance to the last sample in the current run, and test its properties
303 for (int i
= 0; i
< 9; i
++) iter_
->AdvanceSample();
304 EXPECT_EQ(iter_
->track_id(), 1u);
305 EXPECT_EQ(iter_
->sample_offset(), 100 + kSumAscending1
);
306 EXPECT_EQ(iter_
->sample_size(), 10);
307 EXPECT_EQ(iter_
->dts(), DecodeTimestampFromRational(1024 * 9, kAudioScale
));
308 EXPECT_EQ(iter_
->duration(), TimeDeltaFromRational(1024, kAudioScale
));
309 EXPECT_TRUE(iter_
->is_keyframe());
312 iter_
->AdvanceSample();
313 EXPECT_FALSE(iter_
->IsSampleValid());
315 // Test last sample of next run
317 EXPECT_TRUE(iter_
->is_keyframe());
318 for (int i
= 0; i
< 9; i
++) iter_
->AdvanceSample();
319 EXPECT_EQ(iter_
->track_id(), 2u);
320 EXPECT_EQ(iter_
->sample_offset(), 200 + kSumAscending1
);
321 EXPECT_EQ(iter_
->sample_size(), 10);
322 int64 base_dts
= kSumAscending1
+ moof
.tracks
[1].decode_time
.decode_time
;
323 EXPECT_EQ(iter_
->dts(), DecodeTimestampFromRational(base_dts
, kVideoScale
));
324 EXPECT_EQ(iter_
->duration(), TimeDeltaFromRational(10, kVideoScale
));
325 EXPECT_FALSE(iter_
->is_keyframe());
329 EXPECT_EQ(iter_
->track_id(), 1u);
330 EXPECT_EQ(iter_
->dts(), DecodeTimestampFromRational(1024 * 10, kAudioScale
));
331 iter_
->AdvanceSample();
332 EXPECT_EQ(moof
.tracks
[0].runs
[1].data_offset
+
333 moof
.tracks
[0].header
.default_sample_size
,
334 iter_
->sample_offset());
336 EXPECT_FALSE(iter_
->IsRunValid());
339 TEST_F(TrackRunIteratorTest
, TrackExtendsDefaultsTest
) {
340 moov_
.extends
.tracks
[0].default_sample_duration
= 50;
341 moov_
.extends
.tracks
[0].default_sample_size
= 3;
342 moov_
.extends
.tracks
[0].default_sample_flags
= ToSampleFlags("UN");
343 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
344 MovieFragment moof
= CreateFragment();
345 moof
.tracks
[0].header
.has_default_sample_flags
= false;
346 moof
.tracks
[0].header
.default_sample_size
= 0;
347 moof
.tracks
[0].header
.default_sample_duration
= 0;
348 moof
.tracks
[0].runs
[0].sample_sizes
.clear();
349 ASSERT_TRUE(iter_
->Init(moof
));
350 iter_
->AdvanceSample();
351 EXPECT_FALSE(iter_
->is_keyframe());
352 EXPECT_EQ(iter_
->sample_size(), 3);
353 EXPECT_EQ(iter_
->sample_offset(), moof
.tracks
[0].runs
[0].data_offset
+ 3);
354 EXPECT_EQ(iter_
->duration(), TimeDeltaFromRational(50, kAudioScale
));
355 EXPECT_EQ(iter_
->dts(), DecodeTimestampFromRational(50, kAudioScale
));
358 TEST_F(TrackRunIteratorTest
, FirstSampleFlagTest
) {
359 // Ensure that keyframes are flagged correctly in the face of BMFF boxes which
360 // explicitly specify the flags for the first sample in a run and rely on
361 // defaults for all subsequent samples
362 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
363 MovieFragment moof
= CreateFragment();
364 moof
.tracks
[1].header
.has_default_sample_flags
= true;
365 moof
.tracks
[1].header
.default_sample_flags
= ToSampleFlags("UN");
366 SetFlagsOnSamples("US", &moof
.tracks
[1].runs
[0]);
368 ASSERT_TRUE(iter_
->Init(moof
));
369 EXPECT_EQ("1 KR KR KR KR KR KR KR KR KR KR", KeyframeAndRAPInfo(iter_
.get()));
372 EXPECT_EQ("2 KR P P P P P P P P P", KeyframeAndRAPInfo(iter_
.get()));
375 // Verify that parsing fails if a reserved value is in the sample flags.
376 TEST_F(TrackRunIteratorTest
, SampleInfoTest_ReservedInSampleFlags
) {
377 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
378 MovieFragment moof
= CreateFragment();
379 // Change the "depends on" field on one of the samples to a
381 moof
.tracks
[1].runs
[0].sample_flags
[0] = ToSampleFlags("RS");
382 ASSERT_FALSE(iter_
->Init(moof
));
385 // Verify that parsing fails if a reserved value is in the default sample flags.
386 TEST_F(TrackRunIteratorTest
, SampleInfoTest_ReservedInDefaultSampleFlags
) {
387 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
388 MovieFragment moof
= CreateFragment();
389 // Set the default flag to contain a reserved "depends on" value.
390 moof
.tracks
[0].header
.default_sample_flags
= ToSampleFlags("RN");
391 ASSERT_FALSE(iter_
->Init(moof
));
394 TEST_F(TrackRunIteratorTest
, ReorderingTest
) {
395 // Test frame reordering and edit list support. The frames have the following
396 // decode timestamps:
398 // 0ms 40ms 120ms 240ms
399 // | 0 | 1 - | 2 - - |
401 // ...and these composition timestamps, after edit list adjustment:
403 // 0ms 40ms 160ms 240ms
404 // | 0 | 2 - - | 1 - |
406 // Create an edit list with one entry, with an initial start time of 80ms
407 // (that is, 2 / kVideoTimescale) and a duration of zero (which is treated as
408 // infinite according to 14496-12:2012). This will cause the first 80ms of the
409 // media timeline - which will be empty, due to CTS biasing - to be discarded.
410 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
412 entry
.segment_duration
= 0;
413 entry
.media_time
= 2;
414 entry
.media_rate_integer
= 1;
415 entry
.media_rate_fraction
= 0;
416 moov_
.tracks
[1].edit
.list
.edits
.push_back(entry
);
418 // Add CTS offsets. Without bias, the CTS offsets for the first three frames
419 // would simply be [0, 3, -2]. Since CTS offsets should be non-negative for
420 // maximum compatibility, these values are biased up to [2, 5, 0], and the
421 // extra 80ms is removed via the edit list.
422 MovieFragment moof
= CreateFragment();
423 std::vector
<int32
>& cts_offsets
=
424 moof
.tracks
[1].runs
[0].sample_composition_time_offsets
;
425 cts_offsets
.resize(10);
429 moof
.tracks
[1].decode_time
.decode_time
= 0;
431 ASSERT_TRUE(iter_
->Init(moof
));
433 EXPECT_EQ(iter_
->dts(), DecodeTimestampFromRational(0, kVideoScale
));
434 EXPECT_EQ(iter_
->cts(), TimeDeltaFromRational(0, kVideoScale
));
435 EXPECT_EQ(iter_
->duration(), TimeDeltaFromRational(1, kVideoScale
));
436 iter_
->AdvanceSample();
437 EXPECT_EQ(iter_
->dts(), DecodeTimestampFromRational(1, kVideoScale
));
438 EXPECT_EQ(iter_
->cts(), TimeDeltaFromRational(4, kVideoScale
));
439 EXPECT_EQ(iter_
->duration(), TimeDeltaFromRational(2, kVideoScale
));
440 iter_
->AdvanceSample();
441 EXPECT_EQ(iter_
->dts(), DecodeTimestampFromRational(3, kVideoScale
));
442 EXPECT_EQ(iter_
->cts(), TimeDeltaFromRational(1, kVideoScale
));
443 EXPECT_EQ(iter_
->duration(), TimeDeltaFromRational(3, kVideoScale
));
446 TEST_F(TrackRunIteratorTest
, IgnoreUnknownAuxInfoTest
) {
447 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
448 MovieFragment moof
= CreateFragment();
449 moof
.tracks
[1].auxiliary_offset
.offsets
.push_back(50);
450 moof
.tracks
[1].auxiliary_size
.default_sample_info_size
= 2;
451 moof
.tracks
[1].auxiliary_size
.sample_count
= 2;
452 moof
.tracks
[1].runs
[0].sample_count
= 2;
453 ASSERT_TRUE(iter_
->Init(moof
));
455 EXPECT_FALSE(iter_
->AuxInfoNeedsToBeCached());
458 TEST_F(TrackRunIteratorTest
, DecryptConfigTest
) {
459 AddEncryption(&moov_
.tracks
[1]);
460 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
462 MovieFragment moof
= CreateFragment();
463 AddAuxInfoHeaders(50, &moof
.tracks
[1]);
465 ASSERT_TRUE(iter_
->Init(moof
));
467 // The run for track 2 will be first, since its aux info offset is the first
468 // element in the file.
469 EXPECT_EQ(iter_
->track_id(), 2u);
470 EXPECT_TRUE(iter_
->is_encrypted());
471 EXPECT_TRUE(iter_
->AuxInfoNeedsToBeCached());
472 EXPECT_EQ(static_cast<uint32
>(iter_
->aux_info_size()), arraysize(kAuxInfo
));
473 EXPECT_EQ(iter_
->aux_info_offset(), 50);
474 EXPECT_EQ(iter_
->GetMaxClearOffset(), 50);
475 EXPECT_FALSE(iter_
->CacheAuxInfo(NULL
, 0));
476 EXPECT_FALSE(iter_
->CacheAuxInfo(kAuxInfo
, 3));
477 EXPECT_TRUE(iter_
->AuxInfoNeedsToBeCached());
478 EXPECT_TRUE(iter_
->CacheAuxInfo(kAuxInfo
, arraysize(kAuxInfo
)));
479 EXPECT_FALSE(iter_
->AuxInfoNeedsToBeCached());
480 EXPECT_EQ(iter_
->sample_offset(), 200);
481 EXPECT_EQ(iter_
->GetMaxClearOffset(), moof
.tracks
[0].runs
[0].data_offset
);
482 scoped_ptr
<DecryptConfig
> config
= iter_
->GetDecryptConfig();
483 ASSERT_EQ(arraysize(kKeyId
), config
->key_id().size());
484 EXPECT_TRUE(!memcmp(kKeyId
, config
->key_id().data(),
485 config
->key_id().size()));
486 ASSERT_EQ(arraysize(kIv1
), config
->iv().size());
487 EXPECT_TRUE(!memcmp(kIv1
, config
->iv().data(), config
->iv().size()));
488 EXPECT_TRUE(config
->subsamples().empty());
489 iter_
->AdvanceSample();
490 config
= iter_
->GetDecryptConfig();
491 EXPECT_EQ(config
->subsamples().size(), 2u);
492 EXPECT_EQ(config
->subsamples()[0].clear_bytes
, 1u);
493 EXPECT_EQ(config
->subsamples()[1].cypher_bytes
, 4u);
496 TEST_F(TrackRunIteratorTest
, CencSampleGroupTest
) {
497 MovieFragment moof
= CreateFragment();
499 const SampleToGroupEntry kSampleToGroupTable
[] = {
500 // Associated with the second entry in SampleGroupDescription Box.
501 {1, SampleToGroupEntry::kFragmentGroupDescriptionIndexBase
+ 2},
502 // Associated with the first entry in SampleGroupDescription Box.
503 {1, SampleToGroupEntry::kFragmentGroupDescriptionIndexBase
+ 1}};
505 &moof
.tracks
[0], kSampleToGroupTable
, arraysize(kSampleToGroupTable
));
507 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
508 ASSERT_TRUE(InitMoofWithArbitraryAuxInfo(&moof
));
510 std::string
cenc_sample_group_key_id(
511 kCencSampleGroupKeyId
,
512 kCencSampleGroupKeyId
+ arraysize(kCencSampleGroupKeyId
));
513 // The first sample is encrypted and the second sample is unencrypted.
514 EXPECT_TRUE(iter_
->is_encrypted());
515 EXPECT_EQ(cenc_sample_group_key_id
, iter_
->GetDecryptConfig()->key_id());
516 iter_
->AdvanceSample();
517 EXPECT_FALSE(iter_
->is_encrypted());
520 TEST_F(TrackRunIteratorTest
, CencSampleGroupWithTrackEncryptionBoxTest
) {
521 // Add TrackEncryption Box.
522 AddEncryption(&moov_
.tracks
[0]);
524 MovieFragment moof
= CreateFragment();
526 const SampleToGroupEntry kSampleToGroupTable
[] = {
527 // Associated with the second entry in SampleGroupDescription Box.
528 {2, SampleToGroupEntry::kFragmentGroupDescriptionIndexBase
+ 2},
529 // Associated with the default values specified in TrackEncryption Box.
531 // Associated with the first entry in SampleGroupDescription Box.
532 {3, SampleToGroupEntry::kFragmentGroupDescriptionIndexBase
+ 1}};
534 &moof
.tracks
[0], kSampleToGroupTable
, arraysize(kSampleToGroupTable
));
536 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
537 ASSERT_TRUE(InitMoofWithArbitraryAuxInfo(&moof
));
539 std::string
track_encryption_key_id(kKeyId
, kKeyId
+ arraysize(kKeyId
));
540 std::string
cenc_sample_group_key_id(
541 kCencSampleGroupKeyId
,
542 kCencSampleGroupKeyId
+ arraysize(kCencSampleGroupKeyId
));
544 for (size_t i
= 0; i
< kSampleToGroupTable
[0].sample_count
; ++i
) {
545 EXPECT_TRUE(iter_
->is_encrypted());
546 EXPECT_EQ(cenc_sample_group_key_id
, iter_
->GetDecryptConfig()->key_id());
547 iter_
->AdvanceSample();
550 for (size_t i
= 0; i
< kSampleToGroupTable
[1].sample_count
; ++i
) {
551 EXPECT_TRUE(iter_
->is_encrypted());
552 EXPECT_EQ(track_encryption_key_id
, iter_
->GetDecryptConfig()->key_id());
553 iter_
->AdvanceSample();
556 for (size_t i
= 0; i
< kSampleToGroupTable
[2].sample_count
; ++i
) {
557 EXPECT_FALSE(iter_
->is_encrypted());
558 iter_
->AdvanceSample();
561 // The remaining samples should be associated with the default values
562 // specified in TrackEncryption Box.
563 EXPECT_TRUE(iter_
->is_encrypted());
564 EXPECT_EQ(track_encryption_key_id
, iter_
->GetDecryptConfig()->key_id());
567 // It is legal for aux info blocks to be shared among multiple formats.
568 TEST_F(TrackRunIteratorTest
, SharedAuxInfoTest
) {
569 AddEncryption(&moov_
.tracks
[0]);
570 AddEncryption(&moov_
.tracks
[1]);
571 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
573 MovieFragment moof
= CreateFragment();
574 moof
.tracks
[0].runs
.resize(1);
575 AddAuxInfoHeaders(50, &moof
.tracks
[0]);
576 AddAuxInfoHeaders(50, &moof
.tracks
[1]);
577 moof
.tracks
[0].auxiliary_size
.default_sample_info_size
= 8;
579 ASSERT_TRUE(iter_
->Init(moof
));
580 EXPECT_EQ(iter_
->track_id(), 1u);
581 EXPECT_EQ(iter_
->aux_info_offset(), 50);
582 EXPECT_TRUE(iter_
->CacheAuxInfo(kAuxInfo
, arraysize(kAuxInfo
)));
583 scoped_ptr
<DecryptConfig
> config
= iter_
->GetDecryptConfig();
584 ASSERT_EQ(arraysize(kIv1
), config
->iv().size());
585 EXPECT_TRUE(!memcmp(kIv1
, config
->iv().data(), config
->iv().size()));
586 iter_
->AdvanceSample();
587 EXPECT_EQ(iter_
->GetMaxClearOffset(), 50);
589 EXPECT_EQ(iter_
->GetMaxClearOffset(), 50);
590 EXPECT_EQ(iter_
->aux_info_offset(), 50);
591 EXPECT_TRUE(iter_
->CacheAuxInfo(kAuxInfo
, arraysize(kAuxInfo
)));
592 EXPECT_EQ(iter_
->GetMaxClearOffset(), 200);
593 ASSERT_EQ(arraysize(kIv1
), config
->iv().size());
594 EXPECT_TRUE(!memcmp(kIv1
, config
->iv().data(), config
->iv().size()));
595 iter_
->AdvanceSample();
596 EXPECT_EQ(iter_
->GetMaxClearOffset(), 201);
599 // Sensible files are expected to place auxiliary information for a run
600 // immediately before the main data for that run. Alternative schemes are
601 // possible, however, including the somewhat reasonable behavior of placing all
602 // aux info at the head of the 'mdat' box together, and the completely
603 // unreasonable behavior demonstrated here:
604 // byte 50: track 2, run 1 aux info
605 // byte 100: track 1, run 1 data
606 // byte 200: track 2, run 1 data
607 // byte 201: track 1, run 2 aux info (*inside* track 2, run 1 data)
608 // byte 10000: track 1, run 2 data
609 // byte 20000: track 1, run 1 aux info
610 TEST_F(TrackRunIteratorTest
, UnexpectedOrderingTest
) {
611 AddEncryption(&moov_
.tracks
[0]);
612 AddEncryption(&moov_
.tracks
[1]);
613 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
615 MovieFragment moof
= CreateFragment();
616 AddAuxInfoHeaders(20000, &moof
.tracks
[0]);
617 moof
.tracks
[0].auxiliary_offset
.offsets
.push_back(201);
618 moof
.tracks
[0].auxiliary_size
.sample_count
+= 2;
619 moof
.tracks
[0].auxiliary_size
.default_sample_info_size
= 8;
620 moof
.tracks
[0].runs
[1].sample_count
= 2;
621 AddAuxInfoHeaders(50, &moof
.tracks
[1]);
622 moof
.tracks
[1].runs
[0].sample_sizes
[0] = 5;
624 ASSERT_TRUE(iter_
->Init(moof
));
625 EXPECT_EQ(iter_
->track_id(), 2u);
626 EXPECT_EQ(iter_
->aux_info_offset(), 50);
627 EXPECT_EQ(iter_
->sample_offset(), 200);
628 EXPECT_TRUE(iter_
->CacheAuxInfo(kAuxInfo
, arraysize(kAuxInfo
)));
629 EXPECT_EQ(iter_
->GetMaxClearOffset(), 100);
631 EXPECT_EQ(iter_
->track_id(), 1u);
632 EXPECT_EQ(iter_
->aux_info_offset(), 20000);
633 EXPECT_EQ(iter_
->sample_offset(), 100);
634 EXPECT_TRUE(iter_
->CacheAuxInfo(kAuxInfo
, arraysize(kAuxInfo
)));
635 EXPECT_EQ(iter_
->GetMaxClearOffset(), 100);
636 iter_
->AdvanceSample();
637 EXPECT_EQ(iter_
->GetMaxClearOffset(), 101);
639 EXPECT_EQ(iter_
->track_id(), 1u);
640 EXPECT_EQ(iter_
->aux_info_offset(), 201);
641 EXPECT_EQ(iter_
->sample_offset(), 10000);
642 EXPECT_EQ(iter_
->GetMaxClearOffset(), 201);
643 EXPECT_TRUE(iter_
->CacheAuxInfo(kAuxInfo
, arraysize(kAuxInfo
)));
644 EXPECT_EQ(iter_
->GetMaxClearOffset(), 10000);
647 TEST_F(TrackRunIteratorTest
, MissingAndEmptyStss
) {
648 MovieFragment moof
= CreateFragment();
650 // Setup track 0 to not have an stss box, which means that all samples should
651 // be marked as random access points unless the kSampleIsNonSyncSample flag is
652 // set in the sample flags.
653 moov_
.tracks
[0].media
.information
.sample_table
.sync_sample
.is_present
= false;
654 moov_
.tracks
[0].media
.information
.sample_table
.sync_sample
.entries
.resize(0);
655 moof
.tracks
[0].runs
.resize(1);
656 moof
.tracks
[0].runs
[0].sample_count
= 6;
657 moof
.tracks
[0].runs
[0].data_offset
= 100;
658 SetFlagsOnSamples("US UN OS ON NS NN", &moof
.tracks
[0].runs
[0]);
660 // Setup track 1 to have an stss box with no entries, which normally means
661 // that none of the samples should be random access points. If the
662 // kSampleIsNonSyncSample flag is NOT set though, the sample should be
663 // considered a random access point.
664 moov_
.tracks
[1].media
.information
.sample_table
.sync_sample
.is_present
= true;
665 moov_
.tracks
[1].media
.information
.sample_table
.sync_sample
.entries
.resize(0);
666 moof
.tracks
[1].runs
.resize(1);
667 moof
.tracks
[1].runs
[0].sample_count
= 6;
668 moof
.tracks
[1].runs
[0].data_offset
= 200;
669 SetFlagsOnSamples("US UN OS ON NS NN", &moof
.tracks
[1].runs
[0]);
671 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
673 ASSERT_TRUE(iter_
->Init(moof
));
674 EXPECT_TRUE(iter_
->IsRunValid());
676 // Verify that all samples except for the ones that have the
677 // kSampleIsNonSyncSample flag set are marked as random access points.
678 EXPECT_EQ("1 KR P PR P KR K", KeyframeAndRAPInfo(iter_
.get()));
682 // Verify that nothing is marked as a random access point.
683 EXPECT_EQ("2 KR P PR P KR K", KeyframeAndRAPInfo(iter_
.get()));