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.
4
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"
12
13// The sum of the elements in a vector initialized with SumAscending,
14// less the value of the last element.
15static const int kSumAscending1 = 45;
16
17static const int kAudioScale = 48000;
18static const int kVideoScale = 25;
19
20static const uint32 kSampleIsDifferenceSampleFlagMask = 0x10000;
21
22static const uint8 kAuxInfo[] = {
23  0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
24  0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x32,
25  0x00, 0x02,
26  0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
27  0x00, 0x03, 0x00, 0x00, 0x00, 0x04
28};
29
30static const char kIv1[] = {
31  0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
32  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
33};
34
35static const uint8 kKeyId[] = {
36  0x41, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x54,
37  0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x44
38};
39
40namespace media {
41namespace mp4 {
42
43class TrackRunIteratorTest : public testing::Test {
44 public:
45  TrackRunIteratorTest() {
46    CreateMovie();
47  }
48
49 protected:
50  Movie moov_;
51  LogCB log_cb_;
52  scoped_ptr<TrackRunIterator> iter_;
53
54  void CreateMovie() {
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;
65    desc1.type = kAudio;
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;
69
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;
77    desc2.type = kVideo;
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;
81
82    moov_.tracks[2].header.track_id = 3;
83    moov_.tracks[2].media.information.sample_table.description.type = kHint;
84  }
85
86  MovieFragment CreateFragment() {
87    MovieFragment moof;
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);
98
99    moof.tracks[0].runs[1].sample_count = 10;
100    moof.tracks[0].runs[1].data_offset = 10000;
101
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;
114    }
115
116    return moof;
117  }
118
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;
126    } else {
127       sinf = &stsd->audio_entries[0].sinf;
128    }
129
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));
136  }
137
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;
147  }
148
149  void SetAscending(std::vector<uint32>* vec) {
150    vec->resize(10);
151    for (size_t i = 0; i < vec->size(); i++)
152      (*vec)[i] = i+1;
153  }
154};
155
156TEST_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());
161}
162
163TEST_F(TrackRunIteratorTest, BasicOperationTest) {
164  iter_.reset(new TrackRunIterator(&moov_, log_cb_));
165  MovieFragment moof = CreateFragment();
166
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());
179
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());
188
189  // Test end-of-run
190  iter_->AdvanceSample();
191  EXPECT_FALSE(iter_->IsSampleValid());
192
193  // Test last sample of next run
194  iter_->AdvanceRun();
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());
204
205  // Test final run
206  iter_->AdvanceRun();
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());
213  iter_->AdvanceRun();
214  EXPECT_FALSE(iter_->IsRunValid());
215}
216
217TEST_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));
235}
236
237TEST_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));
248  iter_->AdvanceRun();
249  EXPECT_TRUE(iter_->is_keyframe());
250  iter_->AdvanceSample();
251  EXPECT_FALSE(iter_->is_keyframe());
252}
253
254TEST_F(TrackRunIteratorTest, ReorderingTest) {
255  // Test frame reordering and edit list support. The frames have the following
256  // decode timestamps:
257  //
258  //   0ms 40ms   120ms     240ms
259  //   | 0 | 1  - | 2  -  - |
260  //
261  // ...and these composition timestamps, after edit list adjustment:
262  //
263  //   0ms 40ms       160ms  240ms
264  //   | 0 | 2  -  -  | 1 - |
265
266  // Create an edit list with one entry, with an initial start time of 80ms
267  // (that is, 2 / kVideoTimescale) and a duration of zero (which is treated as
268  // infinite according to 14496-12:2012). This will cause the first 80ms of the
269  // media timeline - which will be empty, due to CTS biasing - to be discarded.
270  iter_.reset(new TrackRunIterator(&moov_, log_cb_));
271  EditListEntry entry;
272  entry.segment_duration = 0;
273  entry.media_time = 2;
274  entry.media_rate_integer = 1;
275  entry.media_rate_fraction = 0;
276  moov_.tracks[1].edit.list.edits.push_back(entry);
277
278  // Add CTS offsets. Without bias, the CTS offsets for the first three frames
279  // would simply be [0, 3, -2]. Since CTS offsets should be non-negative for
280  // maximum compatibility, these values are biased up to [2, 5, 0], and the
281  // extra 80ms is removed via the edit list.
282  MovieFragment moof = CreateFragment();
283  std::vector<int32>& cts_offsets =
284    moof.tracks[1].runs[0].sample_composition_time_offsets;
285  cts_offsets.resize(10);
286  cts_offsets[0] = 2;
287  cts_offsets[1] = 5;
288  cts_offsets[2] = 0;
289  moof.tracks[1].decode_time.decode_time = 0;
290
291  ASSERT_TRUE(iter_->Init(moof));
292  iter_->AdvanceRun();
293  EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(0, kVideoScale));
294  EXPECT_EQ(iter_->cts(), TimeDeltaFromRational(0, kVideoScale));
295  EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(1, kVideoScale));
296  iter_->AdvanceSample();
297  EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(1, kVideoScale));
298  EXPECT_EQ(iter_->cts(), TimeDeltaFromRational(4, kVideoScale));
299  EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(2, kVideoScale));
300  iter_->AdvanceSample();
301  EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(3, kVideoScale));
302  EXPECT_EQ(iter_->cts(), TimeDeltaFromRational(1, kVideoScale));
303  EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(3, kVideoScale));
304}
305
306TEST_F(TrackRunIteratorTest, IgnoreUnknownAuxInfoTest) {
307  iter_.reset(new TrackRunIterator(&moov_, log_cb_));
308  MovieFragment moof = CreateFragment();
309  moof.tracks[1].auxiliary_offset.offsets.push_back(50);
310  moof.tracks[1].auxiliary_size.default_sample_info_size = 2;
311  moof.tracks[1].auxiliary_size.sample_count = 2;
312  moof.tracks[1].runs[0].sample_count = 2;
313  ASSERT_TRUE(iter_->Init(moof));
314  iter_->AdvanceRun();
315  EXPECT_FALSE(iter_->AuxInfoNeedsToBeCached());
316}
317
318TEST_F(TrackRunIteratorTest, DecryptConfigTest) {
319  AddEncryption(&moov_.tracks[1]);
320  iter_.reset(new TrackRunIterator(&moov_, log_cb_));
321
322  MovieFragment moof = CreateFragment();
323  AddAuxInfoHeaders(50, &moof.tracks[1]);
324
325  ASSERT_TRUE(iter_->Init(moof));
326
327  // The run for track 2 will be first, since its aux info offset is the first
328  // element in the file.
329  EXPECT_EQ(iter_->track_id(), 2u);
330  EXPECT_TRUE(iter_->is_encrypted());
331  EXPECT_TRUE(iter_->AuxInfoNeedsToBeCached());
332  EXPECT_EQ(static_cast<uint32>(iter_->aux_info_size()), arraysize(kAuxInfo));
333  EXPECT_EQ(iter_->aux_info_offset(), 50);
334  EXPECT_EQ(iter_->GetMaxClearOffset(), 50);
335  EXPECT_FALSE(iter_->CacheAuxInfo(NULL, 0));
336  EXPECT_FALSE(iter_->CacheAuxInfo(kAuxInfo, 3));
337  EXPECT_TRUE(iter_->AuxInfoNeedsToBeCached());
338  EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
339  EXPECT_FALSE(iter_->AuxInfoNeedsToBeCached());
340  EXPECT_EQ(iter_->sample_offset(), 200);
341  EXPECT_EQ(iter_->GetMaxClearOffset(), moof.tracks[0].runs[0].data_offset);
342  scoped_ptr<DecryptConfig> config = iter_->GetDecryptConfig();
343  ASSERT_EQ(arraysize(kKeyId), config->key_id().size());
344  EXPECT_TRUE(!memcmp(kKeyId, config->key_id().data(),
345                      config->key_id().size()));
346  ASSERT_EQ(arraysize(kIv1), config->iv().size());
347  EXPECT_TRUE(!memcmp(kIv1, config->iv().data(), config->iv().size()));
348  EXPECT_TRUE(config->subsamples().empty());
349  iter_->AdvanceSample();
350  config = iter_->GetDecryptConfig();
351  EXPECT_EQ(config->subsamples().size(), 2u);
352  EXPECT_EQ(config->subsamples()[0].clear_bytes, 1u);
353  EXPECT_EQ(config->subsamples()[1].cypher_bytes, 4u);
354}
355
356// It is legal for aux info blocks to be shared among multiple formats.
357TEST_F(TrackRunIteratorTest, SharedAuxInfoTest) {
358  AddEncryption(&moov_.tracks[0]);
359  AddEncryption(&moov_.tracks[1]);
360  iter_.reset(new TrackRunIterator(&moov_, log_cb_));
361
362  MovieFragment moof = CreateFragment();
363  moof.tracks[0].runs.resize(1);
364  AddAuxInfoHeaders(50, &moof.tracks[0]);
365  AddAuxInfoHeaders(50, &moof.tracks[1]);
366  moof.tracks[0].auxiliary_size.default_sample_info_size = 8;
367
368  ASSERT_TRUE(iter_->Init(moof));
369  EXPECT_EQ(iter_->track_id(), 1u);
370  EXPECT_EQ(iter_->aux_info_offset(), 50);
371  EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
372  scoped_ptr<DecryptConfig> config = iter_->GetDecryptConfig();
373  ASSERT_EQ(arraysize(kIv1), config->iv().size());
374  EXPECT_TRUE(!memcmp(kIv1, config->iv().data(), config->iv().size()));
375  iter_->AdvanceSample();
376  EXPECT_EQ(iter_->GetMaxClearOffset(), 50);
377  iter_->AdvanceRun();
378  EXPECT_EQ(iter_->GetMaxClearOffset(), 50);
379  EXPECT_EQ(iter_->aux_info_offset(), 50);
380  EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
381  EXPECT_EQ(iter_->GetMaxClearOffset(), 200);
382  ASSERT_EQ(arraysize(kIv1), config->iv().size());
383  EXPECT_TRUE(!memcmp(kIv1, config->iv().data(), config->iv().size()));
384  iter_->AdvanceSample();
385  EXPECT_EQ(iter_->GetMaxClearOffset(), 201);
386}
387
388// Sensible files are expected to place auxiliary information for a run
389// immediately before the main data for that run. Alternative schemes are
390// possible, however, including the somewhat reasonable behavior of placing all
391// aux info at the head of the 'mdat' box together, and the completely
392// unreasonable behavior demonstrated here:
393//  byte 50: track 2, run 1 aux info
394//  byte 100: track 1, run 1 data
395//  byte 200: track 2, run 1 data
396//  byte 201: track 1, run 2 aux info (*inside* track 2, run 1 data)
397//  byte 10000: track 1, run 2 data
398//  byte 20000: track 1, run 1 aux info
399TEST_F(TrackRunIteratorTest, UnexpectedOrderingTest) {
400  AddEncryption(&moov_.tracks[0]);
401  AddEncryption(&moov_.tracks[1]);
402  iter_.reset(new TrackRunIterator(&moov_, log_cb_));
403
404  MovieFragment moof = CreateFragment();
405  AddAuxInfoHeaders(20000, &moof.tracks[0]);
406  moof.tracks[0].auxiliary_offset.offsets.push_back(201);
407  moof.tracks[0].auxiliary_size.sample_count += 2;
408  moof.tracks[0].auxiliary_size.default_sample_info_size = 8;
409  moof.tracks[0].runs[1].sample_count = 2;
410  AddAuxInfoHeaders(50, &moof.tracks[1]);
411  moof.tracks[1].runs[0].sample_sizes[0] = 5;
412
413  ASSERT_TRUE(iter_->Init(moof));
414  EXPECT_EQ(iter_->track_id(), 2u);
415  EXPECT_EQ(iter_->aux_info_offset(), 50);
416  EXPECT_EQ(iter_->sample_offset(), 200);
417  EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
418  EXPECT_EQ(iter_->GetMaxClearOffset(), 100);
419  iter_->AdvanceRun();
420  EXPECT_EQ(iter_->track_id(), 1u);
421  EXPECT_EQ(iter_->aux_info_offset(), 20000);
422  EXPECT_EQ(iter_->sample_offset(), 100);
423  EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
424  EXPECT_EQ(iter_->GetMaxClearOffset(), 100);
425  iter_->AdvanceSample();
426  EXPECT_EQ(iter_->GetMaxClearOffset(), 101);
427  iter_->AdvanceRun();
428  EXPECT_EQ(iter_->track_id(), 1u);
429  EXPECT_EQ(iter_->aux_info_offset(), 201);
430  EXPECT_EQ(iter_->sample_offset(), 10000);
431  EXPECT_EQ(iter_->GetMaxClearOffset(), 201);
432  EXPECT_TRUE(iter_->CacheAuxInfo(kAuxInfo, arraysize(kAuxInfo)));
433  EXPECT_EQ(iter_->GetMaxClearOffset(), 10000);
434}
435
436}  // namespace mp4
437}  // namespace media
438