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,
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
56 MATCHER(ReservedValueInSampleDependencyInfo
, "") {
57 return CONTAINS_STRING(arg
, "Reserved value used in sample dependency info.");
60 class TrackRunIteratorTest
: public testing::Test
{
62 TrackRunIteratorTest() : media_log_(new StrictMock
<MockMediaLog
>()) {
68 scoped_refptr
<StrictMock
<MockMediaLog
>> media_log_
;
69 scoped_ptr
<TrackRunIterator
> iter_
;
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;
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;
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;
109 sample_depends_on
= kSampleDependsOnUnknown
;
112 sample_depends_on
= kSampleDependsOnOthers
;
115 sample_depends_on
= kSampleDependsOnNoOther
;
118 sample_depends_on
= kSampleDependsOnReserved
;
121 CHECK(false) << "Invalid sample dependency character '"
128 is_non_sync_sample
= false;
131 is_non_sync_sample
= true;
134 CHECK(false) << "Invalid sync sample character '"
138 uint32 flags
= static_cast<uint32
>(sample_depends_on
) << 24;
139 if (is_non_sync_sample
)
140 flags
|= kSampleIsNonSyncSample
;
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());
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();
182 MovieFragment
CreateFragment() {
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]);
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
;
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
,
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
) {
289 for (size_t i
= 0; i
< vec
->size(); i
++)
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());
328 iter_
->AdvanceSample();
329 EXPECT_FALSE(iter_
->IsSampleValid());
331 // Test last sample of next run
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());
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());
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()));
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
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_
));
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);
447 moof
.tracks
[1].decode_time
.decode_time
= 0;
449 ASSERT_TRUE(iter_
->Init(moof
));
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
));
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.
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.
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);
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);
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);
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()));
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
713 EXPECT_EQ("2 K P P P K P", KeyframeAndRAPInfo(iter_
.get()));