1 // Copyright (c) 2012 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 "media/mp4/box_definitions.h"
9 #include "media/mp4/rcheck.h"
10 #include "media/mp4/track_run_iterator.h"
11 #include "testing/gtest/include/gtest/gtest.h"
13 // The sum of the elements in a vector initialized with SumAscending,
14 // less the value of the last element.
15 static const int kSumAscending1
= 45;
17 static const int kAudioScale
= 48000;
18 static const int kVideoScale
= 25;
20 static const uint32 kSampleIsDifferenceSampleFlagMask
= 0x10000;
22 static const uint8 kAuxInfo
[] = {
23 0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
24 0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x32,
26 0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
27 0x00, 0x03, 0x00, 0x00, 0x00, 0x04
30 static const char kIv1
[] = {
31 0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
32 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
35 static const uint8 kKeyId
[] = {
36 0x41, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x54,
37 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x44
43 class TrackRunIteratorTest
: public testing::Test
{
45 TrackRunIteratorTest() {
52 scoped_ptr
<TrackRunIterator
> iter_
;
55 moov_
.header
.timescale
= 1000;
56 moov_
.tracks
.resize(3);
57 moov_
.extends
.tracks
.resize(2);
58 moov_
.tracks
[0].header
.track_id
= 1;
59 moov_
.tracks
[0].media
.header
.timescale
= kAudioScale
;
60 SampleDescription
& desc1
=
61 moov_
.tracks
[0].media
.information
.sample_table
.description
;
62 AudioSampleEntry aud_desc
;
63 aud_desc
.format
= FOURCC_MP4A
;
64 aud_desc
.sinf
.info
.track_encryption
.is_encrypted
= false;
66 desc1
.audio_entries
.push_back(aud_desc
);
67 moov_
.extends
.tracks
[0].track_id
= 1;
68 moov_
.extends
.tracks
[0].default_sample_description_index
= 1;
70 moov_
.tracks
[1].header
.track_id
= 2;
71 moov_
.tracks
[1].media
.header
.timescale
= kVideoScale
;
72 SampleDescription
& desc2
=
73 moov_
.tracks
[1].media
.information
.sample_table
.description
;
74 VideoSampleEntry vid_desc
;
75 vid_desc
.format
= FOURCC_AVC1
;
76 vid_desc
.sinf
.info
.track_encryption
.is_encrypted
= false;
78 desc2
.video_entries
.push_back(vid_desc
);
79 moov_
.extends
.tracks
[1].track_id
= 2;
80 moov_
.extends
.tracks
[1].default_sample_description_index
= 1;
82 moov_
.tracks
[2].header
.track_id
= 3;
83 moov_
.tracks
[2].media
.information
.sample_table
.description
.type
= kHint
;
86 MovieFragment
CreateFragment() {
88 moof
.tracks
.resize(2);
89 moof
.tracks
[0].decode_time
.decode_time
= 0;
90 moof
.tracks
[0].header
.track_id
= 1;
91 moof
.tracks
[0].header
.has_default_sample_flags
= true;
92 moof
.tracks
[0].header
.default_sample_duration
= 1024;
93 moof
.tracks
[0].header
.default_sample_size
= 4;
94 moof
.tracks
[0].runs
.resize(2);
95 moof
.tracks
[0].runs
[0].sample_count
= 10;
96 moof
.tracks
[0].runs
[0].data_offset
= 100;
97 SetAscending(&moof
.tracks
[0].runs
[0].sample_sizes
);
99 moof
.tracks
[0].runs
[1].sample_count
= 10;
100 moof
.tracks
[0].runs
[1].data_offset
= 10000;
102 moof
.tracks
[1].header
.track_id
= 2;
103 moof
.tracks
[1].header
.has_default_sample_flags
= false;
104 moof
.tracks
[1].decode_time
.decode_time
= 10;
105 moof
.tracks
[1].runs
.resize(1);
106 moof
.tracks
[1].runs
[0].sample_count
= 10;
107 moof
.tracks
[1].runs
[0].data_offset
= 200;
108 SetAscending(&moof
.tracks
[1].runs
[0].sample_sizes
);
109 SetAscending(&moof
.tracks
[1].runs
[0].sample_durations
);
110 moof
.tracks
[1].runs
[0].sample_flags
.resize(10);
111 for (size_t i
= 1; i
< moof
.tracks
[1].runs
[0].sample_flags
.size(); i
++) {
112 moof
.tracks
[1].runs
[0].sample_flags
[i
] =
113 kSampleIsDifferenceSampleFlagMask
;
119 // Update the first sample description of a Track to indicate encryption
120 void AddEncryption(Track
* track
) {
121 SampleDescription
* stsd
=
122 &track
->media
.information
.sample_table
.description
;
123 ProtectionSchemeInfo
* sinf
;
124 if (!stsd
->video_entries
.empty()) {
125 sinf
= &stsd
->video_entries
[0].sinf
;
127 sinf
= &stsd
->audio_entries
[0].sinf
;
130 sinf
->type
.type
= FOURCC_CENC
;
131 sinf
->info
.track_encryption
.is_encrypted
= true;
132 sinf
->info
.track_encryption
.default_iv_size
= 8;
133 sinf
->info
.track_encryption
.default_kid
.insert(
134 sinf
->info
.track_encryption
.default_kid
.begin(),
135 kKeyId
, kKeyId
+ arraysize(kKeyId
));
138 // Add aux info covering the first track run to a TrackFragment, and update
139 // the run to ensure it matches length and subsample information.
140 void AddAuxInfoHeaders(int offset
, TrackFragment
* frag
) {
141 frag
->auxiliary_offset
.offsets
.push_back(offset
);
142 frag
->auxiliary_size
.sample_count
= 2;
143 frag
->auxiliary_size
.sample_info_sizes
.push_back(8);
144 frag
->auxiliary_size
.sample_info_sizes
.push_back(22);
145 frag
->runs
[0].sample_count
= 2;
146 frag
->runs
[0].sample_sizes
[1] = 10;
149 void SetAscending(std::vector
<uint32
>* vec
) {
151 for (size_t i
= 0; i
< vec
->size(); i
++)
156 TEST_F(TrackRunIteratorTest
, NoRunsTest
) {
157 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
158 ASSERT_TRUE(iter_
->Init(MovieFragment()));
159 EXPECT_FALSE(iter_
->IsRunValid());
160 EXPECT_FALSE(iter_
->IsSampleValid());
163 TEST_F(TrackRunIteratorTest
, BasicOperationTest
) {
164 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
165 MovieFragment moof
= CreateFragment();
167 // Test that runs are sorted correctly, and that properties of the initial
168 // sample of the first run are correct
169 ASSERT_TRUE(iter_
->Init(moof
));
170 EXPECT_TRUE(iter_
->IsRunValid());
171 EXPECT_FALSE(iter_
->is_encrypted());
172 EXPECT_EQ(iter_
->track_id(), 1u);
173 EXPECT_EQ(iter_
->sample_offset(), 100);
174 EXPECT_EQ(iter_
->sample_size(), 1);
175 EXPECT_EQ(iter_
->dts(), TimeDeltaFromRational(0, kAudioScale
));
176 EXPECT_EQ(iter_
->cts(), TimeDeltaFromRational(0, kAudioScale
));
177 EXPECT_EQ(iter_
->duration(), TimeDeltaFromRational(1024, kAudioScale
));
178 EXPECT_TRUE(iter_
->is_keyframe());
180 // Advance to the last sample in the current run, and test its properties
181 for (int i
= 0; i
< 9; i
++) iter_
->AdvanceSample();
182 EXPECT_EQ(iter_
->track_id(), 1u);
183 EXPECT_EQ(iter_
->sample_offset(), 100 + kSumAscending1
);
184 EXPECT_EQ(iter_
->sample_size(), 10);
185 EXPECT_EQ(iter_
->dts(), TimeDeltaFromRational(1024 * 9, kAudioScale
));
186 EXPECT_EQ(iter_
->duration(), TimeDeltaFromRational(1024, kAudioScale
));
187 EXPECT_TRUE(iter_
->is_keyframe());
190 iter_
->AdvanceSample();
191 EXPECT_FALSE(iter_
->IsSampleValid());
193 // Test last sample of next run
195 EXPECT_TRUE(iter_
->is_keyframe());
196 for (int i
= 0; i
< 9; i
++) iter_
->AdvanceSample();
197 EXPECT_EQ(iter_
->track_id(), 2u);
198 EXPECT_EQ(iter_
->sample_offset(), 200 + kSumAscending1
);
199 EXPECT_EQ(iter_
->sample_size(), 10);
200 int64 base_dts
= kSumAscending1
+ moof
.tracks
[1].decode_time
.decode_time
;
201 EXPECT_EQ(iter_
->dts(), TimeDeltaFromRational(base_dts
, kVideoScale
));
202 EXPECT_EQ(iter_
->duration(), TimeDeltaFromRational(10, kVideoScale
));
203 EXPECT_FALSE(iter_
->is_keyframe());
207 EXPECT_EQ(iter_
->track_id(), 1u);
208 EXPECT_EQ(iter_
->dts(), TimeDeltaFromRational(1024 * 10, kAudioScale
));
209 iter_
->AdvanceSample();
210 EXPECT_EQ(moof
.tracks
[0].runs
[1].data_offset
+
211 moof
.tracks
[0].header
.default_sample_size
,
212 iter_
->sample_offset());
214 EXPECT_FALSE(iter_
->IsRunValid());
217 TEST_F(TrackRunIteratorTest
, TrackExtendsDefaultsTest
) {
218 moov_
.extends
.tracks
[0].default_sample_duration
= 50;
219 moov_
.extends
.tracks
[0].default_sample_size
= 3;
220 moov_
.extends
.tracks
[0].default_sample_flags
=
221 kSampleIsDifferenceSampleFlagMask
;
222 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
223 MovieFragment moof
= CreateFragment();
224 moof
.tracks
[0].header
.has_default_sample_flags
= false;
225 moof
.tracks
[0].header
.default_sample_size
= 0;
226 moof
.tracks
[0].header
.default_sample_duration
= 0;
227 moof
.tracks
[0].runs
[0].sample_sizes
.clear();
228 ASSERT_TRUE(iter_
->Init(moof
));
229 iter_
->AdvanceSample();
230 EXPECT_FALSE(iter_
->is_keyframe());
231 EXPECT_EQ(iter_
->sample_size(), 3);
232 EXPECT_EQ(iter_
->sample_offset(), moof
.tracks
[0].runs
[0].data_offset
+ 3);
233 EXPECT_EQ(iter_
->duration(), TimeDeltaFromRational(50, kAudioScale
));
234 EXPECT_EQ(iter_
->dts(), TimeDeltaFromRational(50, kAudioScale
));
237 TEST_F(TrackRunIteratorTest
, FirstSampleFlagTest
) {
238 // Ensure that keyframes are flagged correctly in the face of BMFF boxes which
239 // explicitly specify the flags for the first sample in a run and rely on
240 // defaults for all subsequent samples
241 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
242 MovieFragment moof
= CreateFragment();
243 moof
.tracks
[1].header
.has_default_sample_flags
= true;
244 moof
.tracks
[1].header
.default_sample_flags
=
245 kSampleIsDifferenceSampleFlagMask
;
246 moof
.tracks
[1].runs
[0].sample_flags
.resize(1);
247 ASSERT_TRUE(iter_
->Init(moof
));
249 EXPECT_TRUE(iter_
->is_keyframe());
250 iter_
->AdvanceSample();
251 EXPECT_FALSE(iter_
->is_keyframe());
254 TEST_F(TrackRunIteratorTest
, MinDecodeTest
) {
255 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
256 MovieFragment moof
= CreateFragment();
257 moof
.tracks
[0].decode_time
.decode_time
= kAudioScale
;
258 ASSERT_TRUE(iter_
->Init(moof
));
259 EXPECT_EQ(TimeDeltaFromRational(moof
.tracks
[1].decode_time
.decode_time
,
261 iter_
->GetMinDecodeTimestamp());
264 TEST_F(TrackRunIteratorTest
, ReorderingTest
) {
265 // Test frame reordering and edit list support. The frames have the following
266 // decode timestamps:
268 // 0ms 40ms 120ms 240ms
269 // | 0 | 1 - | 2 - - |
271 // ...and these composition timestamps, after edit list adjustment:
273 // 0ms 40ms 160ms 240ms
274 // | 0 | 2 - - | 1 - |
276 // Create an edit list with one entry, with an initial start time of 80ms
277 // (that is, 2 / kVideoTimescale) and a duration of zero (which is treated as
278 // infinite according to 14496-12:2012). This will cause the first 80ms of the
279 // media timeline - which will be empty, due to CTS biasing - to be discarded.
280 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
282 entry
.segment_duration
= 0;
283 entry
.media_time
= 2;
284 entry
.media_rate_integer
= 1;
285 entry
.media_rate_fraction
= 0;
286 moov_
.tracks
[1].edit
.list
.edits
.push_back(entry
);
288 // Add CTS offsets. Without bias, the CTS offsets for the first three frames
289 // would simply be [0, 3, -2]. Since CTS offsets should be non-negative for
290 // maximum compatibility, these values are biased up to [2, 5, 0], and the
291 // extra 80ms is removed via the edit list.
292 MovieFragment moof
= CreateFragment();
293 std::vector
<int32
>& cts_offsets
=
294 moof
.tracks
[1].runs
[0].sample_composition_time_offsets
;
295 cts_offsets
.resize(10);
299 moof
.tracks
[1].decode_time
.decode_time
= 0;
301 ASSERT_TRUE(iter_
->Init(moof
));
303 EXPECT_EQ(iter_
->dts(), TimeDeltaFromRational(0, kVideoScale
));
304 EXPECT_EQ(iter_
->cts(), TimeDeltaFromRational(0, kVideoScale
));
305 EXPECT_EQ(iter_
->duration(), TimeDeltaFromRational(1, kVideoScale
));
306 iter_
->AdvanceSample();
307 EXPECT_EQ(iter_
->dts(), TimeDeltaFromRational(1, kVideoScale
));
308 EXPECT_EQ(iter_
->cts(), TimeDeltaFromRational(4, kVideoScale
));
309 EXPECT_EQ(iter_
->duration(), TimeDeltaFromRational(2, kVideoScale
));
310 iter_
->AdvanceSample();
311 EXPECT_EQ(iter_
->dts(), TimeDeltaFromRational(3, kVideoScale
));
312 EXPECT_EQ(iter_
->cts(), TimeDeltaFromRational(1, kVideoScale
));
313 EXPECT_EQ(iter_
->duration(), TimeDeltaFromRational(3, kVideoScale
));
316 TEST_F(TrackRunIteratorTest
, IgnoreUnknownAuxInfoTest
) {
317 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
318 MovieFragment moof
= CreateFragment();
319 moof
.tracks
[1].auxiliary_offset
.offsets
.push_back(50);
320 moof
.tracks
[1].auxiliary_size
.default_sample_info_size
= 2;
321 moof
.tracks
[1].auxiliary_size
.sample_count
= 2;
322 moof
.tracks
[1].runs
[0].sample_count
= 2;
323 ASSERT_TRUE(iter_
->Init(moof
));
325 EXPECT_FALSE(iter_
->AuxInfoNeedsToBeCached());
328 TEST_F(TrackRunIteratorTest
, DecryptConfigTest
) {
329 AddEncryption(&moov_
.tracks
[1]);
330 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
332 MovieFragment moof
= CreateFragment();
333 AddAuxInfoHeaders(50, &moof
.tracks
[1]);
335 ASSERT_TRUE(iter_
->Init(moof
));
337 // The run for track 2 will be first, since its aux info offset is the first
338 // element in the file.
339 EXPECT_EQ(iter_
->track_id(), 2u);
340 EXPECT_TRUE(iter_
->is_encrypted());
341 EXPECT_TRUE(iter_
->AuxInfoNeedsToBeCached());
342 EXPECT_EQ(static_cast<uint32
>(iter_
->aux_info_size()), arraysize(kAuxInfo
));
343 EXPECT_EQ(iter_
->aux_info_offset(), 50);
344 EXPECT_EQ(iter_
->GetMaxClearOffset(), 50);
345 EXPECT_FALSE(iter_
->CacheAuxInfo(NULL
, 0));
346 EXPECT_FALSE(iter_
->CacheAuxInfo(kAuxInfo
, 3));
347 EXPECT_TRUE(iter_
->AuxInfoNeedsToBeCached());
348 EXPECT_TRUE(iter_
->CacheAuxInfo(kAuxInfo
, arraysize(kAuxInfo
)));
349 EXPECT_FALSE(iter_
->AuxInfoNeedsToBeCached());
350 EXPECT_EQ(iter_
->sample_offset(), 200);
351 EXPECT_EQ(iter_
->GetMaxClearOffset(), moof
.tracks
[0].runs
[0].data_offset
);
352 scoped_ptr
<DecryptConfig
> config
= iter_
->GetDecryptConfig();
353 ASSERT_EQ(arraysize(kKeyId
), config
->key_id().size());
354 EXPECT_TRUE(!memcmp(kKeyId
, config
->key_id().data(),
355 config
->key_id().size()));
356 ASSERT_EQ(arraysize(kIv1
), config
->iv().size());
357 EXPECT_TRUE(!memcmp(kIv1
, config
->iv().data(), config
->iv().size()));
358 EXPECT_TRUE(config
->subsamples().empty());
359 iter_
->AdvanceSample();
360 config
= iter_
->GetDecryptConfig();
361 EXPECT_EQ(config
->subsamples().size(), 2u);
362 EXPECT_EQ(config
->subsamples()[0].clear_bytes
, 1u);
363 EXPECT_EQ(config
->subsamples()[1].cypher_bytes
, 4u);
366 // It is legal for aux info blocks to be shared among multiple formats.
367 TEST_F(TrackRunIteratorTest
, SharedAuxInfoTest
) {
368 AddEncryption(&moov_
.tracks
[0]);
369 AddEncryption(&moov_
.tracks
[1]);
370 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
372 MovieFragment moof
= CreateFragment();
373 moof
.tracks
[0].runs
.resize(1);
374 AddAuxInfoHeaders(50, &moof
.tracks
[0]);
375 AddAuxInfoHeaders(50, &moof
.tracks
[1]);
376 moof
.tracks
[0].auxiliary_size
.default_sample_info_size
= 8;
378 ASSERT_TRUE(iter_
->Init(moof
));
379 EXPECT_EQ(iter_
->track_id(), 1u);
380 EXPECT_EQ(iter_
->aux_info_offset(), 50);
381 EXPECT_TRUE(iter_
->CacheAuxInfo(kAuxInfo
, arraysize(kAuxInfo
)));
382 scoped_ptr
<DecryptConfig
> config
= iter_
->GetDecryptConfig();
383 ASSERT_EQ(arraysize(kIv1
), config
->iv().size());
384 EXPECT_TRUE(!memcmp(kIv1
, config
->iv().data(), config
->iv().size()));
385 iter_
->AdvanceSample();
386 EXPECT_EQ(iter_
->GetMaxClearOffset(), 50);
388 EXPECT_EQ(iter_
->GetMaxClearOffset(), 50);
389 EXPECT_EQ(iter_
->aux_info_offset(), 50);
390 EXPECT_TRUE(iter_
->CacheAuxInfo(kAuxInfo
, arraysize(kAuxInfo
)));
391 EXPECT_EQ(iter_
->GetMaxClearOffset(), 200);
392 ASSERT_EQ(arraysize(kIv1
), config
->iv().size());
393 EXPECT_TRUE(!memcmp(kIv1
, config
->iv().data(), config
->iv().size()));
394 iter_
->AdvanceSample();
395 EXPECT_EQ(iter_
->GetMaxClearOffset(), 201);
398 // Sensible files are expected to place auxiliary information for a run
399 // immediately before the main data for that run. Alternative schemes are
400 // possible, however, including the somewhat reasonable behavior of placing all
401 // aux info at the head of the 'mdat' box together, and the completely
402 // unreasonable behavior demonstrated here:
403 // byte 50: track 2, run 1 aux info
404 // byte 100: track 1, run 1 data
405 // byte 200: track 2, run 1 data
406 // byte 201: track 1, run 2 aux info (*inside* track 2, run 1 data)
407 // byte 10000: track 1, run 2 data
408 // byte 20000: track 1, run 1 aux info
409 TEST_F(TrackRunIteratorTest
, UnexpectedOrderingTest
) {
410 AddEncryption(&moov_
.tracks
[0]);
411 AddEncryption(&moov_
.tracks
[1]);
412 iter_
.reset(new TrackRunIterator(&moov_
, log_cb_
));
414 MovieFragment moof
= CreateFragment();
415 AddAuxInfoHeaders(20000, &moof
.tracks
[0]);
416 moof
.tracks
[0].auxiliary_offset
.offsets
.push_back(201);
417 moof
.tracks
[0].auxiliary_size
.sample_count
+= 2;
418 moof
.tracks
[0].auxiliary_size
.default_sample_info_size
= 8;
419 moof
.tracks
[0].runs
[1].sample_count
= 2;
420 AddAuxInfoHeaders(50, &moof
.tracks
[1]);
421 moof
.tracks
[1].runs
[0].sample_sizes
[0] = 5;
423 ASSERT_TRUE(iter_
->Init(moof
));
424 EXPECT_EQ(iter_
->track_id(), 2u);
425 EXPECT_EQ(iter_
->aux_info_offset(), 50);
426 EXPECT_EQ(iter_
->sample_offset(), 200);
427 EXPECT_TRUE(iter_
->CacheAuxInfo(kAuxInfo
, arraysize(kAuxInfo
)));
428 EXPECT_EQ(iter_
->GetMaxClearOffset(), 100);
430 EXPECT_EQ(iter_
->track_id(), 1u);
431 EXPECT_EQ(iter_
->aux_info_offset(), 20000);
432 EXPECT_EQ(iter_
->sample_offset(), 100);
433 EXPECT_TRUE(iter_
->CacheAuxInfo(kAuxInfo
, arraysize(kAuxInfo
)));
434 EXPECT_EQ(iter_
->GetMaxClearOffset(), 100);
435 iter_
->AdvanceSample();
436 EXPECT_EQ(iter_
->GetMaxClearOffset(), 101);
438 EXPECT_EQ(iter_
->track_id(), 1u);
439 EXPECT_EQ(iter_
->aux_info_offset(), 201);
440 EXPECT_EQ(iter_
->sample_offset(), 10000);
441 EXPECT_EQ(iter_
->GetMaxClearOffset(), 201);
442 EXPECT_TRUE(iter_
->CacheAuxInfo(kAuxInfo
, arraysize(kAuxInfo
)));
443 EXPECT_EQ(iter_
->GetMaxClearOffset(), 10000);